How to repeatedly start a service that failed to stop - Powershell

I have previously written a post about stopping and starting a service in Powershell. I have found an issue however, if the service fails to stop in time.

If you attempt to stop a service that is stuck (or waiting to finish a large task), then you may get the following error:

Windows could not stop the [Service name] service on Local Computer.
Error 1053: The service did not respond to the start or control request in a timely fashion.

The same error occurs when attempting to stop a busy service using the Services window:

unable to stop service

Detect if the script has stopped

To get around this, we should detect if the service has stopped successfully:

    $serviceName = 'myService'

    Stop-Service $serviceName

    $arrService = Get-Service -Name $serviceName

    while ($arrService.Status -ne 'Running')
    {
        # Attempt to restart service
    }

Restart the service

When starting the service again, we should wait for a short period before checking if it started successfully. The service may be in a starting state for a while:

    Start-Service $serviceName
    Write-Host $serviceName $arrService.status
    Write-Host $serviceName 'Starting'

    # Wait a short period before checking the service
    Start-Sleep -seconds 5
    
    $arrService.Refresh()

    if ($arrService.Status -eq 'Running')
    {
        Write-Host $serviceName $arrService.status
    }

If the service hasn't started successfully, we should increase the wait time and try again:

    Start-Service $serviceName
    Write-Host $serviceName $arrService.status
    Write-Host $serviceName 'Starting'

    # Wait a short period before checking the service
    Start-Sleep -seconds 5
    
    $arrService.Refresh()

    if ($arrService.Status -eq 'Running')
    {
        Write-Host $serviceName $arrService.status
    }
    else
    {
        Write-Host 'Failed to start the service'

        # Wait a longer period before starting the service again
        Start-Sleep -seconds 55
    }

Wrapping it up into a function

Putting this into a function, and adding an attempt limit so we don't try indefinitely, we get:

Function RestartService
{
    param
    (
        [Parameter(Mandatory=$true)][string]$serviceName, # A service name is required
        [int]$initialWaitTime = '5', # Wait 5 seconds by default on the first attempt
        [int]$recurringWaitTime = '60', # Wait 60 seconds by default on each subsequent attempt
        [int]$maxAttempts = '10' # Make 10 attempts maximum
    )

    Write-Host $serviceName 'Stopping'
    Stop-Service $serviceName

    $arrService = Get-Service -Name $serviceName
    $attempt = 1

    while ($arrService.Status -ne 'Running' -and $attempt -le $maxAttempts)
    {
        Start-Service $serviceName
        Write-Host $serviceName $arrService.status
        Write-Host $serviceName 'Starting'

        # Wait a short period before checking the service
        Start-Sleep -seconds $initialWaitTime
        
        $arrService.Refresh()

        if ($arrService.Status -eq 'Running')
        {
            Write-Host $serviceName $arrService.status
        }
        else
        {
            Write-Host '(Attempt' $attempt') Failed to start' $serviceName '- Trying again in' $recurringWaitTime 'seconds.'
            $initialWaitTime = $recurringWaitTime
            $attempt++
        }
    }
}

This can then be called by passing the service name:

# With default parameters
RestartService 'MyService'

# With parameters set
RestartService 'MyService' -initialWaitTime 5 -recurringWaitTime 55 -maxAttempts 5

And hopefully, if it runs first time, you'll get something like this:

MyService Stopping
MyService Stopped
MyService Starting
MyService Running