The local Administrators group on a Windows machine is the most powerful security boundary you have, and the most consistently mismanaged. Three patterns appear on almost every fleet we audit: too many members (helpdesk groups, vendor accounts, an old domain admin who left in 2021), orphaned SIDs (the user is gone, the SID still has admin), and the Guest account sitting Enabled because nobody noticed.
This guide walks through a complete local-admin audit you can run from PowerShell, includes a fleet-wide one-liner, and ships the Dargslan.LocalAdminAudit module plus a free PDF cheat sheet.
Table of Contents
Step 1: Enumerate the Administrators group
The clean cmdlet is Get-LocalGroupMember. On Server Core or older builds it occasionally chokes when an orphaned SID is in the group; the ADSI fallback below handles that:
try {
Get-LocalGroupMember -Group Administrators
} catch {
([ADSI]'WinNT://./Administrators,group').Invoke('Members') |
ForEach-Object { ([ADSI]$_).Path.Replace('WinNT://','') }
}
The defensible target is "Administrators contains the local Administrator account, the helpdesk group from AD, and nothing else". Five members is the soft cap before it becomes a finding.
Step 2: Enumerate every local user
Get-LocalUser | Select Name, Enabled, LastLogon,
PasswordRequired, PasswordLastSet, PasswordExpires, Description
You are looking for: enabled accounts that should not exist (an "installer" or "vendor" account from a past project), accounts with PasswordRequired = False, and accounts with no PasswordExpires date.
Step 3: Guest, DefaultAccount, WDAGUtilityAccount
Three built-in accounts always show up. The audit answers should be:
- Guest — Enabled = False, full stop.
- DefaultAccount — Enabled = False (Windows 10+ ships it disabled).
- WDAGUtilityAccount — Enabled = False (used by Application Guard if installed).
Step 4: Orphaned SIDs
If Get-LocalGroupMember returns a name that looks like S-1-5-21-... instead of a resolved name, the SID points at a deleted user — usually a domain user whose account was removed from AD but never from this group. Each one is a finding because the SID can be reactivated by recreating an account with the same SID (rare but possible).
Step 5: Inactive accounts
$cut = (Get-Date).AddDays(-90)
Get-LocalUser | Where-Object { $_.Enabled -and $_.LastLogon -and $_.LastLogon -lt $cut }
A defensible PASS / WARN / FAIL score
- Five or fewer Administrators members (1 pt)
- Guest account disabled (1 pt)
- Zero orphaned-SID admins (1 pt)
- Zero inactive enabled users (1 pt)
4/4 PASS, 2-3 WARN, 0-1 FAIL.
Dargslan.LocalAdminAudit module
Install-Module Dargslan.LocalAdminAudit -Scope CurrentUser
Import-Module Dargslan.LocalAdminAudit
Export-DargslanLocalAdminAuditReport -OutDir C:\reports
Fleet-wide:
Invoke-Command -ComputerName (Get-Content hosts.txt) -ScriptBlock {
Import-Module Dargslan.LocalAdminAudit
Get-DargslanLocalAdminAuditReport
} | Select PSComputerName, AdminCount, GuestEnabled, Verdict |
Export-Csv localadmin-fleet.csv -NoTypeInformation
FAQ
What about LAPS-managed local admin?
LAPS only manages the password — it does not change the membership. The audit still applies.
Why does Get-LocalUser sometimes show no LastLogon?
Accounts that have never logged in have a null timestamp. The module treats null as "not stale".
Cheat sheet?
Free PDF at /cheat-sheets/windows-local-admin-audit-2026.