Well as the title suggests, I’m happy with the code, but I always find myself adding more and more code around the cmdlets. Service control in Windows has been pretty straight forward for the past few decades. Obviously PowerShell can control the state and configuration of services, but one thing I’ve always run into with service control is reacting to how the service stops and starts and also managing the state of dependent services. I’m sharing some short code functions that I use.

You’ve probably been using PowerShell long enough to know about the simple service commands, such as:

  1. Get-Service
  2. Stop-Service
  3. Start-Service
  4. Restart-Service

They are kind of like a black box, kind of like those buttons inside the traditional Services MMC. I’m willing to bet, that you’ve encountered a time or two inside the Service MMC that you’ve tried to stop a service, it didn’t work and you ended up going through task manager or some other tool to figure out what was going on. Thanks to PowerShell for giving us these commands, but sometimes you can’t take them at face value. Most scripts need to have a little bit of error checking/control added to them.

Oh wait, did you think about this? Since you stopped the service you were targeting, are you aware of any dependent services that will be stopped? Last time I checked, Stop-Service didn’t tell you about all the dependent services that were stopped. So when you call Start-Service, what about all the other stopped services?

Here is a function I use to start my service, Start-WindowsService:

Function Start-WindowsService {
	Param(
        [string]$ServiceName,
        [int]$SecondsToWait = 30
    )

    $DependentServices = (Get-Service -Name $ServiceName -ErrorAction SilentlyContinue).DependentServices | Where { Get-WmiObject win32_service -Filter "name = '$($_.Name)' and StartMode = 'auto'" }
    If ($DependentServices -ne $null) {
        ForEach ($DependentService in $DependentServices) {
            Start-WindowsService $DependentService.Name -ErrorAction SilentlyContinue
        }
    }
    Start-Service -Name $ServiceName -ErrorAction SilentlyContinue
    $ServiceStateCorrect = Wait-ServiceState $ServiceName "Running" $SecondsToWait

    If ($ServiceStateCorrect) {
        Return $True
    } else {
        Return $false
    }
}

An similar function is Stop-WindowsService:

Function Stop-WindowsService {
	Param(
        [string]$ServiceName,
        [int]$SecondsToWait = 30
    )

    Stop-Service $ServiceName -Force -ErrorAction SilentlyContinue
    $ServiceStateCorrect = Wait-ServiceState $ServiceName "Stopped" $SecondsToWait

    If ($ServiceStateCorrect) {
        Return $True
    } else {
        Return $false
    }
}

If you’ve already tried to copy and paste the code above and run it, you might have found out, I have a 3rd function. This is the Wait-ServiceState function.

Function Wait-ServiceState {
    param (
        [string]$ServiceName,
        [ValidateSet("Running", "StartPending", "Stopped", "StopPending", "ContinuePending", "Paused", "PausePending")][string]$ServiceState,
        [int]$SecondsToWait = 30
    )
    $counter = 0
    $ServiceStateCorrect = $False
    do {
        $counter++
        Start-Sleep -Milliseconds 250
        $serviceInfo = Get-Service $ServiceName
        If ($serviceInfo.Status -eq $ServiceState) {
            $ServiceStateCorrect = $True
            Break
        } else {
            if (($Counter * .250) % 1 -eq 0) {
                Write-Host "$($counter * .250)/$SecondsToWait [$($ServiceName)] State is not $ServiceState"
            }
        }
    } until (($counter * .250) -ge $SecondsToWait)
    Return $ServiceStateCorrect
}

Now instead of creating a function to Restart-Service, I opt to have a few more lines of code. There is usually a reason I want to stop the service, do something such as change the configuration and then start it up. So in my case I would usually have PowerShell code that looks like:

$result = Stop-WindowsService MyCustomService
if (!$result) {
    Stop-Process -Name MyCustomService -Force -Confirm:$false
}
$result = Reconfigure-MyCustomApplication
$result = Start-WindowsService MyCustomService

Hopefully this code has given you a head start in your next script project or given you a few things to think about!

Until next time! As always, please let me know if there is anything you’d like to expand on or blog about!

Advertisements