4

In powershell, I've seen multiple ways to stop a service

The more modern way

Stop-Service wuauserv

And the more legacy way

NET STOP WUAUSERV

The legacy way is much more difficult to automate because it is not natively idempotent.

I have powershell scripts that builds a windows golden images using packer. Initially the scripts used NET STOP. I found once I switched to Stop-Service, I seemed to get more frequent failures when rebooting a VM after installing windows updates.

Do both Stop-Service and NET STOP produce the same result? Or are there differences between them that might explain why the legacy one seems more reliable?

spuder
  • 17,437
  • 19
  • 87
  • 153
  • 1
    I would consider them the be interchangeable. The main thing I'd advise is to code some logic around the call, to verify that the service has actually stopped. If the stop request fails, you could re-try a couple of times and eventually kill the process if necessary. – andyb Nov 21 '17 at 04:32
  • 1
    The "more legacy" these days would be "sc stop" instead of "net stop", but technically if you're in Powershell, use cmdlets where possible. Some things can't be resolved in PS however, but then you can normally parse output to get info of what changed. – Vesper Nov 21 '17 at 06:30

2 Answers2

6

For a Windows service that is:

  • currently running
  • and stoppable in principle

both net stop and Stop-Service should act the same, namely synchronously:

That is, they send the specified service a stop request and wait for stopping to complete (net stop invariably waits, while, in PSv5+, you can opt out of waiting with Stop-Service's -NoWait switch).

Unlike net stop (which reports an error if the service is already stopped), Stop-Service is idempotent (exhibits desired-state logic): If the target service is already in the stopped state, the command is a quiet no-op.

(As an aside: Start-Service is also synchronous, but invariably so, and is also idempotent.)


Set-Service -Status Stopped should act the same as Stop-Service, except that:

  • unlike Stop-Service, it doesn't support -Force in order to stop a service with running dependents (other services that depend on the service being stopped).

  • due to what I presume to be a bug you cannot even stop services that themselves depend on other services(!).

  • in effect, as of Windows PowerShell v5.1 / PowerShell Core v6.0-rc, you can only stop services with Set-Service -Status Stopped that have no dependents (no services that depend on them), nor themselves depend on other services.


Optional reading: looking at the Stop-Service and Start-Service source code:

The publicly available source code on GitHub is for the cross-platform Core edition of PowerShell, but it looks like the code in question was essentially taken unmodified from the Windows PowerShell version.


[1] If reaching the target state takes longer than 2 seconds, the waiting loop issues a warning (every 2 seconds) while continuing to wait; waiting is only aborted with an error if the service is unexpectedly neither in the target-state-pending state nor in the target state.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Err, double check the `DoWaitForStatus` implementation. Despite the comment, it waits on the service state to reach target for 2 seconds. After the timeout exception is thrown, it will write a warning if the service is in either the target or the pending state. The version of the API without the timeout argument will wait infinitely, but I suspect the comment wasn't update after the timeout was added. (and thanks for looking up the code. I used to manage the developer who left his name in the code. :) ) – Matthew Wetmore Nov 22 '17 at 02:29
  • 1
    Ah, there we go - and the special case is coming back to me. I think I'm remembering a service that didn't reach *pending* in 2 seconds, and therefore failed. My body (and apparently mind) is on vacation, so thanks for checking my math. The overall summary for the function is correct. Nicely done (+1). – Matthew Wetmore Nov 22 '17 at 05:55
3

The difference is waiting until the service is actually stopped. net stop service waits until service gets stopped, or at least sends the event that it's now "stopped". The "other legacy" way of sc stop service exits at once after sending stop signal, and dumps current service state which is normally STOP_PENDING. Stop-Service cmdlet does wait for service to stop, but there might be corner cases with services that are stopping for too long and the cmdlet bails off, or it had a -nowait switch in there. Also some services are restarted if needed, so a further check might be required, like this (in case a service didn't stop):

Stop-Service $servicename
$sleep=0
$s="Running"
do {
   $sleep++
   start-sleep 1
   $s=(get-service $servicename).status
} while (($s -ne "Stopped") -and ($sleep -le 20))
Vesper
  • 18,599
  • 6
  • 39
  • 61