More fun with services, PowerShell and ADSI

The other day I showed you how to use ADSI and PowerShell to manage services on remote computers. Since the ADSI type adapter is admittedly “kludgy” at times, I put together a script that you could use like a cmdlet to retrieve service information via ADSI.

   1: #Get-RemoteService.ps1
   2:  
   3: param([string]$computername=$env:computername,
   4:       [string]$service=$(Throw "You must enter a service name like spooler")
   5:       )
   6:  
   7: function Decode-StartType {
   8:     Param([int]$startType)
   9:  
  10:     Switch ($startType) {
  11:         0    {"Boot"}
  12:         1    {"System"}
  13:         2    {"Automatic"}
  14:         3    {"Manual"}
  15:         4    {"Disabled"}
  16:        default    {"Unknown"}
  17:     }
  18: }
  19:  
  20: Function Decode-ServiceType {
  21:     Param([int]$ServiceType)
  22:     
  23:     Switch ($ServiceType ) {
  24:       1     {"Kernel-Mode Driver"}
  25:       2     {"File System Driver"}
  26:       4     {"Adapter Arguments"}
  27:       8     {"File System Driver Service"}
  28:       16    {"Own Process"}
  29:       32    {"Shared Process"}
  30:       272   {"Own Process - Interactive"}
  31:       288   {"Shared Process - Interactive"}
  32:       default   {"Unknown"}
  33:      }
  34: }
  35:  
  36: [ADSI]$svc="WinNT://$computername/$service,service"
  37:  
  38: if ($svc.name) 
  39: {
  40:     #connection made to valid service
  41:       if ($svc.status -eq 4) {$status="Running"}
  42:       elseif ($svc.status -eq 1) {$status="Stopped"}
  43:       else {$status="Unknown"}
  44:         
  45:     $obj=New-Object PSobject
  46:     
  47:     $obj | Add-Member NoteProperty "Computername" $computername.toUpper()
  48:     $obj | Add-Member NoteProperty "Service" $svc.name.value
  49:     $obj | Add-Member NoteProperty "DisplayName" $svc.displayname.value
  50:     $obj | Add-Member NoteProperty "Status" $status 
  51:     $obj | Add-Member NoteProperty "StartType" (Decode-StartType $svc.startType.value)
  52:     $obj | Add-Member NoteProperty "ServiceType" (Decode-ServiceType $svc.servicetype.value)
  53:     $obj | Add-Member NoteProperty "ServiceAccount" $svc.serviceaccountname.value 
  54:     $obj | Add-Member NoteProperty "Path" $svc.Path.Value
  55:     
  56:     write $obj
  57: } 
  58: else 
  59: {
  60:     Write-Warning ("Failed to find the {0} service on {1}" -f $service,$computername.ToUpper())
  61: }
  62:  

The script creates custom object that uses more admin friendly values.

PS C:\> c:\scripts\posh\get-remoteservice -computer godot -service remoteregistry

Computername   : GODOT
Service        : remoteregistry
DisplayName    : Remote Registry
Status         : Running
StartType      : Automatic
ServiceType    : Shared Process
ServiceAccount : NT AUTHORITY\LocalService
Path           : C:\WINDOWS\system32\svchost.exe -k LocalService

But what about finding all the services? You can use ADSI for this as well. Try these commands

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

PS C:\> $server.psbase.children | Where {$_.class -eq “service”} | format-table Name,Displayname,Status -autosize

You can substitute any computername you want. $env:computername will resolve to your computername. Of course the output leaves a little to be desired. Ergo, another script.

   1: #Get-AllRemoteService.ps1
   2:  
   3: param([string]$computername=$env:computername)
   4:  
   5: function Decode-StartType {
   6:     Param([int]$startType)
   7:  
   8:     Switch ($startType) {
   9:         0    {"Boot"}
  10:         1    {"System"}
  11:         2    {"Automatic"}
  12:         3    {"Manual"}
  13:         4    {"Disabled"}
  14:        default    {"Unknown:$starttype"}
  15:     }
  16: }
  17:  
  18: Function Decode-ServiceType {
  19:     Param([int]$ServiceType)
  20:     
  21:     Switch ($ServiceType ) {
  22:       1     {"Kernel-Mode Driver"}
  23:       2     {"File System Driver"}
  24:       4     {"Adapter Arguments"}
  25:       8     {"File System Driver Service"}
  26:       16    {"Own Process"}
  27:       32    {"Shared Process"}
  28:       272   {"Own Process - Interactive"}
  29:       288   {"Shared Process - Interactive"}
  30:       default   {"Unknown:$servicetype"}
  31:      }
  32: }
  33:  
  34: [ADSI]$server="WinNT://$computername"
  35:  
  36: if ($server.name) 
  37: {
  38: #connection made to valid  server
  39: #filter child objects and get services
  40:     $services=$server.psbase.children | where {$_.class -eq "service"}
  41:     
  42:     foreach ($svc in $services) {
  43:         if ($svc.status -eq 4) {$status="Running"}
  44:         elseif ($svc.status -eq 1) {$status="Stopped"}
  45:         else {$status="Unknown"}
  46:         
  47:         $obj=New-Object PSobject
  48:         
  49:         $obj | Add-Member NoteProperty "Computername" $computername.toUpper()
  50:         $obj | Add-Member NoteProperty "Service" $svc.name.value
  51:         $obj | Add-Member NoteProperty "DisplayName" $svc.displayname.value
  52:         $obj | Add-Member NoteProperty "Status" $status 
  53:         $obj | Add-Member NoteProperty "StartType" (Decode-StartType $svc.startType.value)
  54:         $obj | Add-Member NoteProperty "ServiceType" (Decode-ServiceType $svc.servicetype.value)
  55:         $obj | Add-Member NoteProperty "ServiceAccount" $svc.serviceaccountname.value 
  56:         $obj | Add-Member NoteProperty "Path" $svc.Path.Value
  57:         
  58:         write $obj
  59:  } #end foreach
  60: } #end if 
  61: else 
  62: {
  63:     Write-Warning ("Failed to connect to {0}" -f $computername.ToUpper())
  64: }

This script merely needs a computername as a parameter. It will default to the localhost.

PS C:\Scripts> .\get-allremoteservice FILE02

You’ll get my custom service object for every service on that computer. Of course, because the script is writing an object to the pipeline you can sort, filter, group, export and anything else you’d like to do.

PS C:\scripts> .\get-allremoteservice | where {$_.status -eq “running”} | sort ServiceAccount | format-table -groupby ServiceAccount service,Displayname,StartType,ServiceType

Eventually you’ll be able to use Get-Service everywhere (in PowerShell v2.0), but this might be a valuable alternative until then.

Get both scripts here in a convenient zip file.