Azure Functions: Powershell Push-OutputBinding format JSON - json

I have used below code in my azure PowerShell HTTP function. On triggering this function, I receive JSON body but not as in the same order I have provided in code. Please let me know how can I receive JSON body in response as in same order I have coded i.e. {"Status": "val", "Msg": "val", "Value": "val"}
Push-OutputBinding -Name Response -Value ([HttpResponseContext]#{
StatusCode = [HttpStatusCode]::OK
Headers = #{
"Content-type" = "application/json"
}
Body = #{
"Status" = $status
"Msg" = $body
"Value" = $val
} | ConvertTo-Json
})

The order of keys in a hash table is not determined.
Use an ordered dictionary instead of a hashtable.
This will preserve the order of your keys as you intended them.
To do so, simply add the [Ordered] type in front of your body hashtable.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]#{
StatusCode = [HttpStatusCode]::OK
Headers = #{
"Content-type" = "application/json"
}
# Will keep the order of the keys as specified.
Body = [Ordered]#{
"Status" = $status
"Msg" = $body
"Value" = $val
} | ConvertTo-Json
})
Beginning in PowerShell 3.0, you can use the [ordered] attribute to
create an ordered dictionary
(System.Collections.Specialized.OrderedDictionary) in PowerShell.
Ordered dictionaries differ from hash tables in that the keys always
appear in the order in which you list them. The order of keys in a
hash table is not determined.
Source

Related

Invoke-ResMethod Rest API

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.

how to format json string for aws with powershell

i want to send an event to aws via a cli command in a powershell script. Here is the Json i need to send to the eventbridge:
[
{
"Sensor":"{\"id\":\"880/2021-04-13\",\"attributes\":\"green\",\"Name\":\"SensorGreen\",\"state\":\"SUCCEEDED\"}",
"Source":"google.com"
}
]
Thats what a tried in powershell:
$json='[{"Sensor":"{\"id\":\"880/2021-04-13\",\"attributes\":\"green\",\"Name\":\"SensorGreen\",\"state\":\"SUCCEEDED\"}","Source":"google.com"}]'|ConvertTo-Json -Compress
aws events put-events --entries $json --region "eu-central-1"
That does not work. I even tried to write it to a json file and read it and send it from the file but it doesnt work. It somehow leads to too many "\" slashes or no slashes for my "Sensor" where it is necessary. I even tried to create a object and just convert the Sensor object to Json and then the whole object again to have the escaping, but it is not working.
EDIT:
i tried also this as mentioned in the answer:
$RequestObject = [pscustomobject] #(#{
Sensor = [pscustomobject] #{
id = "880/2021-04-13"
attribute = "green"
Name = "SensorGreen"
state = "SUCCEEDED"
}
Source = "google.com"
})
$RequestObject.Get(0).Sensor=$RequestObject.Get(0).Sensor | ConvertTo-Json -Compress
$Json = $RequestObject | ConvertTo-Json -Compress
aws events put-events --entries $Json --region "eu-central-1"
i included the #() to have an array and converted the sensor object twice to json to include the slashes. but the result is:
Error parsing parameter '--entries': Invalid JSON: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
if i use the command line interface only with the aws command and put the object directly into the command, then it works.. but the powershell does not.
You're passing a JSON string to an external program (aws)
Irrespective of how you constructed that string - directly as a string, or from an object / hashtable via ConvertTo-Json - as of PowerShell 7.1 - manual escaping of embedded " as \" is required, which, in turn requires escaping the preexisting \ preceding the embedded " as \\.
Note: None of this should be necessary, but due to a long-standing bug in PowerShell is - see this answer for details.
PowerShell Core 7.2.0-preview.5 now includes experimental feature PSNativeCommandArgumentPassing with an attempted fix, but, unfortunately, it looks like it will lack important accommodations for high-profile CLIs on Windows - see this summary from GitHub issue #15143. However, it would fix calls to aws.exe, the Amazon Web Services CLI.
# The JSON string to be passed as-is.
$json = #'
[
{
"Sensor":"{\"id\":\"880/2021-04-13\",\"attributes\":\"green\",\"Name\":\"SensorGreen\",\"state\":\"SUCCEEDED\"}",
"Source":"google.com"
}
]
'#
# Up to at least PowerShell 7.1:
# Manually perform the required escaping, to work around PowerShell's
# broken argument-passing to external programs.
# Note:
# -replace '\\', '\\' *looks* like a no-op, but replaces each '\' with '\\'
$jsonEscaped = $json -replace '\\', '\\' -replace '"', '\"'
# Now pass the *escaped* JSON string to the aws CLI:
aws events put-events --entries $jsonEscaped --region "eu-central-1"
ConvertTo-Json is for converting objects in PowerShell, not a string that you have tried to already write in Json. Your $Json variable produces this.
"[{\"Sensor\":\"{\\\"id\\\":\\\"880/2021-04-13\\\",\\\"attributes\\\":\\\"green\\\",\\\"Name\\\":\\\"SensorGreen\\\",\\\"state\\\":\\\"SUCCEEDED\\\"}\",\"Source\":\"google.com\"}]"
If you want to create the object in PowerShell and convert it to Json, then you can do this.
$RequestObject = [pscustomobject] #{
Sensor = [pscustomobject] #{
id = "880/2021-04-13"
attribute = "green"
Name = "SensorGreen"
state = "SUCCEEDED"
}
Source = "google.com"
}
$Json = $RequestObject | ConvertTo-Json -Compress
aws events put-events --entries $Json --region "eu-central-1"
Your Json will look like this if you print your variable out.
{"Sensor":{"id":"880/2021-04-13","attribute":"green","Name":"SensorGreen","state":"SUCCEEDED"},"Source":"google.com"}
Which I think is like the Json that the command is expecting. Not entirely sure why you need the strings escaping or the array. Here it is uncompressed.
{
"Sensor": {
"id": "880/2021-04-13",
"attribute": "green",
"Name": "SensorGreen",
"state": "SUCCEEDED"
},
"Source": "google.com"
}
Just noticed the powershell-2.0 tag. If you are using it, then you should do this instead to create your Json.
$Sensor = New-Object psobject -Property #{
id = "880/2021-04-13"
attribute = "green"
Name = "SensorGreen"
state = "SUCCEEDED"
}
$RequestObject = New-Object psobject -Property #{
Sensor = $Sensor
Source = "google.com"
}
$Json = $RequestObject | ConvertTo-Json -Compress
EDIT
If you absolutely must escape the strings in that way and have a single item array, then you should just pass the Json that you have written in your answer without any further conversion.
$json='[{"Sensor":"{\"id\":\"880/2021-04-13\",\"attributes\":\"green\",\"Name\":\"SensorGreen\",\"state\":\"SUCCEEDED\"}","Source":"google.com"}]'
If you want to make PowerShell do that for you then you would need to perform some string replacement on the Sensor object first.
PowerShell 2.0
$Sensor = New-Object psobject -Property #{
id = "880/2021-04-13"
attribute = "green"
Name = "SensorGreen"
state = "SUCCEEDED"
}
$SensorJson = $Sensor | ConvertTo-Json -Compress
$SensorJson.Replace("`"","\`"")
$RequestObject = New-Object psobject -Property #{
Sensor = $SensorJson
Source = "google.com"
}
$Json = $RequestObject | ConvertTo-Json -Compress
PowerShell 3.0+
$Sensor = [pscustomobject] #{
id = "880/2021-04-13"
attribute = "green"
Name = "SensorGreen"
state = "SUCCEEDED"
}
$SensorJson = $Sensor | ConvertTo-Json -Compress
$SensorJson.Replace("`"","\`"")
$RequestObject = [pscustomobject] #{
Sensor = $SensorJson
Source = "google.com"
}
$Json = $RequestObject | ConvertTo-Json -Compress
Then your AWS command
# Add the array around the compressed Json string.
aws events put-events --entries "[$Json]" --region "eu-central-1"
"[$Json]" prints
[{"Sensor":"{\"id\":\"880/2021-04-13\",\"attribute\":\"green\",\"Name\":\"SensorGreen\",\"state\":\"SUCCEEDED\"}","Source":"google.com"}]

How to parse/access JSON returned by Invoke-WebRequest in Powershell 4

I have the following call to an API in a powershell script (Powershell 4.0):
$Json = Invoke-WebRequest -Uri $RequestURL -UseBasicParsing -Headers $headers -ContentType 'application/json; charset=utf-8' -Method POST -Body $postParams -TimeoutSec 40
...and the content of the response (which is a string in JSON format) is written to a file:
Set-Content $path -Value $Json.Content
An example of a typical response...
{
"MyArray": [{
"MyField": "A1",
"MyField2": "A2"
}, {
"MyField": "B1",
"MyField2": "B2"
}]
}
All well and good, but now I have a requirement to parse the returned content as JSON and query some properties from within this Powershell script.
I presume I need to convert my string to 'proper' JSON and then to a powershell object in order to access the properties...so I have tried combinations of ConvertTo-Json and ConvertFrom-Json but can't ever seem to access it in anything other than a string. For example...
$x = $Json.Content | ConvertTo-Json
Write-Host $x.MyArray[0].MyField
$y = $x | ConvertFrom-Json
Write-Host $y[0].MyArray[0].MyField
In both cases above I get an error "Cannot index into a null array" suggesting that MyArray is null.
How do I convert my $Json response object into an object I can drill down into?
See ConvertFrom-Json
Converts a JSON-formatted string to a custom object or a hash table.
The ConvertFrom-Json cmdlet converts a JavaScript Object Notation
(JSON) formatted string to a custom PSCustomObject object that has a
property for each field in the JSON string.
Once you get the response converted to custom object or a hash table, you can access the individual properties
The link includes coding examples
This seems to work...
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions")
$x = (New-Object -TypeName System.Web.Script.Serialization.JavaScriptSerializer -Property #{MaxJsonLength=67108864}).DeserializeObject($Json.Content)
Write-Host $x.MyArray[0].MyField
...although not sure why yet.

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.

ConvertFrom-Json : Cannot convert the JSON string because a dictionary that was converted from the string contains the duplicated keys

The following JSON is getting returned from OData API service:
{
"d": {
"results": [
{
"FileSystemObjectType": 0,
"Id": 1,
"ContentTypeId": "0x0100BC97B2F575CB0C42B79549F3BABD32A8",
"Title": "Nokia California",
"Address": "200 South Matilda Avenue\nW Washington Ave\n94086 Sunnyvale, California\nUnited States of America",
"ID": 1,
"Modified": "2014-02-24T10:06:39Z",
"Created": "2014-02-24T10:06:39Z",
"AuthorId": 12,
"EditorId": 12,
"OData__UIVersionString": "1.0",
"Attachments": false,
"GUID": "d12aafad-502a-4968-a69e-36a7ea05ec80"
}
]
}
}
and saved as a string into variable named $data
An attempt to convert a JSON-formatted string to a custom object using ConvertFrom-Json cmdlet:
$results = $data | ConvertFrom-Json
gives the following error:
ConvertFrom-Json : Cannot convert the JSON string because a dictionary
that was converted from the string contains the duplicated keys 'Id'
and 'ID'.
Is there any way to convert the specified JSON-formatted string in PowerShell?
This is how I have done with it:
$results = $data.ToString().Replace("ID", "_ID") | ConvertFrom-Json
Note, both examples assume the JSON is stored in the $jsonstring variable.
In PowerShell Core, ConvertFrom-Json -AsHashtable is the easiest alternative:
$json = $jsonstring | ConvertFrom-Json -AsHashtable
$json['d']['results']
Name Value
---- -----
Modified 2/24/2014 10:06:39 AM
Title Nokia California
Attachments False
ID 1
ContentTypeId 0x0100BC97B2F575CB0C42B79549F3BABD32A8
GUID d12aafad-502a-4968-a69e-36a7ea05ec80
Created 2/24/2014 10:06:39 AM
EditorId 12
AuthorId 12
Address 200 South Matilda Avenue…
Id 1
OData__UIVersionString 1.0
FileSystemObjectType 0
In Windows PowerShell, you can use the Deserialize(String, Type) method from the JavaScriptSerializer Class.
Add-Type -AssemblyName System.Web.Extensions
$serializer = [Web.Script.Serialization.JavaScriptSerializer]::new()
$json = $serializer.Deserialize($jsonstring, [hashtable])
$json['d']['results']
In PowerShell V1.0, or in PowerShell V2.0 when the JSON is too big, I still use a convertion to XML :
Add-Type -AssemblyName System.ServiceModel.Web, System.Runtime.Serialization
function Convert-JsonToXml
{
PARAM([Parameter(ValueFromPipeline=$true)][string[]]$json)
BEGIN
{
$mStream = New-Object System.IO.MemoryStream
}
PROCESS
{
$json | Write-String -stream $mStream
}
END
{
$mStream.Position = 0
try
{
$jsonReader = [System.Runtime.Serialization.Json.JsonReaderWriterFactory]::CreateJsonReader($mStream,[System.Xml.XmlDictionaryReaderQuotas]::Max)
$xml = New-Object Xml.XmlDocument
$xml.Load($jsonReader)
$xml
}
finally
{
$jsonReader.Close()
$mStream.Dispose()
}
}
}
Using this code you can loop thru your items you can test :
$a = Get-Content C:\temp\jsontest.txt
$b.root.d.results.Item
$b.root.d.results.Item[7].Id[0].InnerText
(Edited)
In you case I would only replace the expected duplicate ID/Id
$data = Get-Content C:\temp\jsontest.txt -Raw
$datacorrected = $a -creplace '"Id":','"Id-minus":'
$psJsonIn = $datacorrected | ConvertFrom-Json
If really you've got unexpected duplicate you can write a function that trap the convertion error due to duplicated key and replace one.
ConvertFrom-JSON it going to try to create a PS Custom Object from the JSON string. PowerShell object property names are case-insensitive, so "ID" and "id" represent the same property name. You're going to have to do something with those duplicate property names in your JSON before you try to do that conversion, or it's going to fail.
I used the ToLower() before converting the json to object, resolved my issue.
$sdf = $data.ToLower() | ConvertFrom-Json