Bookmark and Share

(Portions of this article previously appeared in the Microsoft Press book, Windows PowerShell Scripting Guide.)

Hey, Scripting Guy! Question

Hey, Scripting Guy! I would like to use Windows PowerShell to obtain information about the status of services that are configured on my computer. Do you have any hints you could share?

-- LB

Hey, Scripting Guy! Answer

Hello LB,

Microsoft Scripting Guy Ed Wilson here. This is like deja vu all over again. I have received nearly a dozen e-mail messages this week to the scripter@microsoft.com e-mail address that were all related to working with services on a computer. As a result of this mail, I decided I would gather some of the questions together and look at services this week. If you have some questions during the week, you can ping us on Twitter or write something on my wall on Facebook.  Of course, you are also free to send comments to scripter@microsoft.com as well.

From both a performance perspective and a security perspective, it is important to know which services are actually running on a server or workstation. To gather this information, there are two cmdlets that can perform the task. The first is the Get-Service cmdlet, and the second is the Get-WmiObject cmdlet.

From a functionality standpoint, the  Get-WmiObject cmdlet will use the WIN32_service class and has more capabilities such as the ability to change the configuration of a service. The added functionality comes with a priceit is a bit more difficult to use. 

When using the Get-Service cmdlet, the default behavior is to return a listing of all the services on the computer. The output lists all services, both running and stopped. There are only three properties returned: status, name, and displayname. The list is alphabetized by service name. The default output from  Get-Service is seen here (in truncated form):

PS C:\> Get-Service

Status   Name               DisplayName
------   ----               -----------
Running  AeLookupSvc        Application Experience
Stopped  ALG                Application Layer Gateway Service
Running  Appinfo            Application Information
Stopped  AppMgmt            Application Management
Running  AudioEndpointBu... Windows Audio Endpoint Builder
Running  Audiosrv           Windows Audio
Running  BFE                Base Filtering Engine
Running  BITS               Background Intelligent Transfer Ser...

If you want to know how many services are defined on the machine, you can use the CountServices.ps1 script. On my Windows Vista machine there are 139 services registered; clearly this becomes a management issue. Knowing the number of services registered on a machine may be useful as a simple indicator of static state on a machine. It is, of course, not a total indicator because you could uninstall a service and install a different one, and you would still have 139 services. But as a quick indicator it is useful. In the CountServices.ps1 script, we use the Get-Service cmdlet, surround the cmdlet name with parentheses, and then query the length property. The length property is used to count the number of services on the machine. The parentheses are used to tell Windows PowerShell to execute the code inside the parentheses first, and then perform the action on the outside of the parentheses—in this case, perform a count. The one-line CountServices.ps1 script is seen here.

CountServices.ps1

(Get-Service).length

Perhaps you are interested in services that are running. At this point, we can still use the Get-Service cmdlet, but we will need to bring in some additional power in the form of the Where-Object. To get the running services, we need to first use the Get-Service cmdlet to retrieve a listing of all the services. We then pipe the resulting object into the Where-Object cmdlet. After we are in the Where-Object, we use a script block to examine the status of each service in the object. We reference the current object by using the $_ automatic variable. We use the –eq operator to see if the status is equal to the word running. If it is, we receive it into the new psobject that gets created as a result of the Where-Object. We then surround the entire code on both sides of the pipeline object and query the length property with parentheses. This is what is displayed. The parentheses are used to force execution of the inside code before obtaining the length. The CountRunningServices.ps1 script is seen here.

CountRunningServices.ps1

(Get-Service |
where-object { $_.status -eq "running" }).length

Having the count of running services is very good. As with the count of installed services, it provides a quick “sanity check” to let us know if something has changed. This makes it easier to manage than just glancing at the services.msc management console, which is seen in the following image.  The management console interface is a bit busy, and as a result is not a good tool to use for a fast overview of service status. Windows PowerShell can provide a more comprehensive overview of the service situation on a computer.

Image of services.msc

 

Of course, the same caveat applies to using the count of defined services. It most certainly is not a substitute for auditing, and it does not provide enhanced security. It is just a quick “visual indicator” to tell if something has changed. 

If checking the number of running services is useful from a day-to-day management scenario, documenting the names of the running services is even more important. There are several reasons for writing the service information to a text file:

·         It is an easy process and provides a convenient means to check the state of the server.

·         It is useful from a documentation standpoint.

·         To maintain baseline configuration.

As an example, suppose you want to optimize your server by turning off all unnecessary services. It would make sense to write out the existing configuration before beginning to make extensive changes that could lead to a disaster. By having a documented working configuration, you can simplify this task. Writing running services to a text file is illustrated in the WriteRunningServicesToTxt.ps1 script. The text file created by the WriteRunningServicesToTxt.ps1 script is seen in the following image. The automatic column headers make the file easy to read, but could present a problem when used as input, unless certain allowances are made such as skipping the header line and the separator line.

Image of text file created by running WriteRunningServicesToTxt.ps1

 

In the WriteRunningServicesToTxt.ps1 script, we first create a variable called $strState and assign the string running to it. We then create a variable called $strPath and assign a path on our local system to it. In this example, we use the path, c:\fso\myservice.txt. Note that we include the prospective file name in the path. We then use the Get-Service cmdlet and pipeline the resulting objects to the Where-Object cmdlet. The  Where-Object cmdlet is used as a filter to weed out every service that is not running. After we have filtered the objects, we pipe the results to the Out-File cmdlet and feed the string contained in the $strPath variable to the –filepath parameter. The completed WriteRunningServicesToTxt.ps1 script is seen here.

WriteRunningServicesToTxt.ps1

$strState = "running"
$strPath = "c:\fso\myservice.txt"
Get-Service |
Where-Object { $_.status -eq $strState } |
Out-File -FilePath $strPath

In addition to simply writing service information to a text file, we can also write the results to a .csv file. This actually becomes a useful solution that opens many exciting doors. For example, Microsoft Excel and Microsoft Access like csv files, and Microsoft Word can take a .csv file and easily turn it into a table. But that is just Microsoft Office products; Microsoft SQL server loves .csv files nearly as much as Microsoft Excel does. The one thing I would recommend is that you visualize what your destination application is going to look like, and use PowerShell to configure the .csv file into an acceptable format for the final consumer of the data. For example, if your worksheet is a simple two columns with the first column being a service name and the second column being the status, it makes sense to clean up your .csv file to eliminate all the unnecessary properties before importing it into Microsoft Excel. This is what we do in the ExportRunningServices.ps1 script: We eliminate all properties except name, startmode, and startname before exporting the data to the .csv file. The resulting output is seen here:

Image of resulting output of running script

In the ExportRunningServices.ps1 script, we first create a variable called $strState and we assign the string running to it. We then create a variable called $strPath that will hold the string representing the path to our exported file. We then use the Get-WmiObject cmdlet to retrieve the WIN32_Service WMI class. We supply the string contained in the $strState variable to the –filter parameter of the Get-WmiObject cmdlet. We then pipe the resulting object to the Select-Object cmdlet where we choose the name, startmode, and startname properties from the WIN32_Service WMI class. We then export the object to a .csv file by using the Export-Csv cmdlet while supplying the string contained in the $strPath variable to the –path parameter. The ExportRunningServices.ps1 script is seen here.

ExportRunningServices.ps1

$strState = "running"
$strPath = "C:\FSO\service.csv"
Get-WmiObject win32_service -Filter "state='$strState'" |
select-object name, startmode, startname |
Export-Csv -Path $strPath

Well, LB, that is about it for documenting the status of services. Join us tomorrow as Services Week continues.

If you want to know exactly what we will be scripting tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys