Invoke-RestMethod convert hashtable to json array - powershell

I'm trying to send a request to an API that requires the body to be a json array. How can I convert the parameters hash table into a json array?
$parameters =#{
"sn" = "CND3210W9M"
"pn" = "D5H49AV"
}
$post = Invoke-RestMethod -Uri "https://blah.com/queries" -Method Post -Body ($parameters | ConvertTo-Json) -Headers $headers -ContentType 'application/json'
The API example shows the array below as a valid body
[
{
"sn": "CND3210W9M",
"pn": "D5H49AV"
}
]

If your request is expecting valid json, it might look something like this:
$body = #{
parameters =
#( # Make this an array
#{ # of hashtables
"sn" = "CND3210W9M"
"pn" = "D5H49AV"
} ) }
$body | ConvertTo-Json
Output:
{
"parameters": [
{
"pn": "D5H49AV",
"sn": "CND3210W9M"
}
]
}

#Eris, I tried using your code but it returned an extra {} which the API didn't like.
In the end I ended up just using a here string and moving on. It's not very elegant but couldn't get the API to respond properly otherwise.
$body = #"
[
{
"sn": "$sn",
"pn": "$pn"
}
]
"#

Related

Looping through PSObject from JSON response

I am running Invoke-RestMethod to get a response from an API. That response is in JSON format and I am trying to figure out how to get access to the individual properties in my Powershell code.
Given these two lines:
$response = Invoke-RestMethod -Uri $paymentJournalUri -Method 'GET' -Headers $headers | ConvertTo-Json
Write-Host $response
my output looks like this:
{
"#odata.context": "https://api....yments",
"value": [
{
"#odata.etag": "W/\"JzI....Dsn\"",
"id": "b86ac....8fab",
"journalId": "59....b3ca",
"journalDisplayName": "EFT",
"lineNumber": 30000,
"vendorId": "a4c...552",
...
So how can I loop over the 'value' array and get access to each of those properties?
I have tried something like this:
$PSObj = $response | ConvertTo-Json
#Write-Host $PSObj
$PSObj.value | ForEach-Object {
Write-Host $_.journalDisplayName
}

Powershell Invoke-Webrequest Body Rest-API

I wanted to add a Body in a Invoke-Webrequest
I am a newbie in Powershell Rest API Request...
So here is the example what i need to create it in a Body into a Powershell Scipt:
Body:
{
"select":[
"SERVICE.ID"
],
"parameter":[
{
"field":"SERVICE.NAME",
"value":HOSTNAME
}
]
}
How can i convert these into a Powershell Body ??
You can use a JSON Body directly with Invoke-Webrequest
$Body = #'
{
"select":[
"SERVICE.ID"
],
"parameter":[
{
"field":"SERVICE.NAME",
"value": "HOSTNAME"
}
]
}
'#
Invoke-WebRequest -Uri [...] -Method Post -Body $Body
And you can convert it to PS Object using ConvertFrom-Json like this:
$Obj = $Body | ConvertFrom-Json
$Obj
select parameter
------ ---------
{SERVICE.ID} {#{field=SERVICE.NAME; value=HOSTNAME}}

Invoke-RESTMethod error "request_data must be of type: dict"

I am just starting out with REST API and the only language I have available to me is PowerShell
I am calling a list of "things" - this works and I get the ID property
I then try to iterate through that list and get a more detailed record for the "thing" (The second Invoke-RestMethod) - when I attempt this I get an error:
Invoke-RestMethod : {"reply": {"err_code": 500, "err_msg": "Got an invalid input while processing XDR public API", "err_extra": "request_data must be of type: dict"}}
I thought that convertto-json would convert the hashtable "request" to a dictionary.
I am new to this so please don't assume I know anything.
$ContentType = "application/json"
$URL_All = 'https://api-<company>.xdr.eu.paloaltonetworks.com/public_api/v1/endpoints/get_endpoints/'
$URLEndpoint = 'https://api-<company>.xdr.eu.paloaltonetworks.com/public_api/v1/endpoints/get_endpoint/'
$endpoints = Invoke-RestMethod -uri $URL_All -method POST -Headers #{"x-xdr-auth-id" = "1"; "Authorization" = "<API-KEY_Goes_Here>" } -ContentType "application/json" -Verbose
if ($endpoints.reply.count -gt 0){
write-host "Found $($endpoints.reply.count) details"
}
else {
write-host " API didn't return any endpoint - ending script "
break
}
foreach ($endpoint in $endpoints.reply) {
if($endpoint.agent_id -ne $null){
$request = #{
request_data = '{}'
filters = #{
field = "endpoint_id_list"
operator = "in"
Value = "$($endpoint.agent_ID)"
Search_from = 0
Search_to = 1
sort = #{
field = "last_seen"
keyword = "desc"
}
}
} | ConvertTo-Json
$Endpoint_full += Invoke-RestMethod -uri $URLEndpoint -method POST -Body $request -Headers #{"x-xdr-auth-id" = "1"; "Authorization" = "<API-KEY_Goes_Here>" } -ContentType "application/json" -Verbose
}
}
Your JSON conversion isn't the issue, the hint is in the error message.
"request_data must be of type: dict"
Key being request_data which just consists of this in your request:
request_data = '{}'
Ie. it's basically blank while the API expects a hash table or similar structure.

Powershell Invoke-RestMethod POST has issues with json

I am trying to use a REST API to configure some alerts in RecoverPoint for Virtual Machines (RP4VM). I am trying to enter multiple filters at the same using json. The json file looks like this:
[
{
"JsonSubType": "SystemEventLogsFilter",
"level": "WARNING",
"scope": "NORMAL",
"eventsIDs": [],
"filterUID": {
"id": 1570417688566256135
},
"name": "RPA_issue",
"topic": "RPA",
"groupsToInclude": null
},
{
"JsonSubType": "SystemEventLogsFilter",
"level": "WARNING",
"scope": "ADVANCED",
"eventsIDs": [],
"filterUID": {
"id": -1728986321682574312
},
"name": "cluster_events",
"topic": "CLUSTER",
"groupsToInclude": null
}
]
When I try to run the script I get an error:
Unexpected token (START_ARRAY), expected START_OBJECT: need JSON Object to contain As.PROPERTY type information (for class com.emc.fapi.version5_2.commons.SystemEventLogsFilter)
at [Source: org.apache.catalina.connector.CoyoteInputStream#75b592c2; line: 1, column: 1]
If I remove the square brackets it does the first value but not the second. Is this an issue with my code or an issue with theirs?
The script:
$rp4vmcl = import-csv -Path .\test_clusters.csv
$credential = Get-Credential
$username = $credential.GetNetworkCredential().UserName
$password = $credential.GetNetworkCredential().password
$credPair = "$($username):$($password)"
$encodedCredentials = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($credPair))
$headers = #{
Authorization = "Basic $encodedCredentials";
"Accept" = "application/json";
"Content-Type" = "application/json"
}
$comp = "/system/event_logs_filters"
$json = Get-Content .\event_log_filter.json -Raw
foreach ($s in $rp4vmcl) {
$cluster = $s.cluster_name
$uid = $s.cluster_uid
$curl = $s.cluster_url
$url = "$curl$comp"
$cluster
$results = Invoke-RestMethod -Method POST -uri $url -SkipCertificateCheck -Headers $headers -Body $json
}
If the recipient expect one Call per Json Object it will not be able to handle arrays. It's totally dependant from the implementation of the webservice. Btw, better change the body:
$results = Invoke-RestMethod -Method POST -uri $url -SkipCertificateCheck -Headers $headers -Body ([System.Text.Encoding]::UTF8.GetBytes($json))
Try this first, and if this is doesnt help, loop through the elemnts and call the webservice individual:
$jsonObject = $json | ConvertFrom-Json
$results = #()
$jsonObject | foreach {
$json = $_ | ConvertTo-Json -Depth 99
$results += Invoke-RestMethod -Method POST -uri $url -SkipCertificateCheck -Headers $headers -Body ([System.Text.Encoding]::UTF8.GetBytes($json))
}
Btw, the invoke-restmethod is buggy in powershell < 6 Version, so if youre will with the standard V5 Windows Version, check the response headers via invoke-webservice, and if the response header is:
"application/json"
and not
"application/json; charset=utf-8"
powershell will misinterpret this as a windows encoding. So if youree experiencing encoding issues, you have four options:
1.) if you can modify the webservice, change the response header
2.) switch to a newer powershell version (v7 is recommended)
3.) build your own webservice call directly via the .net cmdlets
4.) use the invoke-webservice, write the answer directly into a file with the OutFile Paramater
see also:
Powershell Invoke-RestMethod incorrect character

How to store 1 output from one script to another?

Suppose I have script1.ps1 with the following code:
Function Renew_Token($token) {
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("X-Vault-Token", $token)
$response = Invoke-RestMethod -method POST -uri "https://vault.com:8243/v1/auth/token/renew-self" -ContentType 'application/json' -headers $headers
$response| ConvertTo-Json -depth 100
}
Function getValues($token) {
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("X-Vault-Token", $token)
$response = Invoke-RestMethod -method GET -uri "https://vault.com:8243/v1/secret/vault/development" -ContentType 'application/json' -headers $headers
$response.data| ConvertTo-Json -depth 100
}
Renew_Token $token
write-host "token renewed!"
write-host "Vault Values:"
getValues $token
This gives me back a response like this:
{
"request_id": "ghgdf5-yuhgt886-gfd76trfd",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": null,
"wrap_info": null,
"warnings": null,
"auth": {
"client_token": "i657ih4rbg68934576y",
"accessor": "t543qyt54y64y654y",
"policies": [
"default",
"vault"
],
"token_policies": [
"default",
"vault"
],
"metadata": null,
"lease_duration": 2000,
"renewable": true,
"entity_id": ""
}
}
token renewed!
Vault Values:
{
"abc": "1234",
"def": "897",
"klm": "something12"
}
Now consider in a Script2.ps1, I call script1
$second_response = & ".\Script1.ps1"
of course, $second_response will store the 2 responses above as output.
How can i store JUST the second response as keys/values say in a table in Script2? i.e. this part:
{
"abc": "1234",
"def": "897",
"klm": "something12"
}
$HashTable = #{ }
$HashTable.Add($second_response.key, $second_response.value)
in other words, somehow the $second_response variable should only store this output:
{
"abc": "1234",
"def": "897",
"klm": "something12"
}
Note: that second response is considerably dynamic. meaning there could be different values on different environments. therefore, i want to be able to DYNAMICALLY store whatever is in this response, not hardcode the values
Also, I need the 2 responses in script 1 because i use script1 for other purposes, such as say i want to only view the vault content. script2 will have operations on the response from script1, so i have them separated for this convenience and flexibility
UPDATE: Following #kuzimoto suggestion, i removed the output and converting the response back from JSON, i get this output from Script2:
abc: 1234
def: 897
klm: something12
I can't comment yet, but here's a couple possibilities:
Edit script1.ps1
You don't mention if you need the first set of results. If you don't, just simply delete or comment out this line $response| ConvertTo-Json -depth 100 from the Renew_Token function.
Parse the output of script1.ps1 as json
You're passing the output of script1 to script2 as plain text. Simply change $second_response = & ".\Script1.ps1" to $second_response = & ".\Script1.ps1" | ConvertFrom-Json. Then when you want to access the second response, use $second_response[1] as both sets of JSON get added to a custom PS Object and can be accessed individually like you would an array.