The Surly Admin

Father, husband, IT Pro, cancer survivor

Get Service Information for a List of Computers

Today was a great example of how something that should be really easy with a computer turns out to be hard. Even in Powershell this happens. Case in point: on Spiceworks today a guy named dhubbard posted a vbScript that got a bunch of information about Windows services. It could be run against multiple servers from a simple text file.

OK, I thought, here’s a great example of how Powershell can convert this 54 line vbScript into 5 or less lines. I should be able to use Get-Content to import the text file, pipe it into Get-Service and then use Select to get the properties I want and pipe that into Export-CSV and do the same thing. In ONE line. Add some error control and maybe it takes a couple more lines but should be pretty simple.

First things first I need to take a look at the properties of Get-Service and make sure names haven’t changed.

Get-Service | Get-Member

This is where it started to go wrong. Several properties from the original vbScript were simply not there. Where’s Startup Type (automatic, manual and disabled)? A couple of others were missing too. Do some quick Googling and sure enough, Get-Service doesn’t provide that information so it’s a non-starter. My dreams of a one-liner begin to crumble.

I’ll have to resort to using WMI, again just like the original script. Luckily using WMI with Powershell is a lot easier then vbScript and the code won’t make your eyes cross. So we Get-Content on the servers.txt file, which has all of our servers in it with one purposefully misspelled one too–we’ll get to that later–and save that to a variable $Servers. Then we loop through that and begin our WMI calls.

$Servers = Get-Content c:\utils\servers.txt
$Result = @()
ForEach ($Server in $Servers)
{ $Services = Get-WmiObject Win32_Service -ComputerName $Server }

Next we need to loop through all of the services for that server and load up a PSObject with the data. I won’t go into a long winded tangent about why you want to use PSObjects but I will say they really are the core of Powershell. The ability to pass objects from one cmdlet to another is where a lot of the power in Powershell comes from. Things like the SORT cmdlet, ForEach and even Where. All of those cmdlets work with PSObjects so if your output is one of those objects you can use all of. More on that later!

$Services | ForEach {
If ($_)
{ $Result += New-Object PSObject -Property @{
'Computer Name' = $Server
'Service Display Name' = $_.Displayname
'Service Name' = $_.Name
'Start Mode' = $_.Startmode
'Start Name' = $_.Startname
State = $_.State
Status = $_.Status
'System Name' = $_.Systemname
}
}

But what about that bad server name in the servers.txt file? That’s where the “If ($_)” comes in on line #2 above. When a Get-WMIObject cmdlet fails, it returns a $null value and throws an error on the console. By checking if the pipeline variable has a value (not $null) we can tell if the Get-WMIObject cmdlet succeeded or failed. If it succeeds we want to record the good information in our PSObject. However, I want to know if it fails too, so we’ll record that information too.

Else
{ $Result += New-Object PSObject -Property @{
'Computer Name' = $Server
'Service Display Name' = $null
'Service Name' = $null
'Start Mode' = $null
'Start Name' = $null
State = $_.State
Status = "Could not connect to server"
'System Name' = $null
}
}

Now we just need to finish the script off by saving it to a CSV file.

$Result | Export-Csv c:\utils\result.txt

So What?

It’s a fair question. This script is 32 lines long, down from 54 of the vbScript. I think it’s a lot easier to read and understand–at least once you get your Powershell eyes–but we really haven’t saved much have we? So why do it at all? As it stands, you might as well just use the vbScript, it’s well written and gets the job done fast and efficiently.

But we can do more with Powershell, and it doesn’t have to take much more coding. First things first, we need to change the code to accept pipeline or command line parameters. Doing this is surprisingly easy. First you make the code into a function (simply put Function followed by the function name behind it and start a new scriptblock). Then you use the Begin/Process/End code blocks. When the script get’s to process the initial $_ variable will have the first line from the pipeline and it will loop through the Process block for each value in the pipeline. It will run the Begin block only once at the beginning of the script run (good for variable and environment setup) and then it will run the End block when the pipeline is exhausted. You don’t have to have Begin or End, but you must have Process. I also added a little Parameter to allow the function to also accept a -Servers argument on the command line.

Get the full source code here.  Now let’s put the code in our $Profile and take it for a spin.

Get-ServiceInfo

Returns all of the services running on your computer and a bunch of information about those services. But not too legible is it? Now try this:

Get-ServiceInfo -Servers localhost | Out-Gridview

Nice, huh? Now try this:

Get-ServiceInfo -Servers localhost | Where { $_.'Service Display Name' -like "*adobe*" } | Out-Gridview

I hope windows of possibility are starting to open up for you? Remember that servers.txt file?

Get-Content c:\utils\servers.txt | Get-ServiceInfo | Where { $_.'Service Display Name' -like "*adobe*" } | Out-Gridview

Let’s say I want to search the entire domain for any service from Symantec and export the results to a CSV. Make sure to have RSAT loaded.

Get-ADComputer -Filter * | % { $_.Name | Get-ServiceInfo | Where { $_.'Service Display Name' -like "*symantec*" } } | Export-CSV c:\utils\results.csv

Yeah, you just did that global search for very specific, relatively difficult information to get in a one-liner. That’s the power of Powershell and the usefulness of objects and the pipeline. Hopefully it’s as exciting to you as it is to me.

Advertisements

September 6, 2012 - Posted by | PowerShell | , , , ,

5 Comments »

  1. Fixed this post, not sure what the heck happened to it but it was all messed up. I was using Blogsy once upon a time and noticed it did similar funky formatting at one time so I suspect putting that back on my iPad was the culprit but ultimately I don’t know. Formatting looks fixed now but if you see something not quite right let me know.

    Comment by Martin9700 | September 25, 2012 | Reply

  2. Something about this post really hates me. Tried to fix the formating on the Get-Member output and it completely ate the post again. So I’ve updated it again and this time just got rid of the text output and used a picture. Hopefully I can leave this post alone now!!

    Comment by Martin9700 | October 3, 2012 | Reply

  3. […] aren’t in a “Running” status.  I touched on this earlier when talking about the Service Information […]

    Pingback by Building PSObject Performance « The Surly Admin | January 14, 2013 | Reply

  4. I was able to run get-serviceinfo just fine, but when I do -servers it says a parameter cannot be found.

    Comment by Andrew Zimmerman | December 2, 2016 | Reply

    • Looks like the -Servers parameter was renamed to -Name and I didn’t add an alias. Sorry about that!

      Comment by Martin9700 | December 2, 2016 | Reply


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: