Get ServiceAccount Name with ADSI

One drawback to the service objects returned from Get-Service is that you can’t see what account the service is running under, often referred to as the service account. This will be something like LocalService or perhaps even a special user account like Mydomain\svcAccount123. You can retrieve this information by querying Windows Management Instrumentation (WMI) and the Win32_Service class. But did you know you can also use ADSI?

First connect to the computer:

PS C:\> [ADSI]$server=”WinNT://$env:computername”

The services will be child objects like users and groups. However, you need a little coding to coax them out into the open.

PS C:\> $server.psbase.children | where {$_.SchemaClassName -eq “Service”}

You should get a list of service objects. But if our goal is to simply retrieve the service account name, how about a script?

#Get-ServiceAccountName.ps1
Param ([string]$computername=$env:computername)
 
[ADSI]$server="WinNT://$computername"
$server.psbase.children | where {$_.SchemaClassName -eq "Service"} | 
select @{name="Computername";Expression={$computername.toUpper()}},`
@{name="Service";Expression={$_.name}},`
@{name="DisplayName";Expression={$_.DisplayName}},`
@{name="AccountName";Expression={$_.ServiceAccountName}},`
@{name="Path";Expression={$_.Path}}
# end of script

The script takes a computer name as a parameter, defaulting to the local computer. The script queries the service objects and returns a custom object like this:

Computername : CHAOS
Service      : SQLWriter
DisplayName  : SQL Server VSS Writer
AccountName  : LocalSystem
Path         : “C:\Program Files\Microsoft SQL Server\90\Shared\sqlwriter.exe”

I include the computername so that if you process a list of computers and want to save the output you can keep track of which service is on which computer.

PS C:\> get-content servers.txt | foreach {c:\scripts\get-serviceaccountname.ps1 $_ } | export-csv svcaccounts.csv

Or perhaps you’d this wrapped up in a function:

Function Get-ServiceAccountName {
    Param ([string]$computername=$env:computername)
    
    [ADSI]$server="WinNT://$computername"
    $server.psbase.children | where {$_.SchemaClassName -eq "Service"} | 
    select @{name="Computername";Expression={$computername.toUpper()}},`
    @{name="Service";Expression={$_.name}},`
    @{name="DisplayName";Expression={$_.DisplayName}},`
    @{name="AccountName";Expression={$_.ServiceAccountName}},`
    @{name="Path";Expression={$_.Path}}
}

Nothing really changes. Or perhaps you’d like a function that accepts pipelined input:

Function Get-ServiceAccountName {
PROCESS {
    [string]$computername=$_
        
    [ADSI]$server="WinNT://$computername"
    
    $server.psbase.children | where {$_.SchemaClassName -eq "Service"} | 
    select @{name="Computername";Expression={$computername.toUpper()}},`
    @{name="Service";Expression={$_.name}},`
    @{name="DisplayName";Expression={$_.DisplayName}},`
    @{name="AccountName";Expression={$_.ServiceAccountName}},`
    @{name="Path";Expression={$_.Path}}
 }
}

Now you can run expressions like these:

PS C:\> “ServerA”,”ServerB1″ | get-serviceAccountname | Sort Computername,Accountname | format-table computername,Service,Accountname
PS C:\> Get-Content computers.txt | get-serviceAccountname | where {$_.accountname -match “administrator”}

The second example will return all accounts with “administrator” in the name; handy if you want to see which services are running as a domain or local administrator.

This function comes in handy come audit time. By the way, you need administrative rights on any remote computer. There isn’t a way to pass alternate credentials, but even if you have a drive mapped with alternate credentials, that will be sufficient.

Download a text file with all these variations here.