I'm trying to manipulate a json object and send it as content into the body of a put / post web request. The source of my json is a file on my disk.
This is my Powershell script:
$urlBase = 'https://mysite.myapp.com/service/api/Item/'
$myJson = (Get-Content 'file.json' | ConvertFrom-JSON)
# Then I manipulate my object
$id = $myJson.id
$myJson.version = '1.2.3.4'
# Request
$response = Invoke-RestMethod -Uri ($urlBase + $id) -Method Put -Body $myJson -ContentType 'application/json' -Headers $hdrs
When I execute my script y get this error message:
Invoke-RestMethod : The remote server returned an error: (400) Bad Request.
At line:18 char:17
+ ... $response = Invoke-RestMethod -Uri ($urlBase + $id) -Method Put -Body ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
If I change my $myJson asignment for this the request works fine...
$myJson = Get-Content 'file.json'
... , but then I can't manipulate my json before send it.
Edited:
If I try to convert back using ConvertTo-Json I get the same error:
$convertedBack = $myJson | ConvertTo-Json
# Request
$response = Invoke-RestMethod -Uri ($urlBase + $id) -Method Put -Body $convertedBack -ContentType 'application/json' -Headers $hdrs
As pointed out in the comments: you need to convert your object back to JSON using the ConvertTo-Json cmdlet.
I see that you've tried that now and had the same problem. So I ask you this: is the value of $convertedBack exactly what you expected? Dump it to file and check!
The reason I am suspicious of this detail is that ConvertTo-Json has a little gotcha in it. Specifically the -Depth parameter which can cause some data loss.
-Depth
Specifies how many levels of contained objects are included in the JSON representation. The default value is 2.
Example Without -Depth
$basicJsonObject = #"
{
"name": "George",
"properties": {
"mood": "jovial",
"coffee": {
"hasCoffee": true,
"mugContents": {
"milk": false,
"doubleShot": true
}
}
}
}
"#
$psObject = ConvertFrom-Json -InputObject $basicJsonObject
Write-Host "Freshly Imported"
Write-Host "DoubleShot = $($psObject.properties.coffee.mugContents.doubleShot)"
$convertedBack = ConvertTo-Json -InputObject $psObject
$reConverted = ConvertFrom-Json -InputObject $convertedBack
Write-Host "Re-Converted"
Write-Host "DoubleShot = $($reConverted.properties.coffee.mugContents.doubleShot)"
Results
Freshly Imported
DoubleShot = True
Re-Converted
DoubleShot =
Example With -Depth
Change one line of code:
$convertedBack = ConvertTo-Json -InputObject $psObject -Depth 5
Results
Freshly Imported
DoubleShot = True
Re-Converted
DoubleShot = True
Note how the new results include the value from the $reConverted variable. This is because the data is not lost further upstream!
Related
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:
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
Try to convert a web request from JSON but always get the below error:
Method invocation failed because [Microsoft.PowerShell.Commands.BasicHtmlWebResponseObject] does not contain a method named 'op_Addition'.
At C:\Users\gmicskei\Desktop\lastlogin_users_azureAD.ps1:39 char:17
+ $QueryResults += $Results
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (op_Addition:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
Here is my code:
do {
$Results = Invoke-WebRequest -Headers $authHeader1 -Uri $Uri -UseBasicParsing -Method "get" -ContentType "application/json"
if ($Results.value) {
$QueryResults += $Results.value
} else {
$QueryResults += $Results
}
$uri = $Results.'#odata.nextlink'
} until (!($uri))
$QueryResultsjson = $QueryResults | ConvertFrom-Json
Can you please advise?
Thanks,
Gabor
The objects returned by Invoke-WebRequest -UseBasicParsing are of type Microsoft.PowerShell.Commands.BasicHtmlWebResponseObject:
This type has no .Value property.
Using it as-is, which your code therefore does, is the source of the problem:
In the first loop iteration, $QueryResults += $Results stores the $Results variable as-is.
In subsequent loop iterations, += tries to "add" to an instance, but no such operation is defined for this type.
As an aside: In order to collect the $Results values in an array, $QueryResults would have to be initialized as an array before entering the loop, but note that building an array iteratively with += should be avoided, due to its inefficency - see this answer.
You can solve all problems by using Invoke-RestMethod instead, which automatically parses JSON responses into objects ([pscustomobject] instances):
$results = do {
# Calls the web service and automatically parse its JSON output
# into an object ([pscustomobject] instance).
$result = Invoke-RestMethod -Headers $authHeader1 -Uri $Uri -Method "get" -ContentType "application/json"
# Output the result at hand, to be collected across all
# iterations in the $results variable being assigned to.
# (.value is assumed to be a top-level property in the JSON data received).
$result.value
# Determine the next URI, if any.
# Since $result is now an *object*, property access should work.
$uri = $result.'#odata.nextlink'
} while ($uri)
I have a script that gets data from a public API. I try to parse a value from the Json response into a variable. However it seems that when I Write-Host the variable it has replaced — with â.
Code:
$SetData = Invoke-RestMethod -Uri "https://mtgjson.com/api/v5/2XM.json" -ContentType "application/json" -Method GET
$Card = $SetData.data.cards | Where-Object { $_.name -eq "Adaptive Automaton" -and $_.isPromo -ne "true"}
Write-Host $Card.type -ForegroundColor Cyan
Output:
Artifact Creature — Construct
It looks like the strings returned by the Invoke-RestMethod here are encoded in 'ISO-8859-1' and not as you would expect in UTF-8.
This means you need to convert to UTF-8 where needed, something like this:
$encoding = [System.Text.Encoding]::GetEncoding('ISO-8859-1')
$SetData = Invoke-RestMethod -Uri "https://mtgjson.com/api/v5/2XM.json" -ContentType "application/json" -Method GET
$Card = $SetData.data.cards | Where-Object { $_.name -eq "Adaptive Automaton" -and !$_.isPromo}
# convert the string in '$Card.type' from encoding 'ISO-8859-1' into 'UTF-8'
$cardType = ([System.Text.Encoding]::UTF8).GetString($encoding.GetBytes($Card.type))
Write-Host $cardType -ForegroundColor Cyan
Output
Artifact Creature — Construct
To convert the whole json to UTF-8, You could use Invoke-WebRequest rather than Invoke-RestMethod:
$encoding = [System.Text.Encoding]::GetEncoding('ISO-8859-1')
$SetData = Invoke-WebRequest -Uri "https://mtgjson.com/api/v5/2XM.json" -Method Get
# convert $SetData.Content to UTF-8 and convert that from JSON
$content = ([System.Text.Encoding]::UTF8).GetString($encoding.GetBytes($SetData.Content)) | ConvertFrom-Json
$Card = $content.data.cards | Where-Object { $_.name -eq "Adaptive Automaton" -and !$_.isPromo}
Write-Host $Card.type -ForegroundColor Cyan
The following is a snippet from my PowerShell script where the values for the parameters $book and $author are not getting substituted. Please suggest some techniques that I can apply to fix it or share some code that can help me out.
$body = #{
version = '1.0'
inactive = 'false'
yml = { "Service1:\n book: $book\n author: $author\n "} | ConvertFrom-Json
} | ConvertTo-Json
$request = Invoke-WebRequest -UseBasicParsing -Method Post -Uri $uri -Body
$body -Headers $headers -ContentType $contentType
$response = ConvertFrom-Json -InputObject $request.Content
You have some weird stuff going on in this line
... yml = { "Service1:\n book: $book\n author: $author\n "} | ConvertFrom-Json } | ConvertTo-Json
Because it says "do a script block with this body, and try to convert the script block to JSON".
So, if you want to have a JSON string in yml field, you have two options.
Write the proper JSON string yourself:
#{...put the rest of your elements here...; yml = "{Service1:'', book:'$book', author: '$author'}"
Populate a hashtable first and then convert it to JSON string:
#{...put the rest of your elements here...; yml = #{Service1=''; book='$book'; author='$author'} } | ConvertTo-Json