Staying on top of local group membership is essential to Microsoft Windows Server security and good IT hygiene. In particular, you need to pay attention to the privileged groups on local machines, such as the local Administrators group. Sometimes Active Directory user accounts are added to these groups so users can install the programs they need to do their jobs without asking for help, and once local access rights are granted, they are rarely revoked. While this approach reduces helpdesk workload, it can significantly increase security risks on your systems by increasing the attack surface area and the risk of privilege abuse.

If you have enough PowerShell knowledge and experience, you can create a script that lists the membership of all local groups, including the local Administrators group. However, exporting all user objects into .CSV format might not be the most effective way to establish a baseline of the members of local groups and spot inappropriate changes to those groups. Original – https://www.netwrix.com/how_to_get_local_group_membership_report.html

3 Steps total

Step 1: Open the PowerShell ISE

Open the PowerShell ISE → Create a new script with the following code and run it, specifying the computer name and the path for export:

Step 2: Script Code

$strComputer = get-content env:computername #Enter the name of the target computer, localhost is used by default
Write-Host “Computer: $strComputer”
$computer = [ADSI]”WinNT://$strComputer”
$objCount = ($computer.psbase.children | measure-object).count
Write-Host “Q-ty objects for computer ‘$strComputer’ = $objCount”
$Counter = 1
$result = @()
foreach($adsiObj in $computer.psbase.children)
{
switch -regex($adsiObj.psbase.SchemaClassName)
{
“group”
{
$group = $adsiObj.name
$LocalGroup = [ADSI]”WinNT://$strComputer/$group,group”
$Members = @($LocalGroup.psbase.Invoke(“Members”))
$objCount = ($Members | measure-object).count
Write-Host “Q-ty objects for group ‘$group’ = $objCount”
$GName = $group.tostring()

ForEach ($Member In $Members) {
$Name = $Member.GetType().InvokeMember(“Name”, “GetProperty”, $Null, $Member, $Null)
$Path = $Member.GetType().InvokeMember(“ADsPath”, “GetProperty”, $Null, $Member, $Null)
Write-Host ” Object = $Path”

$isGroup = ($Member.GetType().InvokeMember(“Class”, “GetProperty”, $Null, $Member, $Null) -eq “group”)
If (($Path -like “*/$strComputer/*”) -Or ($Path -like “WinNT://NT*”)) { $Type = “Local”
} Else {$Type = “Domain”}
$result += New-Object PSObject -Property @{
Computername = $strComputer
NameMember = $Name
PathMember = $Path
TypeMemeber = $Type
ParentGroup = $GName
isGroupMemeber = $isGroup
Depth = $Counter
}
}
}
} #end switch
} #end foreach
Write-Host “Total objects = ” ($result | measure-object).count
$result = $result | select-object Computername, ParentGroup, NameMember, TypeMemeber, PathMember, isGroupMemeber, Depth
$result | Export-Csv -path (“C:\LocalGroups({0})-{1:yyyyMMddHHmm}.csv” -f
$env:COMPUTERNAME,(Get-Date)) -Delimiter “;” -Encoding “UTF8” -force -NoTypeInformation

Step 3: Open the file produced by the script in MS Excel

Open the file produced by the script in MS Excel.