Tuesday, August 28, 2012

Powershell: Get the Services Running and Their Service Accounts on a list of Remote Machines


Time for me to play Powershell Ranger and throw a quick script at you! BAM. A co-worker asked me today to whip up a quick script that could retrieve the services and their corresponding service accounts (user account who the system uses to run the service) from a list or array of remote hosts. I first thought, "Hey no Biggie....Smalls!, I will use the Get-Service cmdlet". The issue with that as seen in the screen shot below the Get-Service cmdlet, when expanding out the full results for one service, does not include a property which details the Service Account.














So instead of using the Get-Service cmdlet we will turn to the Get-WMIObject cmdlet and query the win32_service class to get the values we need. As seen in the below screenshot this method of retrieving a systems Service details contains much more data and it even inlcudes the value "StartName", which contains the Service Account name.


I recommend trying both the methods your self to see the difference:
    Get-Service -Name WinRM | Select *
    Get-WmiObject win32_service | Where {$_.name -eq "WinRM"} | Select *

Now to the Script:

  • Let's review whats going on in this script. First off we create a variable " ServerArray", here is where you will add the names of the servers you wish to run this script against. Follow the instructions in the comment below to add the Server names correctly to create a variable array. If you have a list of servers in a text file, you can have Powershell grab the content of that file and create an array of server  names that way. To do that just remove everything after the "=" sign after ServerArray and enter the following code: Get-Content -Path <path to text file>, that's all! Remember for simplicitys' sake, one server name per line in the text file.
  • Next we let the user running the script either define the location to save the out put file that this script will make or let the script create the default directory which is "C:\Temp\SvcsandSvcAccounts\".
  • Next we have the script verify that the path exists and if it doesn't, the script will create the path to the save location using the New-Item cmdlet. Give a path to the New-Item cmdlet and it will create the file structure it needs to get to the final location.
  • After we define the path to the save location and create the directory structure to it, we begin to loop through each server in $ServerArray. We tell the user with the Write-Host cmdlet which host we are currently grabbing the information for and then go through and grab 4 values for each service.
  • The Get-WMIObject cmdlet uses the -ComputerName flag to connect to remote servers. In our case we pass the server names to the -ComputerName flag using the $Server variable in the foreach loop. Use the Get-WMIObject alias if you want also: gwmi.
  • In this script and many others of mine, I decided to use the calculated property capabilities of Powershell only to rename the Headings on some of the returned value columns, but this is only scratching the surface of the Powershell calculated property ability. I hope to discuss calculated properties in a later blog post. a calculated property example found in the script is any line that loks like this:  @{N="Name";E={$_.Expression}}, you can define your own Names for values in the "N" field, and do calculations in the "E" field. In my example I decided to just regurgitate the $_ variable with an associated property instead of doing anything more complicated with it.
  • In the final steps we do two very simple things: Sort the output using the Sort-Object cmdlet and out put the entire table to a text file called $Server-Services.txt, -or in our case, localhost-services.txt

# List multiple systems in the SystemsArray Variable: quote each system name and seperate with a comma, # this creates an array. Ex: "system1","system2","system3"


$ServerArray="localhost"
$DefineSaveLocation=""
if ($DefineSaveLocation -eq "")
    {$DefineSaveLocation="C:\Temp\SvcsandSvcAccounts\"}
$SaveLocaPath=Test-Path $DefineSaveLocation
if ($SaveLocaPath -eq $False)
    {New-Item -ItemType directory -Path $DefineSaveLocation}
cd $DefineSaveLocation
Foreach ($Server in $ ServerArray )
{
Write-Host "Retrieving Servers for $Server "    
Get-WmiObject win32_service -ComputerName $Server  | select Name,
@{N="Startup Type";E={$_.StartMode}},
@{N="Service Account";E={$_.StartName}},
@{N="System Name";E={$_.Systemname}} | Sort-Object "Name" > ".\$Server -Services.txt"
}

The generated output from the script looks like this, but you could do CSV, html, excel...whatever you like:














This was a super simple Powershell script, but with Powershell the possibilities are endless. More to come!

5 comments:

  1. I have a list of servers (txt file), I need to get the services running in all this from my workastation, How can I load, read and use the list to get the remote info from servers,

    ReplyDelete
    Replies
    1. Mauricio,
      I know this is a pretty late response, but in case you were still looking for this answer, here is a short explanation of how to read the host names from a file. Simply put the names of the hosts in a text file, one per line, and using the following script change line 1 to:
      $ServerArray=Get-Content
      Thats it! The script will loop through each of the hosts you provided in the text file!

      Delete
    2. Looks like the blog didnt like the carrots I put on the last reply, change line 1 to:
      $ServerArray=Get-Content [path to text file]

      Delete
  2. Hello, I am trying to do this, but I was wondering why use wmi as opposed to the built-in get-service in powershell? I'm not sure as I'm learning and haven't found any good answer to that question.

    I also only want to query for 1 specific service name, and report on if it's installed, and the status of the service, and output to excel and html to compare as I have to provide this as a report.

    Any help would be TRULY appreciated, I read your blog all the time, it's fantastic as a reference!

    ReplyDelete
    Replies
    1. JP, in a nut shell the above script had the requirement to get the account running the service and the Get-Service cmdlet at the time of writing did not include the account running the service. In order to get the name of the account I chose to query WMI which does get the account name, so that is the big reason for using WMI above! To query for only one service you would use the Get-Service cmdlet with the name of the service, example:
      Get-Service VPXD

      To output this result to html, use the following command:
      Get-Service [service name] | Convertto-HTML | Set-Content c:\desktop\test.html.

      Hope this is helpful, let me know if I can help out in any other way!

      Delete