Question:
How do I capture the output when Invoke-WebRequest hits a 500, 401, 403, etc.? When the call is successful, it stores the result in the $Response
variable, but if I hit a 401 it throws the error but doesn’t store the response.
1 2 3 4 5 6 7 8 |
$Response = Invoke-WebRequest -Method HEAD -Uri 'https://site.contoso.com' Invoke-WebRequest : The remote server returned an error: (401) Unauthorized. At line:1 char:13 + $Response = Invoke-WebRequest -Method HEAD -Uri 'https://site.contoso.com' -Erro ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand |
The $Response
variable remains null, however the endpoint is returning the 401 Unauthorized and headers, it’s just not being captured by PowerShell since it is treating it as an exception. Piping it through Fiddler confirms this.
Creating a request using [System.Net.WebRequest]
has the same result when I execute $Response = $WebRequest.GetResponse()
.
Here’s my $PSVersionTable
.
1 2 3 4 5 6 7 8 9 10 |
Name Value ---- ----- PSVersion 4.0 WSManStackVersion 3.0 SerializationVersion 1.1.0.1 CLRVersion 4.0.30319.36373 BuildVersion 6.3.9600.16406 PSCompatibleVersions {1.0, 2.0, 3.0, 4.0} PSRemotingProtocolVersion 2.2 |
Answer:
This is actually an open issue on the PowerShell project on GitHub, it might be resolved some day though.
For now, here is how we tend to work around that.
First, load this function into memory.
1 2 3 4 5 6 7 8 9 10 11 12 |
function Failure { $global:helpme = $body $global:helpmoref = $moref $global:result = $_.Exception.Response.GetResponseStream() $global:reader = New-Object System.IO.StreamReader($global:result) $global:responseBody = $global:reader.ReadToEnd(); Write-Host -BackgroundColor:Black -ForegroundColor:Red "Status: A system exception was caught." Write-Host -BackgroundColor:Black -ForegroundColor:Red $global:responsebody Write-Host -BackgroundColor:Black -ForegroundColor:Red "The request body has been saved to `$global:helpme" break } |
Then, wrap your WebRequests like this.
1 2 3 4 5 6 7 8 9 |
try { $Response = Invoke-WebRequest -Method HEAD -Uri 'https://site.contoso.com' } catch { Failure } |
The cool thing about this approach is that it writes Globally Scoped Variables, and they’ll persist after your function exits. When you do encounter an error, just poke around inside $global:result
or $global:responsebody
for the full error.
This is a must-have when writing modules based on RPC or REST endpoints.
Credit for this technique goes to Steve Wahl.