Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Capture response on failed/401 Invoke-WebRequest

Tags:

powershell

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   
like image 893
Taylor Avatar asked Feb 14 '17 16:02

Taylor


2 Answers

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.

like image 90
FoxDeploy Avatar answered Sep 21 '22 21:09

FoxDeploy


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.

like image 38
Taylor Avatar answered Sep 22 '22 21:09

Taylor