Do-untill loop getting error (powershell) - json

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)

Related

"Unexpected token" or "JSON parse error - trailing comma" when doing API call with PowerShell

I have the following PowerShell API script:
$VMname = "abcd"
$IP_address = '2.2.2.2'
$url = "https://ansibletower.xyz.com/api/v2/job_templates/12321/launch/"
$token = "9998980fsfdfsdfdf"
$headers = #{Authorization = "Bearer $token"}
$contentType = "application/json"
$method = "POST"
#### $body = '{"extra_vars": {"vm_name": "abc", "vm_ip_address": "2.2.2.2"}}'
$body = '{"extra_vars":{"vm_name":'$VMname', "vm_ip_address":'$IP_address'}}'
Invoke-RestMethod -ContentType "$contentType" -Uri $url -Method $method -Headers $headers -Body $body
When I try it with manually predefined values in the body (see the commented body line above) - it works. But when I try it with variables $VMname and $IP_address, I get the error:
Unexpected token '$VMname', "vm_ip_address":'$IP_address'}}''
expression or statement.
And if I remove single quotes before and after variables VMname and IP_address I get:
{"detail":"JSON parse error - Expecting value: line 1
column...Possible cause: trailing comma."}
It is obviously a problem with syntax and / or formatting but I cannot find the solution. Does anyone know?
Use ConvertTo-Json for this:
$VMname = "abcd"
$IP_address = '2.2.2.2'
$body = ConvertTo-Json -Compress -Depth 9 #{
extra_vars = #{
vm_name = $VMname
vm_ip_address = $IP_address
}
}
$Body
{"extra_vars":{"vm_name":"abcd","vm_ip_address":"2.2.2.2"}}
Btw, it is probably not even required to serialize your body to Json as Invoke-Restmethod is supposed to take care of that:
-body
When the input is a POST request and the body is a String, the value to the left of the first equals sign (=) is set as a key in the form data and the remaining text is set as the value. To specify multiple keys, use an IDictionary object, such as a hash table, for the Body.

Invoke-RestMethod Range Error, Possible Bug?

Example code that's generating error
$Headers = #{
"Token" = $Token;
"Range" = $Range
}
Invoke-RestMethod -Uri $Uri -Method GET -Headers $Headers -WebSession $WebSession
Error: Invoke-RestMethod : The 'Range' header must be modified using the appropriate property or method.
I believe this is due to a PowerShell Bug. So now I'm attempting with the below example code, and get generic 401 (not authorized) error.
$request = [System.Net.WebRequest]::Create($uri)
$request.Method = "GET"
$request.Headers.Add("Token", $Token)
$request.AddRange("items", 0, 50)
$reader = New-Object System.IO.StreamReader($request.GetResponse().GetResponseStream())
$data = ConvertFrom-Json $reader.ReadToEnd()
The API requires a sessionId which I am clarifying in the first example code block. Is there a way to pass this in the above (second example code block)?
Any other reason for this 401 error? The first code block example retrieves data without issue if range isn't specified.

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.

Manipulate json and send it into web request using Powershell

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!

Only see ' rows=System.Object[] ' when enumerating JSON results in Powershell

I am returning a web service call that supposedly comes back in json format, and it does indeed appear to be valid json. As a string, it looks like this:
{"total":10,"rows":[{"process":"VISIO.EXE","computer_id":57,"last_used":"2016-04-20T01:34:09Z"},{"process":"VISIO.EXE","computer_id":64,"last_used":"2016-04-01T04:09:35Z"},{"process":"VISIO.EXE","computer_id":181,"last_used":"2016-03-10T23:02:53Z"},{"process":"VISIO.EXE","computer_id":230,"last_used":"2016-04-19T05:31:32Z"},{"process":"VISIO.EXE","computer_id":237,"last_used":"2016-04-04T10:23:23Z"},{"process":"VISIO.EXE","computer_id":284,"last_used":"2016-04-15T10:54:29Z"},{"process":"VISIO.EXE","computer_id":8401,"last_used":"2016-05-12T21:55:39Z"},{"process":"VISIO.EXE","computer_id":9045,"last_used":"2016-05-12T08:10:40Z"},{"process":"VISIO.EXE","computer_id":9527,"last_used":"2016-05-11T00:49:11Z"},{"process":"VISIO.EXE","computer_id":10198,"last_used":"2016-05-06T06:59:29Z"}]}
I am trying to enumerate through the results, using the following script. I have tried all of the listed options 1-4 by uncommenting them out one at a time, but I cannot get more than one result to return.
Any ideas as to what I'm doing wrong?
$url = 'https://servername:port/api/sam/raw_app_usage_property_values?criteria={"and":[["process","=","visio.exe"]]}&limit=100&columns[]=process&columns[]=computer_id&columns[]=last_used'
# option 1. get all results, we see a full list of processes, full string returned
#$results = Invoke-WebRequest -Uri $url -Method GET -Headers $headers
#write-host $results
# option 2. get all results but break them down as part of the request. Only one result returned - #{total=10; rows=System.Object[]}
#$results = Invoke-WebRequest -Uri $url -Method GET -Headers $headers | ConvertFrom-Json
#write-host $results
# option 3. use a different method, which supposedly breaks down the request natively. Only one result returned - #{total=10; rows=System.Object[]}
#$results = Invoke-RestMethod -Uri $url -Method GET -Headers $headers
#write-host $results
# option 4. get content directly from file, only one result returned - #{total=10; rows=System.Object[]}
#$results = (Get-Content -path "C:\temp\raw_app_usage_property_values.json" -raw)
# is there anything in the array?
foreach ($result in $results) {
write-host $result
}
You seem to be getting the data back. Remember that the returned object has two properties, "total" and "rows".
Try:
foreach ($result in $results.rows) {
write-host $result
}