Use PowerShell to List Stopped Services that are Set to Start Automatically While Excluding Delayed Start Services

Have you ever had 67 emails about services on your servers being up and down from your monitoring solution? It's not a good feeling and those emails are only about the ones that monitoring is setup for. What other services could be stopped that aren't being monitored? Wouldn't you like a quick and easy way to check whether or not all of the services that are set to start automatically are actually running?

My first thought: Use the Get-Service cmdlet. Unfortunately it doesn't have a parameter for the Startup Type:

1Get-Service | Get-Member

ps-services1.jpg

It's a little more difficult, but what about using WMI via the Get-WMIObject cmdlet? Most of the WMI classes I've found useful start with win32 and I'm looking for a service class:

1Get-WmiObject -List win32*service*

ps-services2.jpg

What properties are available when using the WMI Win32_Service class? This looks promising because there's a StartMode parameter. The only problem is that the BITS service shown in the following image is set to Delayed Start and the StartMode is "Auto":

1Get-WmiObject -Class Win32_Service -Filter "name = 'bits'" |
2Format-List * -Force

ps-services3.jpg

WMI can be used to retrieve a list of stopped services that are set to start automatically:

1Get-WmiObject -Class Win32_Service -Filter "state = 'stopped' and startmode = 'auto'" |
2Select-Object name

ps-services4.jpg

The problem is that all of the services shown in the previous image except one are set to "Automatic (Delayed Start)" as shown in the example in the following image. This causes a sort of false positive because those services aren't necessarily suppose to be running:

ps-services5.jpg

The only way I've found to determine if a service is set to "DelayedAutoStart" is in the registry. This property does not exist for services that are not set to "DelayedAutoStart":

1Get-ChildItem HKLM:\SYSTEM\CurrentControlSet\Services |
2Where-Object {$_.property -contains "DelayedAutoStart"} |
3Select-Object -First 1

ps-services6.jpg

Here's a list of all the services on my pc that are set to "DelayedAutoStart":

1Get-ChildItem HKLM:\SYSTEM\CurrentControlSet\Services |
2Where-Object {$_.property -contains "DelayedAutoStart"} |
3Select-Object -ExpandProperty PSChildName

ps-services7.jpg

The first command retrieves a list of stopped services that are set to start automatically. The second one retrieves a list of services that are set to "DelayedAutoStart". Combine the two so the items returned by the first command that exist in the second one are not returned in the final results:

1(Get-WmiObject -Class Win32_Service -Filter "state = 'stopped' and startmode = 'auto'" |
2Select-Object -ExpandProperty name) |
3Where-Object {(Get-ChildItem HKLM:\SYSTEM\CurrentControlSet\Services |
4Where-Object {$_.property -contains "DelayedAutoStart"} |
5Select-Object -ExpandProperty PSChildName) -notcontains $_} |
6Select-Object @{l='Computer Name';e={$env:computername}},
7@{l='Service Name';e={$_}}

ps-services81.jpg

For the final test, I'll run this script on 32 servers at the same time:

ps-services91.jpg

There are 32 different server names contained in the servers.txt file and it took 3 seconds to complete. Now that's what I call efficiency and a lot less hassle than 67 emails to look at.

ps-services101.jpg

µ