Can't find expected json body in System.Net.WebException - rest

I'm working with an API via PowerShell that returns human-readable errors as a json object in the response body when an error occurs. However, when I attempt to find that json body in an exception, I can see the error, the underlying System.Net.WebException and the further underlying System.Net.HttpWebResponse, but nowhere can I find the actual body they're referring to. Is this something that is accessible?
For example, here is a valid API call that would work:
Invoke-RestMethod -Method Get -Headers #{Authorization="Token token=$YourTokenHere";"Content-type"="application/json"} -Uri "https://mydomain.pagerduty.com/api/v1/users/ABCDEF" -Body #{offset=0;limit=100}
If you then change the user ID at the end of the URI, it fails and you get this error:
Invoke-RestMethod : The remote server returned an error: (400) Bad Request.
At line:1 char:1
+ Invoke-RestMethod -Method Get -Headers #{Authorization="Token token=b ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
I can dive down in to the error to see the underlying error and the response if I do the following and convert to json for easy viewing of subproperties:
$Error[0].Exception.Response | ConvertTo-Json
But no matter how I comb through these errors, I can't seem to find the json body. Where might it be, or how can I capture it? I seem to have the same result if I do a try/catch.

You could read the responsestream so you can get the body of the response. Ex:
try {
Invoke-RestMethod -Method Get -Headers #{Authorization="Token token=$YourTokenHere";"Content-type"="application/json"} -Uri "https://mydomain.pagerduty.com/api/v1/users/ABCDEF" -Body #{offset=0;limit=100}
} catch {
$stream = New-Object System.IO.StreamReader $_.Exception.Response.GetResponseStream()
$json = $stream.ReadToEnd()
$stream.Dispose()
$json
}
Output:
{"error":{"message":"Account Not Found","code":2007}}

Related

Powershell curl calls fails on the second request

I am creating a script to call a couple of endpoints in my service.
The calls look like this:
$scheduleResponse=(curl -Method 'POST' -Uri $baseUrl'/scheduletests' `
-Headers #{"Accept"="application/json";"Content-Type"="application/json"} `
-Body $body)
$scheduleContent=ConvertFrom-Json([String]::new($scheduleResponse.Content))
#Error checks here
$testId = [String]::new($scheduleContent.ScheduleTests.TestID)
$getTestResultsBody = '{"TestID":"' + $testId + '"}' #Hard-coding this value works.
$getResponse=(curl -Method 'POST' -Uri $baseUrl'/gettestresults' -Headers #{"Accept"="application/json";"Content-Type"="application/json"} -Body $getTestResultsBody)
My getResponse fails with this error message:
curl : Unable to read data from the transport connection: The
connection was closed. At C:\Development\Services\TESTService\Test
Clients\Caller.ps1:43 char:27 ... etResponse=(curl -Method 'POST'
-Uri $baseUrl'/gettestresults' -Heade ...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : NotSpecified: (:) [Invoke-WebRequest], IOException
FullyQualifiedErrorId : System.IO.IOException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
I debugged my service and it is receiving the correct request and sending the correct response back.
If I hard-code testId in the getRequest, the whole script works. So my guess is that it has something to do with reading the test id from the first call.
Any pointers on what could be wrong?

powershell invoke rest api toward AWX

I'm struggling for last week or more with sending rest api command from powershell to add host in AWX(from curl is working). When I sent one parameter is worki but I need to send also variables
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Basic $VfAwxTokenX")
$Headers.Add('Content-Type', 'application/json')
$body = #{
name = '$vmname'
variables = 'test'
}| ConvertTo-Json
write-output $body
$response = Invoke-RestMethod 'https://awx/api/v2/inventories/2/hosts/' -Method 'POST' -Headers $headers -UseBasicParsing -Body $body
and error what i get:
{
"name": "wewewe",
"variables": "test" } Invoke-RestMethod : {"variables":["Cannot parse as JSON (error: Expecting value: line 1 column 1 (char 0)) or
YAML (error: Input type str is not a dictionary)."]} At line:32
char:13
$response = Invoke-RestMethod 'https://awx/api ...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod],
WebException
FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Maybe any one of users had that issue and now how to overcome it?
According to my research, the parameter variables should be defined as JSON or YAML format. For more details, please refer to here.
For more details about how to call the API, please refer to the blog.
the problem was prosaic
variables = 'test'
if i put anything else then test is working :/

Powershell is swallowing REST error response

I am using Invoke-WebRequest in Powershell and whenever my request is determined to be invalid by the targeted API endpoint, it obviously denies the request and sends back an HTTP error code like (400) Bad Request but it also includes the reason for the error (provided by the API vendor) but that isn't included in the logs inside of PowerShell.
I confirmed the detailed error is sent back because I see it in PostMan and the vendor confirmed the same. Powershell just doesn't want to show it. Here is an example of my code and the response it is generating.
Invoke-WebRequest -Credential $cred -Uri $url -Method POST -Body $json -ContentType 'application/json'
Invoke-WebRequest : The remote server returned an error: (400) Bad Request.
At \\*****\******$\Appsense\Desktop\Untitled2.ps1:42 char:1
+ Invoke-WebRequest -Credential $cred -Uri $url -Method POST -Body $jso ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest)
[Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.
InvokeWebRequestCommand
How do I capture that more detailed error message?
Two parts to this. First you need to have it throw a terminating error with -ErrorAction Stop. This allows us to then use a try/catch block to catch the exception. With the Exception we can then get the detailed response stored in the Exception Status Description. This is fine for most requests.
To get the body of the message requires a few more steps. Since we get a WebResponse object, there is no "Nice" Message parameter for us. So we have to use a StreamReader to Stream the content ourselves:
try
{
$Response = Invoke-WebRequest -Credential $cred -Uri $url -Method POST -Body $json -ContentType 'application/json' -ErrorAction Stop
# This will only execute if the Invoke-WebRequest is successful.
$StatusCode = $Response.StatusCode
}
catch
{
#Excepion - Display error codes
Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__
Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription
#Get body of me
$streamReader = [System.IO.StreamReader]::new($_.Exception.Response.GetResponseStream())
$ErrResp = $streamReader.ReadToEnd() | ConvertFrom-Json
$streamReader.Close()
Write-Host $ErrResp
}

Invoke-WebRequest / Invoke-RestMethod failed with error underlying connection closed

I have an existing REST API that accept x-www-form-urlencoded. The API need parameter apikey, and tested successfully in Postman as shown below.
However I need to invoke this API using Powershell.Below is my code :
$params = #{"apikey"="abcd1234"}
Invoke-WebRequest -Uri http://localhost:3030/api/v1/usergroupsync -Method POST -Body $params
#also tried below, no avail.
Invoke-RestMethod -Uri http://localhost:3030/api/v1/usergroupsync -Method POST -Body $params
However I encountered this error :
Invoke-WebRequest : The underlying connection was closed: An unexpected error occured on a receive At line:14 char:1
+ Invoke-WebRequest -Uri http://localhost:3030/api/v1/usergroupsync -Method POST -...
+==============================================================================
+ CategoryInfo : InvalidOperations: (System.Net.HttpWebRequest:HTTTpWebRequest) [Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebcmdletWebResponseException,Microsoft.Powershell.Commands.InvokeWebRequest
If I remove -Body, there is no error, and Response was as expected "API Key is not valid" which means my REST API validate correctly.
So I suspect the reason if my issue is on the body? Any idea on how to solve this issue?
PS Version is 4.0.0
PS C:\> $PSVersionTable.PSVersion
Major Minor Build Revision
----- ----- ----- --------
4 0 -1 -1
You should use the -Header switch to pass your parameters. Though Invoke-WebRequest support header, I recommend using Invoke-RestMethod as it also return the Headers.
Try something like,
Invoke-RestMethod -Method Post -Uri http://localhost:3030/api/v1/usergroupsync -Body (ConvertTo-Json $body) -Header #{"apikey"=$apiKey}
Check this and this for more information

Create Post Body with Parameter and List in Powershell

I'm attempting to use a REST API where I can add a list of items via a POST. However, I cannot seem to get the format of the body correct. The documentation says it's looking for a parameter called "data" that's type is body and the data type is an array. The sample provided for data, shows ["String","String","String"] I've asked for help on the vendors forums, but few users seem to use PowerShell.
I receive the following error:
"Invoke-RestMethod : {"message":"Request body must be populated for body parameter \"data\"","details":{},"description":"","code":10,"http_response":{"message":"The request was well-formed but was unable to be followed due to semantic errors","code":422}}"
I've tried many different formats for the body, but none seem to take. Here's an example of what I've been attempting:
$apiKey = "XXXXXXXXXXXXXXXXXXXXXX"
$url = "https://X.X.com"
$URI = "https://X.X.com/api/reference_data/sets/bulk_load/APITest"
Invoke-RestMethod -Method Post -Uri $URI -Body (convertto-json $body) -Header #{"SEC"= $apiKey }
$body = #{"10.10.1.5","10.10.1.5","50.50.50.50","123.45.6.7"}
I've also tried something like:
$body = #{"data"="body";"value"="10.10.1.5","50.50.50.50","123.45.6.7"} | convertto-json
But then I get this error:
Invoke-RestMethod : {"message":"beginObject() Expecting JSON Array, not a JSON Start of an Object","details":{},"description":"An error occurred parsing the JSON formatted message
body","code":1001,"http_response":{"message":"Invalid syntax for this request was provided","code":400}}
At Z:\Tools\Scripts\PowerShell\RefSetPostExample.ps1:24 char:1
+ Invoke-RestMethod -Method Post -Uri $URI -Body $body -contenttype "ap ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Thanks in advance for any advice on this.
Thanks to #4c74356b41 's practical advice I found:
http://wahlnetwork.com/2016/02/18/using-powershell-arrays-and-hashtables-with-json-formatting/
The answer was to do this:
$body = #("10.10.50.50","10.50.1.5")| convertto-json
Thank you!