- Get and set IP address with PowerShell - Wed, Oct 18 2023
- If a Windows service hangs, restart the service with PowerShell - Mon, Dec 12 2022
- Install, remove, list, and set default printer with PowerShell - Mon, Nov 7 2022
Recently, I was checking the state of update deployment when I noticed a computer had not reported in a long time. It was an RDS server to which many users were connected. Attempting to stop the Windows Update service from the Services console (services.msc) would cause the service to hang for a while. Eventually, it threw a warning message that the service had failed to stop.
Right-clicking a service in the Services console and selecting Stop is followed by a pop-up window if the service doesn't stop immediately. With a hanging service, this progress bar will persist for a while, but eventually, it will fail with the error message Windows could not stop the service.
You can close the window, but if you refresh the status of the service, it will simply display Stopping forever.
At this point, a quick solution would be to simply restart the server, which usually eliminates the issue. However, there are situations when this is not desirable or even possible. I couldn't just disconnect users in the middle of their work because Windows Update was misbehaving.
Fortunately, there is a workaround that usually solves the problem.
Stop a hanging service without restarting
First, this isn't a guarantee that it will work in 100% of the cases. Nothing is ever 100% in Windows or any other OS. However, it will work most of the time. With the disclaimer out of the way, let us see how.
A service is a process that runs in the background. If it is a process, you can find it in Task Manager (or with the Get-Processing PowerShell cmdlet), and then kill it.
Services almost always run as an instance of the svchost.exe process—the Windows service host process. If you check Task Manager (or Get-Process) for svchost.exe, you'll see many instances of it running. Which is the one corresponding to your hanging service?
Well, the answer is not available in Task Manager, but it is easy to find in the WMI repository using PowerShell:
(Get-CimInstance -ComputerName "COMPUTER_NAME" -ClassName Win32_Service -Filter "name = 'SERVICE_NAME'")
In my case, the service is Windows Update (wuauserv). The output of the command is shown below:
Bingo—we have the process ID. The only thing to do now is stop the process by using—you guessed it—PowerShell.
$Process = Get-CimInstance -ClassName Win32_Service -filter "name = 'wuauserv'" Stop-Process -Id $Process.ProcessId
In this case, I omitted the computer name since I ran the command locally, but in the next section, I will show you how to make this work easily for one or more remote computers.
Now, the service is stopped and can be restarted using the Services console or PowerShell:
Start-Service wuauserv
After this, the service should be running.
If, however, the service is still misbehaving, you may need to investigate further (using Procmon.exe, for instance), or maybe restarting the computer when possible.
You can also use any tool that allows you to explore the deep labyrinths of WMI. However, PowerShell is easy, already available, and can be used to automate. This will help us in the next section.
Restart a Windows service with a PowerShell script
Of course, avoiding a restart and interrupting your colleague's work is nice, but it is even nicer if you can automate, for instance, if you have many computers that have the same issue with a service.
Below, I include a short PowerShell function I wrote that allows you to accomplish exactly that: restart one or more services on one or more computers. As always, you should test before running a script in production.
Subscribe to 4sysops newsletter!
function Stop-HangingService { <# .Synopsis Stops a hanging service .DESCRIPTION Stops a single service on a single computer if the service status is Starting or Stopping. These are transient states that a service should not be in for more than a few seconds, and they are a good indicator that a service may be hanging. If the service is in a running or stopped state, you are informed and no further action is taken .PARAMETER NAME One or more services. The service name(s) is needed, not the Display Name(s) .PARAMETER COMPUTERNAME One or more computers on which the hanging service(s) should be stopped .EXAMPLE Stop-HangingService -Name BITS Stops the service BITS (if it is in a hanging state) on the local computer .EXAMPLE Stop-HangingService -Name Wuauserv -ComputerName Srv1,Srv2 -Verbose Stops the hanging Windows Update service on the computers Srv1 and Srv2 .EXAMPLE (Get-ADComputer -SearchBase $SqlServersOu -filter *).Name | Stop-HangingService -Name SQLAgent Stops the hanging service SQLAgent on all the computers in the specified OU #> [CmdletBinding()] Param( [Parameter(Mandatory,Position = 0,HelpMessage = "Name of the service you're trying to stop")][string[]]$Name, [Parameter(ValueFromPipeline,Position = 1)][string[]]$ComputerName = $env:COMPUTERNAME ) Begin { Write-Verbose "$(Get-Date -Format HH:mm:ss) BEGIN : $($MyInvocation.MyCommand)" $StartTime = Get-Date #region Helper function, stops a single service on a single computer function Stop-SingleService { <# .Synopsis This is a helper function that stops a single service on a single computer. It is a helper function for Stop-HangingService #> [CmdletBinding()] Param( [Parameter(Mandatory,HelpMessage = "Name of the service you're trying to stop")][string]$Name, [Parameter()][string]$ComputerName = $env:COMPUTERNAME ) try { Write-Verbose "$(Get-Date -Format HH:mm:ss) PROCESS: Retrieve status of service $Name on $($ComputerName.ToUpper())" $Service = Get-Service -Name $Name -ComputerName $ComputerName -ErrorAction Stop } #try catch { Write-Verbose "$(Get-Date -Format HH:mm:ss) PROCESS: The service $Name could not be found on $($ComputerName.ToUpper())" return } #catch if ($Service.Status -like "*Stopping*" -or $Service.Status -like "*Starting*") { Write-Verbose "$(Get-Date -Format HH:mm:ss) PROCESS: Get Process ID for service $Name" $Process = (Get-CimInstance -ComputerName $ComputerName -ClassName Win32_Service -filter "name = '$Name'") Write-Verbose "$(Get-Date -Format HH:mm:ss) PROCESS: Stopping process ID $($Process.ProcessId) for service $Name on $($ComputerName.ToUpper())" Invoke-Command -ComputerName $ComputerName -ScriptBlock {Stop-Process -Id $using:Process.ProcessId -Force} } else { Write-Verbose "$(Get-Date -Format HH:mm:ss) PROCESS: The service $Name is not hanging on $($ComputerName.ToUpper())" } } #endregion Helper function } #Begin Process { foreach ($c in $ComputerName) { foreach ($n in $Name) { Stop-SingleService -ComputerName $c -Name $n } } } #Process End { $EndTime = Get-Date Write-Verbose "$(Get-Date -Format HH:mm:ss) FINISH : $($MyInvocation.MyCommand). The operation completed in $(New-TimeSpan -Start $StartTime -End $EndTime )" } } #function Stop-HangingService
Conclusion
In this post, you learned how you can (often) avoid restarting a computer because a service hangs. You also received a PowerShell script that allows you to restart a service on multiple computers.
Hi Emanuel
Just one correction in your command for stopping the process mentioned above
Stop-Process -Id $Process.ProcessId.
Rgds
Thanks for the heads-up, Anand.
Cheers! Emanuel
thanks alot of information keren banget