Powershell: Get-Service should return the user account the services are running under

Created on 1 Nov 2016  路  8Comments  路  Source: PowerShell/PowerShell

Currently the #1 item on the PowerShell UserVoice: https://windowsserver.uservoice.com/forums/301869-powershell/suggestions/12336204-get-service-should-return-the-user-account-the-ser

Arnaud Petitjean:

The Get-Service cmdlet should return the service account associated to each service.

We can get this information easily from the Win32_Service WMI class, but to me, it should be returned by Get-Service.

Expected behavior

Get-Service Spooler | Format-List Name,LogOnAs

Name : Spooler
LogOnAs : Local System

Actual behavior

Get-Service Spooler | Format-List Name,LogOnAs

Name : Spooler

Environment data

All versions of PowerShell

Area-Cmdlets Issue-Enhancement Resolution-Fixed

Most helpful comment

please also return StartupType and ServicePath

All 8 comments

please also return StartupType and ServicePath

@copdips StartType as been included in Get-Service output since 5.0:

C:\Users\jaiello> get-service sshd | fl *


Name                : sshd
RequiredServices    : {ssh-agent}
CanPauseAndContinue : False
CanShutdown         : False
CanStop             : False
DisplayName         : sshd
DependentServices   : {}
MachineName         : .
ServiceName         : sshd
ServicesDependedOn  : {ssh-agent}
ServiceHandle       :
Status              : Stopped
ServiceType         : Win32OwnProcess
StartType           : Manual
Site                :
Container           :

But yeah, we should probably have ServicePath too

@SteveL-MSFT , @lzybkr @joeyaiello : As mentioned, Service Account info as well service path is available from Win32_Service WMI class. Should I get started on this?

image

@charub you can always take up issues marked as Backlog, although it's preferred if you can prioritize issues you can fix marked for 6.0.0 milestone

Some food for thought: Don't forget the Description property, and consider device drivers as well.

Years ago when I used to work on PowerGUI (PowerShell v1 days, before StartType was added), we solved this problem like this:

$wmiServices = @{}
Get-WmiObject -Namespace root\cimv2 -Class Win32_Service | ForEach-Object {
    $wmiServices.Add($_.Name,$_)
}
Get-Service `
    | ForEach-Object {
        $_ | Add-Member -Name Win32_Service -MemberType NoteProperty -Value $(if ($wmiServices.ContainsKey($_.ServiceName)) {$wmiServices[$_.ServiceName]} else {$null}) -PassThru
    } `
    | Add-Member -Name Description -MemberType ScriptProperty -Value {if ($this.Win32_Service) { $this.Win32_Service.Description }} -PassThru `
    | Add-Member -Name LogonAs -MemberType ScriptProperty -Value {if ($this.Win32_Service) { $this.Win32_Service.StartName }} -PassThru `
    | Add-Member -Name StartupType -MemberType ScriptProperty -Value {if ($this.Win32_Service) { $this.Win32_Service.StartMode }} -PassThru

When digging into the details of how Get-Service works way back then, I discovered that you can also get device drivers from Get-Service. Device drivers can be discovered via WMI using the Win32_SystemDriver class. This led to the following proxy function (implemented using pspx) that supports retrieving the additional properties that were missing (although I didn't get the path) and that supports retrieving services, device drivers, or both:

# This script creates a proxy function for Get-Service. It adds support for retrieving both
# device drivers (which are services under the covers) and windows services, using wildcards
# or fullnames. It allows you to specify the type of service you are looking for as well with
# the -ServiceType parameter. And lastly, it retrieves the Description, the LogonAs and the
# StartupType properties of the services you enumerate (these properties are not visible with
# the built-in Get-Service cmdlet).

Fix-It Get-Service `
    -Synopsis 'Gets the Windows services and device driver services on a local or remote computer.' `
    -Description @'
The Get-Service command gets objects that represent the services on a local computer or on a remote computer, including running and stopped services.

You can direct Get-Service to get only particular services by specifying the service name or display name of the services, or you can pipe service objects to Get-Service.

Services returned from this command may be Windows services or device driver services. You can direct Get-Service to only return services of a particular type using the ServiceType parameter.
'@ `
    -Parameter @(
        @{
            Name = 'ServiceType'
            Alias = 'Type'
            Type = [System.String]
            ValidateSet = @('Service','DeviceDriver','All')
            Description = @'
Gets only services of the specified type. The default is All if you search by name without wildcards, or Service otherwise. 

    The valid values for this parameter are: 

    -- All: Gets all services on a computer.
    -- Service: Gets standard Windows Services on a computer.
    -- DeviceDriver: Gets device driver services on a computer.

'@
            DefaultValue = '''All'' if you search by name without wildcards, or ''Service'' otherwise.'
            Example = @(
                @{
                    ScriptBlock={Get-Service -ServiceType DeviceDriver}
                    Description='This command retrieves all device driver services on the local computer. This example shows that you can enumerate all device driver services.'
                }
                @{
                    ScriptBlock={Get-Service -Name hvboot}
                    Description='This command gets the hvboot device driver service on the local computer. This example shows that you can search for device driver services by name.'
                }
                @{
                    ScriptBlock={Get-Service -ServiceType All}
                    Description='This command gets both standard Windows Services and device driver services on the local computer.'
                }
            )
        }
    ) `
    -DefaultParameterValue @{
        ServiceType = {if ($PSPassThruParameters.ContainsKey('Name') -and ($Name -notmatch '[\?\*]')) {'All'} else {'Service'}}
    } `
    -Begin {
        function ConvertTo-WmiFilter {
            [CmdletBinding()]
            [OutputType([System.String])]
            param(
                [Parameter(Mandatory=$true,Position=0)]
                [ValidateNotNullOrEmpty()]
                [System.String]
                $PropertyName,

                [Parameter()]
                [ValidateNotNullOrEmpty()]
                [System.String[]]
                $FilterValue,

                [Parameter()]
                [ValidateNotNullOrEmpty()]
                [System.String[]]
                $LiteralFilterValue
            )
            $wmiFilterSet = @()
            if ($FilterValue) {
                foreach ($item in $FilterValue) {
                    if ([System.Management.Automation.WildcardPattern]::ContainsWildcardCharacters($item)) {
                        $wmiEquivalentItem = $item -replace '``','垄' -replace '^\*','%' -replace '^\?','_' -replace '([^`]{1}?)\*','$1%' -replace '([^`]{1}?)\?','$1_' -replace '`\*','*' -replace '`\?','?' -replace '垄','`'
                        $wmiFilterSet += "$PropertyName LIKE '$wmiEquivalentItem'"
                    } else {
                        $wmiEquivalentItem = [System.Management.Automation.WildcardPattern]::Unescape($item)
                        $wmiFilterSet += "$PropertyName = '$item'"
                    }
                }
            }
            if ($LiteralFilterValue) {
                foreach ($item in $LiteralFilterValue) {
                    $wmiFilterSet += "$PropertyName = '$item'"
                }
            }
            $wmiFilterSet -join ' OR '
        }
        $wmiServices = @{}
        $getWmiObjectParameters = @{
            Namespace = 'root\cimv2'
            Class = $(if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('Name')) {'Win32_BaseService'} else {'Win32_Service'})
        }
        if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('ComputerName')) {
            $getWmiObjectParameters['ComputerName'] = $ComputerName
        }
        if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('ServiceType')) {
            switch ($ServiceType) {
                'DeviceDriver' {
                    $getWmiObjectParameters['Class'] = 'Win32_SystemDriver'
                    break
                }
                'Service' {
                    $getWmiObjectParameters['Class'] = 'Win32_Service'
                    break
                }
                default {
                    $getWmiObjectParameters['Class'] = 'Win32_BaseService'
                    break
                }
            }
        }
        $filter = ''
        if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('Name')) {
            $filter += '(' + (ConvertTo-WmiFilter -PropertyName Name -FilterValue $Name) + ')'
            $filter += ' OR '
            $filter += '(' + (ConvertTo-WmiFilter -PropertyName DisplayName -FilterValue $Name) + ')'
        } elseif ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('DisplayName')) {
            $filter += '(' + (ConvertTo-WmiFilter -PropertyName DisplayName -FilterValue $DisplayName) + ')'
        }
        if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('Include')) {
            if ($filter) {
                $filter += ' AND '
            }
            $filter += '(' + (ConvertTo-WmiFilter -PropertyName Name -FilterValue $Include) + ')'
        }
        if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('Exclude')) {
            if ($filter) {
                $filter += ' AND NOT '
            }
            $filter += '(' + (ConvertTo-WmiFilter -PropertyName Name -FilterValue $Exclude) + ')'
        }
        if ($filter) {
            $getWmiObjectParameters['Filter'] = $filter
        }
        $serviceNames = @()
        Get-WmiObject @getWmiObjectParameters | ForEach-Object {
            if (-not $wmiServices.ContainsKey($_.__SERVER)) {
                $wmiServices[$_.__SERVER] = @{}
            }
            if ($serviceNames -notcontains ($_.Name)) {
                $serviceNames += $_.Name
            }
            $wmiServices[$_.__SERVER][$_.Name] = $_
        }
        if ($serviceNames) {
            $PSPassThruParameters.Remove('Name') | Out-Null
            $PSPassThruParameters.Remove('Include') | Out-Null
            $PSPassThruParameters.Remove('Exclude') | Out-Null
            $PSPassThruParameters['Name'] = $serviceNames | Sort-Object
        }
    } `
    -Process {
        ForEach-Object {
            $serviceComputerName = $_.MachineName
            if (@('127.0.0.1','.','localhost',"${env:COMPUTERNAME}.${env:USERDNSDOMAIN}") -contains $serviceComputerName) {
                $serviceComputerName = $env:COMPUTERNAME
            }
            if ($wmiServices.ContainsKey($serviceComputerName) -and $wmiServices.$serviceComputerName.ContainsKey($_.ServiceName)) {
                $_ `
                    | Add-Member -Name WmiService -MemberType NoteProperty -Value $wmiServices.$serviceComputerName[$_.ServiceName] -PassThru `
                    | Add-Member -Name Description -MemberType ScriptProperty -Value {if ($this.WmiService) { $this.WmiService.Description }} -PassThru `
                    | Add-Member -Name LogonAs -MemberType ScriptProperty -Value {if ($this.WmiService) { $this.WmiService.StartName }} -PassThru `
                    | Add-Member -Name StartupType -MemberType ScriptProperty -Value {if ($this.WmiService) { $this.WmiService.StartMode }} -PassThru `
                    | Add-Member -Force -Name ToString -MemberType ScriptMethod -Value {$this.Name} -PassThru
            }
        }
    } `
    -DefineNow

I just wanted to add this here because it made for easier use of Get-Service that gave back more data properties and that could be used to enumerate device drivers as well.

Whatever happened to this? It looks like it never made it out for public use.

Try:

Get-Service wuauserv | fl * -Force
Was this page helpful?
0 / 5 - 0 ratings

Related issues

rkeithhill picture rkeithhill  路  3Comments

aragula12 picture aragula12  路  3Comments

abock picture abock  路  3Comments

JohnLBevan picture JohnLBevan  路  3Comments

alx9r picture alx9r  路  3Comments