PowerShell Get All Local Group Memberships on a PC
─□✕

PowerShell Get All Local Group Memberships on a PC

PowerShell Tips Editor 4 min read

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

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.

Send-Item -To