PowerShell Get All Local Group Memberships on a PC
Active Directory gets all the attention, but local group sprawl is where real privilege escalation happens. Knowing how to use powershell get local group memberships across your entire fleet — not just one machine — is a security baseline requirement that auditors increasingly demand. This guide covers everything from quick single-machine enumeration to a fully automated, multi-computer CSV export you can run from a management workstation.
Get-LocalGroup and Get-LocalGroupMember on the Local Machine
The Microsoft.PowerShell.LocalAccounts module (available since PowerShell 5.1 on Windows 10 / Server 2016) provides the cleanest interface for local account management. Start by enumerating all groups, then iterate.
# List every local group and its members on the current machine
$results = foreach ($group in Get-LocalGroup) {
try {
$members = Get-LocalGroupMember -Group $group.Name -ErrorAction Stop
}
catch {
$members = @()
}
foreach ($member in $members) {
[PSCustomObject]@{
ComputerName = $env:COMPUTERNAME
GroupName = $group.Name
MemberName = $member.Name
MemberType = $member.ObjectClass # User or Group
PrincipalSource = $member.PrincipalSource # Local, ActiveDirectory, MicrosoftAccount
}
}
}
$results | Format-Table -AutoSize
The PrincipalSource property immediately distinguishes local accounts from domain accounts and Microsoft accounts — critical for spotting unexpected domain groups nested inside Administrators.
Using CIM/WMI for Down-Level Compatibility
On Windows Server 2012 R2 or Windows 8.1, the LocalAccounts module is not available. Use Get-CimInstance with the Win32_GroupUser association class as a fallback.
function Get-LocalGroupMemberCim {
param([string]$ComputerName = $env:COMPUTERNAME,
[string]$GroupName = 'Administrators')
$query = "SELECT * FROM Win32_GroupUser WHERE GroupComponent=`"Win32_Group.Domain='$ComputerName',Name='$GroupName'`""
Get-CimInstance -Query $query -ComputerName $ComputerName |
ForEach-Object {
$part = $_.PartComponent
[PSCustomObject]@{
ComputerName = $ComputerName
GroupName = $GroupName
MemberName = $part.Name
Domain = $part.Domain
}
}
}
Get-LocalGroupMemberCim -GroupName 'Administrators'
Note that Get-CimInstance uses DCOM/WinRM transport, not the deprecated DCOM-only path of Get-WmiObject. Always prefer Get-CimInstance in new scripts.
Running Against Remote Computers with Invoke-Command
To audit multiple machines in parallel, use Invoke-Command with a computer list. WinRM must be enabled on targets — use Enable-PSRemoting via Group Policy if it isn’t.
$computers = Get-Content 'C:\Scripts\servers.txt'
$remoteResults = Invoke-Command -ComputerName $computers -ErrorAction SilentlyContinue -ScriptBlock {
foreach ($group in Get-LocalGroup) {
try {
$members = Get-LocalGroupMember -Group $group.Name -ErrorAction Stop
}
catch { $members = @() }
foreach ($member in $members) {
[PSCustomObject]@{
ComputerName = $env:COMPUTERNAME
GroupName = $group.Name
MemberName = $member.Name
MemberType = $member.ObjectClass
PrincipalSource = $member.PrincipalSource
}
}
}
}
# Strip PSComputerName / RunspaceId added by Invoke-Command
$clean = $remoteResults | Select-Object ComputerName, GroupName, MemberName, MemberType, PrincipalSource
$clean | Format-Table -AutoSize
Handling Domain Accounts vs Local Accounts in Output
The PrincipalSource and member name format reveal the account origin. Domain members appear as DOMAIN\username while local accounts appear as COMPUTERNAME\username. Filter accordingly for compliance reports.
# Find machines where domain accounts are in local Administrators
$domainAdmins = $clean | Where-Object {
$_.GroupName -eq 'Administrators' -and
$_.PrincipalSource -eq 'ActiveDirectory'
}
Write-Host "Machines with domain accounts in local Admins: $(($domainAdmins | Select-Object -Unique ComputerName).Count)"
# Find unexpected local accounts that are NOT the built-in Administrator
$unexpectedLocal = $clean | Where-Object {
$_.GroupName -eq 'Administrators' -and
$_.PrincipalSource -eq 'Local' -and
$_.MemberName -notmatch 'Administrator$'
}
$unexpectedLocal | Format-Table -AutoSize
Exporting Findings to CSV for Compliance
A timestamped CSV is the standard deliverable for security audits and change-management reviews.
$reportDate = Get-Date -Format 'yyyy-MM-dd'
$outputPath = "C:\Audit\LocalGroupMembership-$reportDate.csv"
$clean | Export-Csv -Path $outputPath -NoTypeInformation -Encoding UTF8
Write-Host "Exported $($clean.Count) membership records to $outputPath"
# Summary by group across all machines
$clean | Group-Object GroupName |
Select-Object Name, Count |
Sort-Object Count -Descending |
Format-Table -AutoSize
Common Errors and Fixes
Get-LocalGroupMember fails on domain controllers where the local SAM is unavailable. Domain controllers do not have a traditional local SAM database. The error “Failed to enumerate members” is expected. Add a check: if ((Get-CimInstance Win32_ComputerSystem).DomainRole -ge 4) { Write-Warning "DC — skipping local group query" } and exclude DCs from your computer list.
WS-Management errors when WinRM is not enabled on remote hosts. The error “The WinRM client cannot complete the operation” means the remote machine’s WinRM listener is off or blocked by firewall. Enable it via GPO: Computer Configuration → Policies → Windows Settings → Windows Remote Management, or run Invoke-Command -ComputerName $pc -ScriptBlock { Enable-PSRemoting -Force } if you have admin rights through another method.
Related Cmdlets / See Also
- PowerShell Local Users and Groups
- PowerShell Invoke-Command Guide
- PowerShell Active Directory Audit Report
Wrapping Up
Auditing local group memberships fleet-wide takes less than 50 lines of PowerShell. Combine Get-LocalGroupMember for modern systems, Get-CimInstance for down-level compatibility, and Invoke-Command for scale. Schedule monthly exports and diff them with Compare-Object to catch privilege creep before your next audit.


