Invoke-WebServiceProxy and Ignore Errors

on Monday, February 11, 2019

One of the frustrating parts of using Invoke-WebRequest, Invoke-Rest, and Invoke-WebServiceProxy is that when they “throw” errors, they don’t actually throw. Many of the original powershell functions don’t throw errors, instead they use Write-Error and and return control from the function. This is really strange functionality for anyone coming for C#, javascript, or other 3GL languages.

You can suppress the error message by using the [CmdletBinding] parameter –ErrorAction “SilentlyContinue”. However, there are two problems with this. When using ‘SilentlyContinue’, the error message is still written to the $global:Error collection. And, these functions don’t always implement the functionality the same way. For example, Invoke-WebRequest doesn’t care what the error action is, it’s still going to write the error to the screen and it’s going to update the $global:Error collection.

The output from these two examples is kind of hard to see, because the Write-Host from within the finally block writes to the screen before the error message from Invoke-WebRequest. But, when $global:Error.Clear() is run within the finally block it somehow only affects a scoped instance of $global:Error. Which is completely counterintuitive to the idea of ‘$global’.

Test-ClearInsideOfFinallyBlock.ps1:

$global:Error.Clear()
Write-Host "Test-ClearInsideOfFinallyBlock - Start - `$global:Error.Count = $($global:Error.Count)"
try {
Invoke-WebRequest -UseBasicParsing -Uri "https://doesntexist.com" -ErrorAction Ignore
} finally {
Write-Host "Test-ClearInsideOfFinallyBlock - After Invoke - `$global:Error.Count = $($global:Error.Count)"
$global:Error.Clear()
Write-Host "Test-ClearInsideOfFinallyBlock - After Invoke / End of Finally Block - `$global:Error.Count = $($global:Error.Count)"
}
Write-Host "Test-ClearInsideOfFinallyBlock - End - `$global:Error.Count = $($global:Error.Count)"
<#
Output:
Test-ClearInsideOfFinallyBlock - Start - $global:Error.Count = 0
Test-ClearInsideOfFinallyBlock - After Invoke - $global:Error.Count = 1
Test-ClearInsideOfFinallyBlock - After Invoke / End of Finally Block - $global:Error.Count = 0
Invoke-WebRequest : Unable to connect to the remote server
At D:\Projects\Main\Powershell\Ucsb.Sa.PowerShell\m1\Test-ClearInsideOfFinallyBlock.ps1:4 char:5
+ Invoke-WebRequest -UseBasicParsing -Uri "https://doesntexist.com" ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
Test-ClearInsideOfFinallyBlock - End - $global:Error.Count = 1
#>

Test-ClearOutsideOfFinallyBlock.ps1:

$global:Error.Clear()
Write-Host "Test-ClearOutsideOfFinallyBlock - Start - `$global:Error.Count = $($global:Error.Count)"
try {
Invoke-WebRequest -UseBasicParsing -Uri "https://doesntexist.com" -ErrorAction Ignore
} finally {
}
Write-Host "Test-ClearOutsideOfFinallyBlock - After Invoke - `$global:Error.Count = $($global:Error.Count)"
$global:Error.Clear()
Write-Host "Test-ClearOutsideOfFinallyBlock - End - `$global:Error.Count = $($global:Error.Count)"
<#
Output:
Test-InsideOfFinallyBlock - Start - $global:Error.Count = 0
Test-InsideOfFinallyBlock - After Invoke - $global:Error.Count = 1
Invoke-WebRequest : Unable to connect to the remote server
At D:\Projects\Main\Powershell\Ucsb.Sa.PowerShell\m1\Test-InsideOfFinallyBlock.ps1:4 char:5
+ Invoke-WebRequest -UseBasicParsing -Uri "https://doesntexist.com" ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
Test-InsideOfFinallyBlock - End - $global:Error.Count = 1
#>

But, the way Invoke-WebServiceProxy was written, it does respect the –ErrorAction parameter. And it was seemingly designed to work like this:

  • -ErrorAction Continue

    Writes an error message to screen. Updates $global:Error. And returns nothing.
  • -ErrorAction SilentlyIgnore

    Does not write an error message to screen. Updates $global:Error. And returns nothing.
  • -ErrorAction Ignore

    Does not write an error message to screen. Does not update $global:Error. And returns nothing.

This implementation makes sense when you understand it. But, because Invoke-WebRequest and Invoke-WebService behave differently you would never know it.

    0 comments:

    Post a Comment


    Creative Commons License
    This site uses Alex Gorbatchev's SyntaxHighlighter, and hosted by herdingcode.com's Jon Galloway.