PowerShell Problem Solver: Find Local User Accounts Using PowerShell

I got a good question from a Petri reader on Twitter not long ago. I was asked about finding local user accounts on a list of servers. Seems like a reasonable task and something that PowerShell can handle quite nicely. If you merely want to enumerate local user accounts, WMI is a great place to start. We can use the Win32_UserAccount class.

You can use with Get-WMIObject or Get-CIMInstance. Using the latter is preferred these days as it uses the PowerShell remoting ports and is much more firewall friendly. With either cmdlet it is also very easy to list users from multiple computers.

When developing a PowerShell expression using WMI, I like to test locally first if at all possible. This ensures my syntax is correct.

get-ciminstance win32_useraccount

Use the Get-CIMInstance cmdlet in Windows PowerShell. (Image Credit: Jeff Hicks)
Use the Get-CIMInstance cmdlet in Windows PowerShell. (Image Credit: Jeff Hicks)

That seemed pretty easy. My computer does not belong to a domain so the Domain property reflects the computername. As with most things in PowerShell, there is more here than meets the eye. I could re-run this command and pipe to Select-Object *, or I can use Get-CIMClass to discover the properties.

get-cimclass win32_useraccount | select -expand cimclassproperties | Select Name,CimType

Obtaining properties in Windows PowerShell. (Image Credit: Jeff Hicks)
Obtaining properties in Windows PowerShell. (Image Credit: Jeff Hicks)

 
Once you know the properties, it is simple enough to include them.

get-ciminstance win32_useraccount | Select Name,Description,Status,Disabled,AccountType | sort Status | format-table -groupby Status -Property Name,Description,Disabled,AccountType

Including the properties. (Image Credit: Jeff Hicks)
Including the properties. (Image Credit: Jeff Hicks)

 
Here’s a nice report with accounts grouped on whether they are disabled or not. I can repeat this process and specify a remote computer.

get-ciminstance win32_useraccount -comp chi-fp02 | sort Status | format-table -groupby Status Property Name,Description,Disabled,AccountType,PSComputername

A report that displays grouped accounts. (Image Credit: Jeff Hicks)
A report that displays grouped accounts. (Image Credit: Jeff Hicks)

 
Since I knew I was going to format results as a table, I can omit the Select-Object portion of my expression. Of course in PowerShell if I can do something for one computer, I can do it for many.

get-ciminstance win32_useraccount -ComputerName chi-web02,chi-fp02,chi-core01

030915 1332 PowerShellP5
I can do just about anything I want with the result from here. I can export to a CSV file, create an HTML report or re-format.

get-ciminstance win32_useraccount -ComputerName chi-web02,chi-fp02,chi-core01 |
sort PSComputername,Disabled,Name |
format-Table -groupby PSComputername -property Caption,Name,Disabled

030915 1332 PowerShellP6
I’ve typed out the computer names, but you can get those values from anywhere, even a text list using Get-Content.

$computers = get-content c:\scripts\mycomputers.txt
get-ciminstance win32_useraccount -ComputerName $computers |
sort PSComputername,Disabled,Name |
format-Table -groupby PSComputername -property Caption,Name,Disabled

Or perhaps need to limit your search. Don’t pipe your command to Where-Object to filter. Instead take advantage of WMI filtering and do it at the source, i.e. the remote server.

This is especially important if you are querying many remote computers. Let’s say you want to find all the disabled local accounts. Again, test locally first.

Get-CimInstance Win32_Useraccount -filter "Disabled = 'true'"

WMI filters use the legacy operators like the = sign and instead of the PowerShell option $True or $False, you need to use the string. Now it is a simple matter to extend this to my list of computers.

Get-CimInstance Win32_Useraccount -filter "Disabled = 'true'" -ComputerName $computers

030915 1332 PowerShellP7
Or perhaps I need to find all enabled local accounts other than Administrator.

Get-CimInstance Win32_Useraccount -filter "Disabled = 'false' AND name <>'Administrator'" -ComputerName $computers

Finding enabled accounts with Windows PowerShell. (Image Credit: Jeff Hicks)
Finding enabled accounts with Windows PowerShell. (Image Credit: Jeff Hicks)

 
I’ve been using Get-CIMInstace, which I think is the better choice. If necessary, you can also use Get-WMIObject. The syntax is identical.

Get-wmiobject Win32_Useraccount -filter "Disabled = 'false' AND name <>'Administrator'" -ComputerName $computers

Using the Get-wmiobject cmdlet in Windows PowerShell. (Image Credit: Jeff Hicks)
Using the Get-wmiobject cmdlet in Windows PowerShell. (Image Credit: Jeff Hicks)

 
You get the same information, although technically this is a different type of object.

Before we wrap up, let me point out that if you query a domain controller you will get domain accounts.
Querying a domain controller in Windows PowerShell. (Image Credit: Jeff Hicks)
Querying a domain controller in Windows PowerShell. (Image Credit: Jeff Hicks)

 
Sometimes this can be useful, but if your goal is to identify local user accounts on domain members, you’ll need to make sure no domain controllers are in your list. By the way, if you need to use alternate credentials, you will need to create CIMSessions for each remote computers. My examples have been ad-hoc with PowerShell setting up and tearing down temporary CIMSessions.

$csess =  new-cimsession -ComputerName $computers -Credential globomantics\administrator

Now I have a collection of CIMSessions, which I can use like this:

get-ciminstance win32_useraccount -filter "name='LocalAdmin'" -CimSession $csess | Select Name,Disabled,PSComputername

030915 1332 PowerShellP11
And because I can reuse these sessions, subsequent CIM commands run much faster.

get-ciminstance win32_useraccount -filter "disabled='false'" -CimSession $csess |
Sort PSComputername |
Select Name,Description,SID,PSComputername,@{Name="ReportDate";Expression={Get-Date}} |
Export-CSV c:\work\LocalAccounts.csv –NoTypeInformation

With a single command I created a CSV report of enabled local accounts. I could set this up as a PowerShell scheduled job to audit local accounts on a monthly basis, send them via email, or log them to a SQL database. Once you understand some PowerShell fundamentals and grasp the power of the pipeline, you’ll be amazed at what you can accomplish.