Summary: Windows PowerShell MVP, Mike Robbins, shows us how to use Windows PowerShell to check the status of Windows services and improve the accuracy of results.
Microsoft Scripting Guy, Ed Wilson, is here. Welcome back guest blogger, Mike Robbins.
Mike F Robbins is a senior systems engineer with 20 years of professional experience as an IT pro. Mike has provided enterprise computing solutions for educational, financial, health care, and manufacturing customers. He’s a Windows PowerShell MVP and self-proclaimed evangelist who uses Windows PowerShell on a daily basis to administer and manage Windows Server, Hyper-V, SQL Server, Exchange Server, SharePoint, Active Directory, Remote Desktop, EqualLogic storage area networks, AppAssure, and Backup Exec.
Mike is the winner of the advanced category in the 2013 PowerShell Scripting Games. He has written guest blog posts for the Hey, Scripting Guy! Blog, PowerShell.org, and PowerShell Magazine, and he is a contributing author of a chapter in PowerShell Deep Dives. Mike is the leader and cofounder of the Mississippi PowerShell User Group. Mike is also a frequent speaker at Windows PowerShell and SQL Server Saturday technology events. He blogs at mikefrobbins.com and can be found on twitter at @mikefrobbins.
Here’s Mike…
When it comes to the subject of checking the status of services with Windows PowerShell, the first cmdlet that comes to mind is Get-Service, so we’ll take a look at it first.
Get-Service -Name sppsvc |
Select-Object -Property *
As shown in the previous example, Get-Service has a Status property, but the issue is that it doesn’t have a property that tells us whether the service is set to be started automatically, so there’s no way of knowing whether the service should be running.
Because the Get-Service cmdlet doesn’t have properties that contain the necessary information to determine what the start mode of a service is, we’ll resort to using Windows Management Instrumentation (WMI). In this scenario, I’m going to maintain backwards compatibility with Windows PowerShell 2.0, so I’ll use the Get-WmiObject cmdlet instead of Get-CimInstance cmdlet.
Get-WmiObject -Class Win32_Service -Filter {State != 'Running' and StartMode = 'Auto'}
In this example, notice the following:
- Querying the Win32_Service WMI class with Windows PowerShell allows us to see the start mode of a service.
- Best practices were followed because the command uses the Filter parameter to filter left instead of piping the information for all services to the Where-Object cmdlet.
- In the results from WMI, the State property returns the value that was listed in the Status property in the results of Get-Service.
To learn more about the service that isn’t started, we need to determine if any additional properties exist that could provide us with additional information. Any cmdlet that produces output can be piped to the Get-Member cmdlet to determine what properties and methods exist, so that’s exactly what we’ll do in this scenario. We’re specifically looking for properties, so we’ll specify that via the MemberType property of Get-Member:
Get-WmiObject -Class Win32_Service -Filter {State != 'Running' and StartMode = 'Auto'} |
Get-Member -MemberType Properties
The DisplayName property looks promising to give us a friendlier name for this service:
Get-WmiObject -Class Win32_Service -Filter {State != 'Running' and StartMode = 'Auto'} |
Select-Object -Property DisplayName, Name, StartMode, State
To determine a little more information about this service, let’s take a look at it in the GUI. We’re specifically looking to see what the GUI says the startup type is.
We’ve just discovered that querying WMI for the startup mode of services doesn’t differentiate services that are set to start automatically from the ones that are set to start automatically with a delayed start. This could be a problem because the delayed start services might return a false positive if we make the assumption that services set to automatic should be running. The delayed start services aren’t necessarily supposed to be running because, by definition, their startup is delayed.
To determine which services are set to start automatically, but not with a delayed start, we’ll need to query the registry:
Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\*' |
Where-Object {$_.Start -eq 2 -and $_.DelayedAutoStart -ne 1} |
Select-Object -Property PSChildName
We’re specifically looking for services that have a value of 2 for the Start property. This can mean automatic or automatic with delayed start. We’re also looking for services that don’t have a value of 1 for the DelayedAutoStart property. The DelayedAutoStart property is created when a service is set to start automatically with a delayed start. If the service is changed later, that property will remain, but its value will be 0 if the service is set to start automatically (not a delayed start). If the service is set to manual, the Start property will be 3, and if it’s disabled, it will be 4.
Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\sppsvc'
Now we need to combine what we previously accomplished by querying WMI with the information we retrieved from the registry to eliminate the delayed start services. In the following example, I’ve stopped the Windows Audio and Print Spooler services on the local computer so we should receive some results, but the Software Protection service should be excluded this time.
Get-WmiObject -Class Win32_Service -Filter {State != 'Running' and StartMode = 'Auto'} |
ForEach-Object {Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\$($_.Name)" |
Where-Object {$_.Start -eq 2 -and $_.DelayedAutoStart -ne 1}} |
Select-Object -Property @{label='ServiceName';expression={$_.PSChildName}}
While we could accomplish the tasks we set out to accomplish with a one-liner as in the previous example. For remote machines, this command needs to run locally on the remote computer because the registry provider doesn’t natively support remote access. We’ll write a couple of reusable functions that use the Windows PowerShell remoting Invoke-Command cmdlet. These functions could be integrated into a Windows PowerShell script module.
Note You can download the two functions that are used from this point forward from the Script Center Repository.
Get-MrAutoStoppedService -ComputerName dc01, sql01, sql02, web01
You could also retrieve the computer names from a text file (or from Active Directory) and pipe them to these functions. This time, we’ll use the Start-MrAutoStoppedService function to start the services that should be running. We’ll also use the Credential parameter to specify alternate credentials when connecting to the remote computers.
Get-Content -Path C:\Scripts\servers.txt |
Start-MrAutoStoppedService -Credential (Get-Credential)
Now when we check to see if any services on our servers aren’t running, nothing is returned because all of the services that should be running are running.
Get-Content -Path C:\Scripts\servers.txt |
Get-MrAutoStoppedService
We could schedule the Start-MrAutoStoppedService function to run periodically by using a scheduled task, and then use the following command to notify us via email if problems were found and action was taken.
if (Get-Content -Path C:\Scripts\servers.txt | Start-MrAutoStoppedService | Out-String -OutVariable Services) {
$Params = @{
From = 'serverstatus@mikefrobbins.com'
SmtpServer = 'mail.mikefrobbins.com'
Subject = 'Server Service Status Update'
To = 'helpdesk@mikefrobbins.com'
Body = "The following services are configured with a startup type of Automatic. They were in the stopped state and they have now been started: `n $Services"
}
Send-MailMessage @Params
}
~Mike
Thanks, Mike, for sharing your time and knowledge.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy