ps/Modules/Alkami.PowerShell.Services/Public/Stop-AlkamiService.ps1
2023-05-30 22:51:22 -07:00

113 lines
4.4 KiB
PowerShell

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
}
}