3

How do you make powershell ignore failures on legacy commands?

All powershell modules support -ErrorAction, however that does not work with legacy commands like NET STOP

NET STOP WUAUSERV
echo $?
true

I want to attempt to stop the service, and if the service is already stopped, continue. An easy way to reproduce this is to try and stop the service twice.

Things I've tried

$ErrorActionPreference = "SilentlyContinue"
NET STOP WUAUSERV
NET STOP WUAUSERV
echo $LASTEXITCODE

Tried using AND and OR operrators

and

NET STOP WUAUSERV || $true
NET STOP WUAUSERV || $true
echo $LASTEXITCODE

or

NET STOP WUAUSERV && $true
NET STOP WUAUSERV && $true
echo $LASTEXITCODE

I've also tried redirecting errors

NET STOP WUAUSERV 2>nul
NET STOP WUAUSERV 2>nul
echo $LASTEXITCODE

As recommended in a similar question, I've also tried

cmd.exe /C "NET STOP WUAUSERV"

How can I make this legacy command idempotent?

spuder
  • 17,437
  • 19
  • 87
  • 153

3 Answers3

2

For many common tasks, the right answer is to use the native PowerShell commands that already exist.

For this particular example, the correct path is to use Stop-Service cmdlet, which will handle your conditions gracefully.

If you must use a legacy tool, see this StackOverflow answer: Powershell: Capture program stdout and stderr to separate variables

The .exe's don't put a traditional value on the PowerShell output pipeline for you to evaluate, so trying to compare $true does nothing useful. See also: Windows PowerShell Rethinking the Pipeline

Additionally, $ErrorActionPreference will have no effect on a legacy .EXE. (And -ErrorAction is specific to PowerShell commands.)

Matthew Wetmore
  • 958
  • 8
  • 18
  • 1
    Good idea to recommend PowerShell cmdlets. Re "don't put a traditional value on the PowerShell output pipeline for you to evaluate": console applications send their _stdout_ output through the pipeline. `||` is currently not supported at all in PowerShell (generates a syntax error); [should it get implemented](https://github.com/PowerShell/PowerShell/issues/3241), it certainly _will_ be expected to to act on the exit code of console applications (as well as on the success status of PowerShell-native commands). `-ErrorAction` cannot even be _applied_ to invocations of executables. – mklement0 Nov 21 '17 at 03:59
  • There is some slight difference between the way Stop-Service works that is giving me issues which I've explained in another question https://stackoverflow.com/questions/47405100/whats-the-difference-between-powershells-stop-service-and-net-stop – spuder Nov 21 '17 at 04:22
  • 1
    @mklement0, yup. I was debating whether to go into the topic of the stdout results in the output, and the syntactical challenges of the question, but left it off. The few things I had added late may have been confusing, so I tweaked the answer a bit to your points. – Matthew Wetmore Nov 21 '17 at 22:04
  • @spuder I have to wonder why you're stopping the updating service `wuauserv` while you're trying to update... I commented on the other post that you may want to ensure the service is entirely stopped, even when using `Stop-Service` if you truly need it stopped... Again, I'll caution you to seriously consider if stopping that service is really what you want to do. – Matthew Wetmore Nov 21 '17 at 22:20
  • @MatthewWetmore This script is used in automation to build golden images using packer. If windows update service is running, it can prematurely reboot the machine before packer has finished. http://blog.powerbiz.net.au/fixes/stop-windows-server-2012-and-windows-8-from-automatically-rebooting-the-server-after-logging-in/ – spuder Nov 21 '17 at 23:28
2
  • To silence error / stderr output in PowerShell, use 2> $null, not 2> NUL.

    • The value of $ErrorActionPreference should not have any effect on calling external utilities (console applications) such as net.exe, but - due to a bug as of Windows PowerShell v5.1 / PowerShell Core v6.0.0-beta.9 - if the value is 'Stop', 2> $null (indeed, any redirection of stream 2, the error stream) currently triggers a PowerShell error.
  • Unfortunately, control operators && and || are not supported in PowerShell as of Windows PowerShell v5.1 / PowerShell Core v6.0.0-beta.9; even if they were, however, you wouldn't need them here (and you wouldn't use them with $true or $false).


Irrespective of whether stderr output is silenced or not:

  • $LASTEXITCODE contains the exit code of the most recently executed external utility.

  • Immediately after executing an external utility, $? is $True if $LASTEXITCODE is 0, and $False otherwise.

For a comprehensive discussion of PowerShell's error handling - with respect to both PowerShell-native commands and external utilities - see here.

mklement0
  • 382,024
  • 64
  • 607
  • 775
0

My other answer discusses dealing with stderr output / exit codes from external executables generally.

As for an idempotent way to stop a service (ensuring that a service is in the stopped state):

  • Matthew Wetmore's helpful answer suggests using PowerShell's Stop-Service cmdlet, which should be idempotent: it either stops the service, if it is running, or is a no-op if it already is in the stopped state.

    • Stopping is synchronous, unless you use the -NoWait switch (PSv5+), as shown in this answer of mine.
  • If, as you suggest here, there is indeed a problem with Stop-Service (I'm not aware of any issues), you can supplement use of net.exe with the Get-Service cmdlet to ensure that a service is stopped, while still detecting error cases:

$svc = 'wuauserv'
if ((Get-Service $svc).Status -ne 'Stopped') {
  net stop $svc
  if ($LASTEXITCODE) { Throw "Failed to stop service $svc."}
} else {
  Write-Verbose -Verbose "(Service $svc is already stopped.)"
}
mklement0
  • 382,024
  • 64
  • 607
  • 775