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.
$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
.
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
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.
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.
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.
I found that you can capture the Response within a try/catch block.
try
{
$Response = Invoke-WebRequest -Method HEAD -Uri 'https://site.contoso.com'
}
catch
{
$Failure = $_.Exception.Response
}
However, this only works if you trap it and look at the Response at the time of running it. You cannot retrieve the Response data from the $Error variable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With