Invoke-ResMethod Rest API - json

This is my first time reaching out on here.
I am trying to create a script for Ivanti Appsense using json code powershell, but i hit an issue
i keep getting a return message "te request is invalid" i am hoping i can get some help
so in powershell this is my code
$url = "http://server/path/api/ImmediateTask"
$cred = Get-Credential
$body = #"
{
"id":"the ID",
"operations" = [
{
"windowsSettingsGroupDisplayName": "_Active Setup",
"operation":{
"liveSettingsDelete":{
"deleteRegistry": true,
"deleteFiles": true,
"preserveArchives": true
}
}
}
"#
$request = Invoke-RestMethod -Method post -Credential $cred -Uri $url -Body $body -ContentType "application/json"
$request
however when i run it and use the correct credentials this is my output

This might not be the whole answer to your problem, but one issue is you're sending invalid json to the API.
You can use PowerShell's features to generate the json string programmatically rather than do it by hand yourself. This way PowerShell will give you more meaningful error messages if your syntax is invalid, rather than waiting for the API to give you a generic "An error has occurred" message:
$data = [ordered] #{
"id" = "the ID"
"operations" = #(
[ordered] #{
"windowsSettingsGroupDisplayName" = "_Active Setup"
"operation" = [ordered] #{
"liveSettingsDelete" = [ordered] #{
"deleteRegistry" = $true
"deleteFiles" = $true
"preserveArchives" = $true
}
}
}
)
};
$json = ConvertTo-Json $data -Depth 99;
write-host $json
#{
# "id": "the ID",
# "operations": [
# {
# "windowsSettingsGroupDisplayName": "_Active Setup",
# "operation": {
# "liveSettingsDelete": {
# "deleteRegistry": true,
# "deleteFiles": true,
# "preserveArchives": true
# }
# }
# }
# ]
#}
$data is basically building a nested hashtable structure, which PowerShell (and your IDE) will warn you about if you have missing brakcets, unclosed quotes, etc.
ConvertTo-Json converts this structured object into a json string.
You might still get errors from your API after doing this, but at least you'll know your json is valid.

Related

Azure Management Service - Type conversion error ( NSG Security rule )

I am using the Azure Management Rest API in Powershell , to create an NSG with some rule properties at the creation time. (yes I am aware that there is a PS module that can do that as well)
I have constructed the body of my PUT request as per the Microsoft documentation.
$url = "https://management.azure.com/subscriptions/$subid/resourceGroups/$rg/providers/Microsoft.Network/networkSecurityGroups/$nsg2" +"?api-version=2022-05-01"
$body = #{
"name" = "NSG-Test";
"location" = "useast";
"properties"= #{
"securityRules" = #(
#{
"name" = "rule1"
"properties"= #{
"protocol" = "*"
"sourcePortRange"= "*"
"destinationPortRange" = "80"
"sourceAddressPrefix"= "*"
"destinationAddressPrefix"= "*"
"access" = "Allow"
"priority" = 130
"direction"="Inbound"
}
}
)
}
} | ConvertTo-Json
try{
$Result = (Invoke-RestMethod -Uri $url -Headers $Headers -Method PUT -Body $body -Verbose -ContentType "application/json")
Write-Host $Result
}
Unfortunately I am greeted with the following error when executing this code :
{
"error": {
"code": "InvalidRequestFormat",
"message": "Cannot parse the request.",
"details": [
{
"code": "InvalidJson",
"message": "Error converting value \"System.Collections.Hashtable\" to type 'Microsoft.WindowsAzure.Networking.Nrp.Frontend.Contract.Csm.Public.Se
curityRule'. Path 'properties.securityRules[0]', line 4, position 75."
}
]
}
}
So the reason behind this is the nested dictionary #{"name"="rule1"..} inside the securityRules attribute value.
When removing this hashtable, the request executes and the NSG gets created, however without any properties of course.
Is there any way to circumvent this issue and have the REST API accept my JSON body with it's properties?
I tried to reproduce the same in my environment I got the same error as below:
To resolve this issue, Make sure to add -Depth 4 in the ConvertTo-Json .
When I added ConvertTo-Json -Depth 4 the error was resolved.
Code:
$AppId="<clientID>"
$AppSecret="75X8Q~2RXXXXXX"
$TokenURI="https://login.microsoftonline.com/2f2ebbbc-e970-XXXXXXXX/oauth2/token"
$Resource="https://management.core.windows.net"
#OAUTH
$BodyRequest="grant_type=client_credentials&client_id=$AppId&client_secret=$AppSecret&resource=$Resource"
$AccessToken=Invoke-RestMethod -Method Post -Uri $TokenURI `
-Body $BodyRequest -ContentType 'application/x-www-form-urlencoded'
$subid ="<SubscriptionID>"
$rg="imran"
$nsg2="nsg2"
#$Headers=#{}
#$Headers.Add("Authorization ","Bearer " + $AccessToken.access_token)
$RequestURI = "https://management.azure.com/subscriptions/$subid/resourceGroups/$rg/providers/Microsoft.Network/networkSecurityGroups/$nsg2" + "?api-version=2022-07-01"
$body=#{
"name" = "nsg2";
"location" = "East us";
"properties"= #{
"securityRules" = #(
#{
"name" = "rule1"
"properties"= #{
"protocol" = "*"
"sourcePortRange"= "*"
"destinationPortRange" = "80"
"sourceAddressPrefix"= "*"
"destinationAddressPrefix"= "*"
"access" = "Allow"
"priority" = 130
"direction"="Inbound"
}
}
)
}
} | ConvertTo-Json -Depth 4
$Headers=#{}
$Headers.Add("Authorization","Bearer " + $AccessToken.access_token)
$Result = (Invoke-RestMethod -Uri $RequestURI -Headers $Headers -Method PUT -Body $body -Verbose -ContentType 'application/json' )
Write-Host $Result
Result:
To confirm in portal rule1 is added successfully like below:

Azure Runbook test with JSON input parameter? [duplicate]

I am badly struck by this problem. I request you to answer or give a hint. I am running out of options.
I am calling an azure runbook upon high CPU utilization via a WebHook. My problem is inside runbook data is not getting decoded properly. For example, the below line is not printing anything.
Write-Output $WebHookData.RequestHeader
Wheras IF i try to explictly convert the data to JSON, like this
*$WebhookData = ConvertFrom-Json $WebhookData*
then it is a throwing error.
ConvertFrom-Json : Invalid JSON primitive: . At line:6 char:31 +
$WebhookData = $WebhookData | ConvertFrom-Json
By the way, I am trying to use the runbook available on Azure gallery {Vertically scale up an Azure Resource Manager VM with Azure Automation}
My Webhook is called from alert created on VM.
A very strange observation:
Working WebHood Example (found in an example) {"WebhookName":"test1","RequestBody":" [\r\n {\r\n \"Message\": \"Test Message\"\r\n }\r\n****]****"
Not Working(the data sent upon calling runbook from VM):
{"WebhookName":"test2","RequestBody":" {\"schemaId\":\"AzureMonitorMetricAlert\"}}
Thanks
I was getting the same error. From my testing, it appears that when performing a "Test" of the runbook, the Webhook data is received as plain text, but when invoked remotely it comes through already formatted as JSON. Here was my solution to cover both scenarios and so far has been working well...
Param (
[object] $WebhookData
)
# Structure Webhook Input Data
If ($WebhookData.WebhookName) {
$WebhookName = $WebhookData.WebhookName
$WebhookHeaders = $WebhookData.RequestHeader
$WebhookBody = $WebhookData.RequestBody
} ElseIf ($WebhookData) {
$WebhookJSON = ConvertFrom-Json -InputObject $WebhookData
$WebhookName = $WebhookJSON.WebhookName
$WebhookHeaders = $WebhookJSON.RequestHeader
$WebhookBody = $WebhookJSON.RequestBody
} Else {
Write-Error -Message 'Runbook was not started from Webhook' -ErrorAction stop
}
I tried with a webhook, the script Write-Output $WebHookData.RequestHeader should work fine.
And if I use ConvertFrom-Json $WebhookData, I can reproduce your issue, not sure why it occurred, according to the doc, the $WebhookData is also in a JSON format, if it is accepted, you could use ConvertFrom-Json -InputObject $WebhookData.RequestBody, it will work fine.
My runbook:
param
(
[Parameter (Mandatory = $false)]
[object] $WebhookData
)
if ($WebhookData) {
Write-Output $WebhookData.RequestHeader
$Body = ConvertFrom-Json -InputObject $WebhookData.RequestBody
Write-Output $Body
} else
{
Write-Output "Missing information";
exit;
}
The powershell script I used to send a webhook:
$uri = "https://s5events.azure-automation.net/webhooks?token=xxxxxxxxxxxx"
$vms = #(
#{ Name="vm01";ResourceGroup="vm01"},
#{ Name="vm02";ResourceGroup="vm02"}
)
$body = ConvertTo-Json -InputObject $vms
$header = #{ message="StartedbyContoso"}
$response = Invoke-WebRequest -Method Post -Uri $uri -Body $body -Headers $header
$jobid = (ConvertFrom-Json ($response.Content)).jobids[0]
Output:
I had the same problem use following to get webhookdata if using test pane with Alert json as input
if(-Not $WebhookData.RequestBody){
$WebhookData = (ConvertFrom-Json -InputObject $WebhookData)
}
$RequestBody = ConvertFrom-JSON -InputObject $WebhookData.RequestBody

JSON payload for POST sent as "System.Collections.Hashtable" instead of actual data

I am using Powershell to dynamically create a payload of data to be packaged up and sent on in a REST API Post Request.
My problem is that when it is recived by the API, it is listed as System.Collections.Hashtable. I am clearly doing something wrong here in how the data is being formatted, but nothing seems to work for me.
Here's how it is received by the API
{
"properties": {
"recip_test": [
"System.Collections.Hashtable",
"System.Collections.Hashtable"
],
"offending_shifts": "MAX, OnCall-Default Shift",
"group_name": "Alarmpoint Administrators"
}
}
I've tried ConvertTo-Json as well as += () / .Add() but none of those seem to work.
I am looping through an array of data which represent ID's in that array. For each item in that array (in the loop) I need to make a hash table which looks like this,
$recipient = #{
'id' = $y
'recipientType' = 'PERSON'
}
And then take that hash and shovel it into the payload field for recipients which then needs to be passed in the POST request. Below is the full code.
foreach($x in $collated_group_data) {
if ($x.group_name -ne 'Alarmpoint Administrators') {
next
}
$uuid = "***********/triggers?apiKey=**************"
$webhook_path = "$base/api/integration/1/functions/$uuid"
$payload = #{
'properties' = #{
'group_name' = $x.group_name
'offending_shifts' = $x.offending_shifts.Substring(0, $x.offending_shifts.Length - 2)
'recipients' = #()
}
}
foreach($y in $x.supervisor_ids) {
$payload.properties.recipients += #{'id' = $y; 'recipientType' = 'PERSON'}
}
$payload = $payload | ConvertTo-Json
Invoke-WebRequest -Uri $webhook_path -Method POST -Body $payload -ContentType 'application/json'
}
You must use the -Depth parameter with a value of 3 or greater in the ConvertTo-Json command in this case.
$payload = $payload | ConvertTo-Json -Depth 3
By default, the -Depth parameter is set to 2. The parameter specifies how many levels of contained objects are included in the JSON representation. You have three levels in your example.

How to parse RESTful API response with powershell that doesn't have key defined of the array

I'm writing a powershell script to call a RESTful API and I need to parse the response. I'm having trouble parsing the response because there isn't a key name to the array. The API just returns contents of an array:
[
{
"ProfileId": "b9b4bf0b-fea3-4464-af91-b79b521d36ba",
"SourcePhoneNumber": "8880001111",
"FirstName": "Peter"
},
{
"ProfileId": "b9b4bf1b-cta3-4464-af91-b68c521d36ba",
"SourcePhoneNumber": "8660001111",
"FirstName": "Fred"
}
]
Here's how I'm calling the API and getting the response:
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("content-type", "application/json")
$headers.Add("Accept-Language", "en-US")
$headers.Add("Authorization", "OAuth $AccessToken")
$response = Invoke-RestMethod "$($privateApiUrl)/api/v1/profiles" -Headers $headers -Method Get -ErrorVariable RestError -ErrorAction SilentlyContinue
if ($RestError)
{
$HttpStatusCode = $RestError.ErrorRecord.Exception.Response.StatusCode.value__
$HttpStatusDescription = $RestError.ErrorRecord.Exception.Response.StatusDescription
Throw "Http Status Code: $($HttpStatusCode) `nHttp Status Description: $($HttpStatusDescription)"
}
else {
Write-Host $response
#Need to parse the first ProfileId out of the response here
$json = ConvertTo-Json -InputObject #( $response )
Write-Host $json[0].ProfileId #This outputs nothing
$response | Out-File -FilePath c:\response2.txt
}
The 2nd to last line "Write-Host $json[0]... is where I'm looking to access the ProfileID.
The response is getting written to c:\response2.txt so I know the API request is working and I'm getting good data back from API call.
So what am I doing wrong in accessing the ProfileId of the objects?
As TessellatingHeckler stated - i just needed to not convert it to json and use $response[0].ProfileId

Open IE and send JSON POST data

I'm trying using PowerShell to open a web page while sending JSON as my POST data.
Here is what I have
$ie = New-Object -comObject InternetExplorer.Application
$ie.visible = $true
$postBytes = [System.Text.Encoding]::Default.GetBytes('"xCoord":"11.11","yCoord":"22.22","scale":"8,000,016.00","coordSysId":"123"');
$ie.navigate("http://localhost/ProcessSearch", "_blank", $postBytes, "Content-Type: application/json; charset=utf-8")
The web page does open however the JSON isn't getting posted according to Fiddler.
How can I send the POST data?
I would use the cURL utility http://curl.haxx.se/dlwiz/?type=bin&os=Win64 and run it from PowerShell. No need to use IE or any browser for that matter.
I've been looking at something similar and I've posted this http://amonkeysden.tumblr.com/post/5842982257/posting-to-a-restful-api-with-powershell - there's a link to the source there as well but more specifically this is the module:
https://bitbucket.org/thompsonson/powershellmodulerepository/src/5e0afe9d0e26/PsUrl/PsUrl.psm1
and you probably want the New-RestItem function though the Write-URL might do it for you as well.
You should pass the JSON as a string - the example below isn't tested... :).
eg.
New-RestItem http://www.tumblr.com/api/write -Data #{"JSON" = '{"something": "value", "more": {"morekey1":"value", "morekey2": "value"} }'
function New-RestItem {
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, Position=0)]
[String]$Url,
[HashTable]$Data,
[TimeSpan]$Timeout = [System.TimeSpan]::FromMinutes(1)
)
Add-Type -AssemblyName System.Web
$formData = [System.Web.HttpUtility]::ParseQueryString("")
foreach($key in $Data.Keys){
$formData.Add($key, $Data[$key])
}
Write-Verbose $formData.ToString()
$reqBody = [System.Text.Encoding]::Default.GetBytes($formData.ToString())
try{
$req = [System.Net.WebRequest]::Create($Url)
$req.Method = "POST"
$req.ContentType = "application/x-www-form-urlencoded"
$req.Timeout = $Timeout.TotalMilliseconds
$reqStream = $req.GetRequestStream()
$reqStream.Write($reqBody, 0, $reqBody.Length)
$reqStream.Close()
$resp = $req.GetResponse()
Write-Verbose $resp
Write-Output $resp.StatusCode
}
catch [System.Net.WebException]{
if ($_.Exception -ne $null -and $_.Exception.Response -ne $null) {
$errorResult = $_.Exception.Response.GetResponseStream()
$errorText = (New-Object System.IO.StreamReader($errorResult)).ReadToEnd()
Write-Warning "The remote server response: $errorText"
Write-Output $_.Exception.Response.StatusCode
} else {
throw $_
}
}
<#
.Synopsis
POSTs the data to the given URL
.Description
POSTs the data to the given URL and returns the status code
.Parameter Url
URL to POST
.Parameter Data
Hashtable of the data to post.
.Parameter Timeout
Optional timeout value, by default timeout is 1 minute.
.Input
URL and a hash of the data to create
.Output
The HTTP Status code (Created (201), Forbidden (403) or Bad Request (400))
.Example
PS:> New-RestItem http://www.tumblr.com/api/write -Data #{"Foo" = "Bar" }
Description
-----------
POST's data to the tumblr write API as application/x-www-form-urlencoded
#>
}