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