# encoding: ascii
# api: powershell
# title: Stop-ServiceandProcess
# description: Stop service and associated Pid. Sometimes homegrown win32 services has its Pid still terminating gracefully in the background for quite a while. This function allows to wait indefinitely, or after some time, kill the associated Pid.
# version: 0.1
# type: function
# author: Daniel Cheng
# license: CC0
# x-poshcode-id: 5355
# x-archived: 2014-08-12T22:19:11
# x-published: 2014-08-04T21:59:00
#
#
Function Stop-ServiceandProcess
# daniel.cheng@thomsonreuters.com
# Function: Stop service and associated Pid. Sometimes homegrown services has its Pid still terminating gracefully in the background.
{
param (
[string[]]$servers = 'localhost',
[parameter(Mandatory)][string[]]$services,
[int]$InterrogateServiceDelay = 1, # seconds
[int]$killDelay = $null
)
$serviceState = @{
00 = 'The request was accepted.';
01 = 'The request is not supported.';
02 = 'The user did not have the necessary access.';
03 = 'The service cannot be stopped because other services that are running are dependent on it.';
04 = 'The requested control code is not valid, or it is unacceptable to the service.';
05 = 'The requested control code cannot be sent to the service because the state of the service (Win32_BaseService State property) is equal to 0, 1, or 2.';
06 = 'The service has not been started.';
07 = 'The service did not respond to the start request in a timely fashion.';
08 = 'Unknown failure when starting the service.';
09 = 'The directory path to the service executable file was not found.';
10 = 'The service is already running.';
11 = 'The database to add a new service is locked.';
12 = 'A dependency this service relies on has been removed from the system.';
13 = 'The service failed to find the service needed from a dependent service.';
14 = 'The service has been disabled from the system.';
15 = 'The service does not have the correct authentication to run on the system.';
16 = 'This service is being removed from the system.';
17 = 'The service has no execution thread.';
18 = 'The service has circular dependencies when it starts.';
19 = 'A service is running under the same name.';
20 = 'The service name has invalid characters.';
21 = 'Invalid parameters have been passed to the service.';
22 = 'The account under which this service runs is either invalid or lacks the permissions to run the service.';
23 = 'The service exists in the database of services available from the system.';
24 = 'The service is currently paused in the system.'
}
foreach ($server in $servers) {foreach ($service in $services) {
$currentService = gwmi -ComputerName $server -Class win32_service -Filter "name='$($service)'" -ea SilentlyContinue
if ($currentService -isnot [object]) {
throw "Error with creating object for $($server):$('$service')!"
} else {
switch ($currentService) {
{
$_.StartMode -ne 'Disabled' -and `
$_.State -ne 'Stopped' -and `
$_.ProcessId -ne 0
} {
Write-Output "$(Get-Date -Format s): $($env:computername): Service ('$($service)', Pid=$($currentService.ProcessId)) stop command sent to $($server)."
Write-Output "$(Get-Date -Format s): $($server): StopService('$($service)', Pid=$($currentService.ProcessId)): $($serviceState.$([int]$currentService.StopService().ReturnValue))"
$timeElapsed = [System.Diagnostics.Stopwatch]::StartNew()
do
{
switch ($exitCode = $([int]$currentService.InterrogateService().ReturnValue)){
4 {Write-Host -NoNewline '.'}
default {Write-Output "$(Get-Date -Format s): $($server): InterrogateService('$($service)', Pid=$($currentService.ProcessId)): (exit code=$($exitCode)) $($serviceState.$exitCode)"}
}
if ($killDelay -ne 0 -and $timeElapsed.Elapsed.Seconds -ge $killDelay) {
Write-Output "$(Get-Date -Format s): $($env:computername): Process ('$($service)', Pid=$($currentService.ProcessId)) kill command sent to $($server)."
Stop-Process -Id $currentService.ProcessId -Force
}
Start-Sleep -Seconds $InterrogateServiceDelay
} until (
[bool]!(Get-Process -ComputerName $server -Id $currentService.ProcessId -ea SilentlyContinue).Id
)
Write-Output "Operation took $($timeElapsed.Elapsed.Seconds) seconds."
$timeElapsed.Stop()
}
default {Write-Output "No stop operation necessary (stopped already) or possible (disabled)."}
}
gwmi -ComputerName $server -Class win32_service -Filter "name='$($service)'" | select PSComputerName, Name, ServiceType, StartMode, State, ProcessId, PathName | ft -AutoSize
}
}}
}
Stop-ServiceandProcess -servers dc -services 'custom service 1', 'custom service 2' -killDelay 30