function Stop-AlkamiService { <# .SYNOPSIS Attempts to stop a service and kills the process if it doesn't complete shutdown within a configurable limit .PARAMETER ServiceName Name of the service to stop .PARAMETER ServiceTimeoutSeconds Number of seconds to wait for the service to stop #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] [OutputType([System.Boolean])] Param( [Parameter(Mandatory = $true)] [Alias("sName")] [string]$ServiceName, [Parameter(Mandatory = $false)] [Alias("Seconds")] [int]$ServiceTimeoutSeconds = 15 ) $logLead = (Get-LogLeadName) $serviceTimeout = [System.TimeSpan]::FromSeconds($ServiceTimeoutSeconds) try { $service = Get-Service -Name $ServiceName | Select-Object -First 1 } catch { Write-Warning "$logLead : $_" } if ($null -eq $service -or $service.Status -eq [ServiceProcess.ServiceControllerStatus]::Stopped) { Write-Host ("$logLead : Service {0} not found or was already stopped" -f $ServiceName) return $true } try { $processes = @(Get-ProcessFromService $service) if ($processes.Count -eq 0) { Write-Host "$logLead : No running processes found for $($service.Name)" return $true } $process = ($processes | Select-Object -First 1) Write-Host "$logLead : Stopping PID: $($process.Id) for [$($process.ProcessName)] at [$($process.path)]" # Warn about having too many processes $otherProcesses = ($processes | Select-Object -Skip 1) if ($otherProcesses.Count -gt 0) { Write-Warning "$logLead : Found multiple processes for this service information. Please investigate." Write-Host "$logLead : Only processing for the first process returned in the list" foreach ($extraProcess in $otherProcesses) { Write-Warning "$logLead : NOT STOPPING: PID: $($process.Id) for [$($process.ProcessName)] at [$($process.path)]" } } } catch { Write-Warning ("$logLead : {0}" -f ($Error[0] | Format-List -Force)) } $service.Refresh() if ($PSCmdlet.ShouldProcess($ServiceName, "Stopping Service")) { if ($service.Status -ne "StopPending" -and $service.CanStop -eq $true) { try { Invoke-SCExe @("stop", $serviceName) } catch { Write-Warning ("$logLead : {0}" -f ($Error[0] | Format-List -Force)) } } } try { if ($PSCmdlet.ShouldProcess($ServiceName, "Wait for Service Stop")) { $service.WaitForStatus([ServiceProcess.ServiceControllerStatus]::Stopped, $serviceTimeout) $waitInterval = 0 do { Start-Sleep -Seconds 1 $waitInterval++ } while ($null -ne (Get-Process -Id $process.Id -ErrorAction SilentlyContinue) -and ($waitInterval -lt $serviceTimeout.Seconds)) # If the timeout expires, the catch block will handle killing the service # If the service stops gracefully but the process doesn't quit, we'll kill it here if ($null -ne (Get-Process -Id $process.Id -ErrorAction SilentlyContinue)) { try { Write-Warning ("$logLead : Old process still running after timeout. Killing process with ID {0}" -f $process.Id) Stop-ProcessIfFound $process.Name } catch { # Do nothing, the process terminated before we could kill it Write-Host "$logLead : The process terminated before we could kill it." } } } } catch [ServiceProcess.TimeoutException] { if ($null -ne $process.Id -or $process.Count -gt 0) { Write-Warning ("$logLead : Timeout stopping service {0} after {1} seconds. The process will be killed." -f $ServiceName, $serviceTimeout.Seconds) Stop-ProcessIfFound $process.Name } else { Write-Warning ("$logLead : The process ID could not be determined before shutdown.") } } # Check one more time if the service is stopped. #$service = Get-Service -Name $ServiceName | Select-Object -First 1 $service.Refresh() $stopped = $service.Status -eq "Stopped" if ($PSCmdlet.ShouldProcess($ServiceName, "Determining Return Value")) { return $stopped } else { return $true } }