PowerShell Problem Solver: Use PowerShell to Find Non-System Service Accounts

A common task for many IT Pros is to keep tabs on service accounts. That is the say, the account that a service runs under. For the majority of services, this account is determined automatically and is something like LocalSystem or LocalService. But sometimes there is a need to run a service under a local user account or even a domain account. It is important to know where those accounts are being used so you can coordinate password changes. Of course, you don’t want to have to navigate the Services management console to find these accounts. PowerShell can make this a pretty easy task.

If you are new to PowerShell you might first think to use the Get-Service cmdlet. Sadly, this cmdlet doesn’t expose the service account information.
Use PowerShell to Find Non-System Service Accounts
As you can see there are no properties to indicate the service account. Fortunately, this information is found using WMI. Let’s get the same service and look at all of the properties to discover the correct property name.

get-wmiobject win32_service -filter "Name = 'mssqlserver'" | select *

I’ve highlighted the relevant property name.
010615 1403 PowerShellP2
Now that I know the property name I can filter and work with it. For example, you may want to know what service accounts are in use on a given computer.

Get-CimInstance win32_service | Group StartName

010615 1403 PowerShellP3
I switched to the Get-CimInstance cmdlet to query the same WMI information. And even though my examples are searching locally, it is just easy to search one or more remote computers.

But what I really want is to identify that service running under .\Jeff which is a local user account. It could just as easily be a domain account. So I need to filter out anything that is LocalSystem, LocalService or NetworkService. You might be tempted to try an expression like this:

Get-CimInstance win32_service | where { $_.startname -notmatch 'LocalSystem|^NT Authority'} | Select Name,Displayname,Start*,State

010615 1403 PowerShellP4
Yes, this works, but is not a best practice especially if you are querying multiple remote computers. This is an example of late-filtering. I am retrieving all of the services and then filtering for the startname. The better approach is to use filtering with Get-CimInstance, or Get-WMIObject. Although I’ll admit the syntax can get a little tricky.

Get-CimInstance win32_service -filter "NOT (startname LIKE 'NT Authority%' OR startname LIKE 'LocalSystem')" | group startname

I suppose if I were looking for a specific account name that would be easier. But instead I am filtering out all non system service accounts. Here’s how I might search multiple remote computers.

Get-CimInstance win32_service -filter "NOT (startname LIKE 'NT Authority%' OR startname LIKE 'LocalSystem')" -computername chi-dc01,chi-dc02,chi-dc04,chi-core01,chi-hvr2,chi-fp02 | Select Name,Displayname,Start*,State,PSComputername

010615 1403 PowerShellP5
I have one service running under the domain administrator account, which is probably not a good thing. So I might as well change the account name. Or you might want to change the password if it is a legitimate service account.

The Set-Service cmdlet won’t work in this case. Again, we need to turn to WMI and the Change() method from the Win32_Service class.

$c = get-cimclass win32_service
$c.cimclassmethods["change"].parameters

010615 1403 PowerShellP6
Once you know the property names, using Invoke-CimMethod is pretty easy.

get-ciminstance win32_service -filter "Name='SharedAccess'" -computer chi-core01 |
Invoke-CimMethod -Name Change -Arguments @{Startname="LocalSystem";StartPassword="P@ssw0rd123XyZ"}

Checking the service again, I can see the new service account.
010615 1403 PowerShellP7
Note that if the service had been running you may have needed to restart it for changes to take effect. You could have used Restart-Service or the WMI methods.
When solving a problem like this with PowerShell, I encourage you to start small and work locally. Once your syntax is under control, it is generally a pretty simple task to scale your command out.