Get-LocalUser: auditing local Windows accounts with PowerShell

Do you know exactly how many user accounts exist on your Windows machine right now? Most people don't — and that's exactly the gap an attacker with initial access will exploit: create a hidden account with a generic name, enable it with a password, and keep quiet persistence. Get-LocalUser is the cmdlet that exposes all of it in a single line.
What Get-LocalUser is
Get-LocalUser is part of the Microsoft.PowerShell.LocalAccounts module, available natively since PowerShell 5.1 (Windows 10 / Server 2016 onward). It returns every local user on the machine — manually created accounts, built-in system accounts (Administrator, Guest, DefaultAccount, WDAGUtilityAccount) and accounts linked to a Microsoft account.
Unlike net user, Get-LocalUser returns structured objects you can filter, compare and export easily.
Listing all local accounts
Open PowerShell as administrator and run:
Get-LocalUserThe output includes the columns that matter most for an immediate audit:
Name Enabled Description
---- ------- -----------
Administrator False Built-in account for administering the computer
DefaultAccount False A user account managed by the system
Guest False Built-in account for guest access
john True
WDAGUtilityAccount False A user account managed by Windows Defender...Any account with Enabled: True and an unrecognized name — or Guest itself enabled — deserves investigation.
Filtering by status and properties
The cmdlet accepts a name filter, or you can use Where-Object for more specific conditions:
# Enabled accounts only
Get-LocalUser | Where-Object { $_.Enabled -eq $true }
# Accounts with no expiration date (risk: no defined lifecycle)
Get-LocalUser | Where-Object { $_.AccountExpires -eq $null -and $_.Enabled -eq $true }
# Accounts that have never logged in
Get-LocalUser | Where-Object { $_.LastLogon -eq $null }Enabled accounts with no expiration and no login history are a clear sign of an account created for persistence, or a forgotten account — either way, they should be disabled or removed.
Checking local group membership
Who is in the Administrators group?
Any unauthorized account in the Administrators group is a compromise in progress — not a suspicion, a certainty.
Get-LocalGroupMember -Group "Administrators"On a localized Windows install the group name may differ — check with Get-LocalGroup if the command above fails.
The output shows name, type (User/Group) and SID (Security Identifier). Compare the SID against the expected one whenever you find unfamiliar names — attackers sometimes create accounts with names similar to existing ones (admn instead of admin).
# List all local groups and their members
Get-LocalGroup | ForEach-Object {
$group = $_.Name
Get-LocalGroupMember -Group $group | Select-Object @{n="Group";e={$group}}, Name, ObjectClass
}Full details of a specific account
Get-LocalUser -Name "john" | Select-Object *This exposes properties that don't show up in the default listing:
- PasswordLastSet — when the password was last changed
- PasswordExpires — whether the password expires (service accounts often have this disabled)
- UserMayChangePassword — whether the user can change their own password
- AccountExpires — the account's expiration date
- LastLogon — date of the last successful login
Exporting to CSV: a complete audit report
For documentation or later analysis, export everything at once:
Get-LocalUser | Select-Object Name, Enabled, LastLogon, PasswordLastSet, AccountExpires, Description |
Export-Csv -Path "$env:USERPROFILE\Desktop\account-audit-$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation -Encoding UTF8The file generated on the Desktop includes the date in its name — useful for comparing weekly snapshots and identifying accounts that appeared between one period and the next.
How do you identify recently created accounts?
Get-LocalUser doesn't expose the account creation date directly, but you can cross-reference the security event log. Event 4720 records the creation of local accounts:
Get-WinEvent -FilterHashtable @{LogName="Security"; Id=4720} |
Select-Object TimeCreated, Message | Format-ListCombined with Get-WinEvent for investigating Windows events, you can trace exactly when each account was created and by which process — essential data in incident response.
Disabling or removing a suspicious account
Found something wrong? Disable it before investigating (less destructive than removing):
# Disable
Disable-LocalUser -Name "suspicious-account"
# Remove (irreversible)
Remove-LocalUser -Name "suspicious-account"Disabling preserves the SID and history for forensics; removing wipes the trail. In an incident response scenario, always disable first.
Quick check on problematic built-in accounts
Three checks every sysadmin should run on any new machine:
# Is Guest enabled? (it never should be)
Get-LocalUser -Name "Guest" | Select-Object Name, Enabled
# Is the built-in Administrator account active?
Get-LocalUser -Name "Administrator" | Select-Object Name, Enabled, LastLogon
# Accounts whose password is set never to expire
Get-LocalUser | Where-Object { $_.PasswordExpires -eq $null -and $_.Enabled -eq $true } |
Select-Object Name, PasswordLastSetGuest enabled, the built-in Administrator active for no apparent reason, and accounts with permanent passwords are classic findings in hardening audits. For the full list of settings that strengthen Windows, see the post on a safer Windows 11: 10 settings to enable today.
Get-LocalUser across a network: auditing multiple machines
The cmdlet has no -ComputerName parameter (unlike older cmdlets), but it works via PowerShell Remoting:
$computers = @("PC-01", "PC-02", "PC-03")
$result = Invoke-Command -ComputerName $computers -ScriptBlock {
Get-LocalUser | Where-Object { $_.Enabled -eq $true } |
Select-Object Name, Enabled, LastLogon, @{n="Machine";e={$env:COMPUTERNAME}}
}
$result | Export-Csv -Path "network-audit.csv" -NoTypeInformation -Encoding UTF8That produces a consolidated inventory of every enabled account across multiple workstations — the starting point for any identity audit in a corporate environment.
To monitor suspicious network connections on those same machines, pair it with Get-NetTCPConnection for monitoring open ports on Windows. And if you need to review authentication logs against security events, the guide to auditing security logs with PowerShell covers that full workflow.
Auditing local accounts isn't a one-off task — it should be a monthly routine in any environment. An orphaned account, or one created by malware, goes unnoticed for months if nobody checks. Get-LocalUser takes less than a minute to run and can show you exactly what's been waiting to be found.




Comments