Powershell script Missingtypename - json

I am trying to make some API calls through powershell using this documentation
https://octopus.com/blog/manually-push-build-information-to-octopus
$Headers = #{"X-Octopus-ApiKey"=$MYKEY"}
$jsonBody = #{
PackageId = "Test"
Version = "1.1.1"
OctopusBuildInformation =
#{
BuildEnvironment = "Jenkins"
BuildNumber = "1"
BuildURL = "myURL"
VcsCommitNumber = "250"
VcsType = "Git"
Commits = [{Id = "250" LinkUrl = "gitUrl" Comment = "someComment"}]
VcsRoot = "gitUrl"
}
} | ConvertTo-Json -Depth 5
Invoke-RestMethod -Method Post -Uri "http://alfadeployment/api/build-information" -Headers $Headers -Body $jsonBody
However, since I added the commits array, I am getting the following error:
Missing type name after '['.
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : MissingTypename

PowerShell arrays aren't enclosed in [...] - the latter is used for type literals, i.e. to refer to .NET types - hence the error message.
You may - but don't have to - enclose PowerShell arrays in #(...), the array-subexpression operator; using ,, the array constructor operator is enough, though #(...) can be helpful for visual clarity, and to create an empty (#()) or single-element arrays (#('foo')), which is perhaps clearer than the unary use of , (, 'foo')).
Additionally, the array element in your case must be a hashtable too (#{ ... }, not { ... }, the latter is a script block), and, given that the property definitions are on the same line, they must be ;-separated.
To put it all together:
Commits = #(
#{ Id = "250"; LinkUrl = "gitUrl"; Comment = "someComment" }
)

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.

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.

Changing Slack CURL call into PowerShell

I've in the middle of writing a translation of a working Slack script from Bash to Powershell, and I'm stumbled on what is literally the meat of the problem; how to use Invoke-WebRequest to replace curl.
This is the curl command that works successfully in Bash on *nix systems:
curl \
-X POST \
-H "Content-type: application/json" \
--data "{
\"attachments\":
[
{
\"mrkdwn_in\": [\"text\"],
\"color\": \"$COLOUR\",
\"text\": \"$MESSAGE\",
}
]
}" \
https://hooks.slack.com/services/tokenpart1/tokenpart2
Note the $COLOUR and $MESSAGE variables are derived from elsewhere in the script (not the section I'm having trouble with).
I can't get it to work in PowerShell. My translation so far is:
$Body = #{
"attachments" = "[
{
"mrkdwn_in": ["text"],
"color": "$Colour",
"text": "$Message",
]"
}
$BodyJSON = $Body |convertto-JSON
Invoke-WebRequest -Headers -Method Post -Body "$BodyJSON" -Uri "https://hooks.slack.com/services/tokenpart1/tokenpart2" -ContentType application/json
This results in the following error:
At C:\path-to-file-etc.ps1:53 char:11
+ "mrkdwn_in": ["text"],
+ ~~~~~~~~~~~~~~~~~~~~~
Unexpected token 'mrkdwn_in": ["text"],
"color": "$COLOUR",
"text": "$MESSAGE",
}
]"' in expression or statement.
At C:\path-to-file-etc.ps1:53 char:11
+ "mrkdwn_in": ["text"],
+ ~
The hash literal was incomplete.At
C:\path-to-file-etc.ps1:53 char:11
+ "mrkdwn_in": ["text"],
+ ~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordEx
ception
+ FullyQualifiedErrorId : UnexpectedToken
Process exited with code 1
Process exited with code 1
Step Notify (PowerShell) failed
I have pretty much zero experience in Powershell. Also because this script has to be able to dropped onto a whole variety of boxes,I won't use any libraries or custom cmdlets or anything like that. Out of box methods or die.
Since you're using ConvertTo-Json anyway, it's simpler to construct the entire input as a (nested) custom object / hashtable, and let ConvertTo-Json handle all JSON formatting:
$Body = [pscustomobject] #{
attachments = , [pscustomobject] #{
mrkdwn_in = , 'text'
color = $Colour
text = $Message
}
}
$BodyJson = $Body | ConvertTo-Json -Depth 3
Note: You could substitute [ordered] for [pscustomobject] to use a hashtable with ordered keys instead; even omitting either would work, though seeing the entries in the resulting JSON in different order may be confusing.
Note the use of ,, the array-construction operator, to ensure that the attachments and mrkdwn_in entries are treated as arrays.
Additionally, since ConvertTo-Json only fully serializes to a default depth of 2 levels, -Depth 3 must be used to ensure that the value of entry mrkdwn_in is serialized as an array.
As for what you tried:
Your immediate problem (in addition to the missing } that Jeff Zeitlin points out in a comment on the question): You've neglected to escape the embedded " chars. in your multi-line string.
Therefore, as documentation topic Get-Help about_Quoting_Rules discusses, you can either use `" to embed double quotes inside "..." or use a here-string (#"<nl>....<n>"#).
Even with the syntax problems fixed, your code wouldn't work as intended, however, because ConvertTo-Json wouldn't preserve the pre-formatted JSON in the attachments entry and instead treat the string value as a string literal that needs escaping; here's a simple example:
#{
foo = "[
1,
2
]"
} | ConvertTo-Json
The above yields:
{
"foo": "[\n 1, \n 2 \n ]"
}

How use curl with spaces in json

When making a POST call with curl I'm having trouble getting the JSON data to be passed correctly. The string is cut off at the first whitespace/blank space.
My call looks like this, I'm executing through a powershell script that has two parameters. Using these parameters I concatenate the JSON that is used for the call.
I input for instance 854e-ae7686fbc75a 365 as parameters
# usage: powershell -file send.ps1 [apitoken] [interval]
param (
[string]$apitoken = "",
[int]$startInterval = "365"
)
$AUTHORIZATION = $apitoken
$STARTDATE = (Get-Date).AddDays(-$startInterval)
$ENDDATE = (Get-Date)
$JSON = '{\"ApiToken\":\"' + $AUTHORIZATION + '\",' +
'\"StartDate\":\"' + $STARTDATE + '\",' +
'\"EndDate\":\"' + $ENDDATE + '\",' +
'\"Type\":\"SalesExport\"' +
'}'
$CURLEXE = '..\bin\curl.exe'
$CurlArgument = '-X', 'POST',
'-m', 7200,
'-H', '"Content-Type:application/json"',
'-d', $JSON,
'https://my.server/export/sales'
& $CURLEXE #CurlArgument
The STARTDATE would then be the current date minus 365 days. ENDDATE would be the current date.
The problem is as soon as there is a blank space (or possibly any whitespace) in the JSON variable curl thinks that the JSON has ended and says that the JSON is unterminated. There is no difference if I surround the variable with single or double quotes.
Even if the whitespace is inside one of the two DateTime variables the problem occurs!
How can I format the JSON properly so that it can include whitespace?
Not a direct answer to your question, but since you're using PowerShell anyway: why not use the native Invoke-WebRequest instead of an external command?
$today = (Get-Date).Date
$uri = 'https://example.org/export/sales'
$headers = #{
'Content-Type' = 'application/json'
}
$data = #{
'ApiToken' = $apitoken
'StartDate' = $today.AddDays(-$startInterval).ToString('d')
'EndDate' = $today.ToString('d')
'Type' = 'SalesExport'
} | ConvertTo-Json
Invoke-WebRequest -Method Post -Headers $headers -Body $data -Uri $uri
Alternatively you can define the JSON data as a multiline string:
$startDate = $today.AddDays(-$startInterval).ToString('d')
$endDate = $today.ToString('d')
$data = #"
{
"ApiToken": "$apitoken",
"StartDate": "$startDate",
"EndDate": "$endDate",
"Type": "SalesExport"
}
"#

smartsheet api update rows error 1004. you are not authorized

I am trying to use powershell and the API to update a cell, but I get an error: 1004 You are not authorized to perform this action. I've elided the IDs . . .
I have a 30 day trial. Do I need to buy in to some level of support before I can use the API to do updates or what? The sheet is on my account and I am the owner. Is Write Sheets role not included in Owner?
I am using powershell 5.1 to make the http request – here is my script. It first gets the correct sheet ID, then puts the rows and columns I am interested in into hashes, and then tries to update the sheet, but although I am the owner, I am unauthorized.
# access account of jjj.mmm#ddd.com
$apiKey = "3ccfgk..."
$url = "https://api.smartsheet.com/2.0/sheets/"
$get_headers = #{"Authorization" = "Bearer " + $apiKey}
$put_headers = #{"Authorization" = "Bearer " + $apiKey, "Content-Type: application/json" }
# get all the sheets
$response = Invoke-RestMethod -Uri $url -Headers $get_headers
$rd = $response.data
$json = ConvertTo-Json $rd
# find the Forecast sheet
$sheet_id = $rd | Where {$_.name -eq "Forecast Intermediary Sheet"} | Select -ExpandProperty id
"Sheet ID: $sheet_id"
#get the Forecast sheet
$surl = "$url$sheet_id"
$surl
$response = Invoke-RestMethod -Uri $surl -Headers $get_headers
$response | Format-List
# iterate over the rows and get the active ones
$active_rows = #{}
foreach ($row in $response.rows) {
if ($row.rowNumber -ge 14) {
if ($row.cells[2].value -Match "Active") {
$active_rows.Add($row.cells[2].value, $row.id)
}
}
}
# get the col_ids by date
$date_cols = #{}
foreach ($c in $response.columns) {
if ($c.title -as [datetime]) {
$d = $c.title -replace "/20", "/"
$ds = [datetime]::parseexact($d, 'mm/dd/yy', $null)
$date_cols.Add($ds.Tostring("yyyy-mm-dd"), $c.id)
}
}
"UPDATE:"
$rowid = $active_rows["SSL PS Active"]
$colid = $date_cols["2017-11-01"]
"row: $rowid, col: $colid"
$json = '[{ "id": "'+$rowid+'", "cells": [{"columnId": "'+$colid+'","value": "23"} }]'
"JSON: $json"
$purl = "$surl/rows"
"PUT URL: $purl"
$r = Invoke-RestMethod -Method "PUT" -uri $purl -Headers $put_headers -Body $json
$r
Below is the output of the script. The error seems to indicate it is syntactically correct, but OWNER is insufficient permissions.
Sheet ID: 5724...
https://api.smartsheet.com/2.0/sheets/5724....
UPDATE:
row: 5668..., col: 5118...
JSON: [{ "id": "5668...", "cells": [{"columnId": "5118...","value": "23"} }]
PUT URL: https://api.smartsheet.com/2.0/sheets/5724.../rows
Invoke-RestMethod : {
"errorCode" : 1004,
"message" : "You are not authorized to perform this action.",
"refId" : "14a7o8lu9sfyj"
}
At \\winfiles\jmoore\powershell\ss_api.ps1:58 char:6
+ $r = Invoke-RestMethod -Method "PUT" -uri $purl -Headers $put_headers ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod],
WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodComand
Here is the meta data of my sheet
Sheet ID: 5724...
https://api.smartsheet.com/2.0/sheets/5724....
id : 5724...
name : Forecast Intermediary Sheet
version : 2
totalRowCount : 37
accessLevel : OWNER
effectiveAttachmentOptions : {GOOGLE_DRIVE, DROPBOX, ONEDRIVE, EGNYTE...}
ganttEnabled : False
dependenciesEnabled : False
resourceManagementEnabled : False
cellImageUploadEnabled : True
userSettings : #{criticalPathEnabled=False; displaySummaryTasks=True}
permalink : https://app.smartsheet.com/b/home?lx=LCTEj6F0xWNKWWxFUuLH0w
createdAt : 2017-11-22T22:34:51Z
modifiedAt : 2017-11-22T22:34:51Z
columns : {#{id=6985...; index=0; title=Column1; type=TEXT_NUMBER; primary=True;
validation=False; width=64}, #{id=1356...; index=1; title=Column2;
type=PICKLIST; options=System.Object[]; validation=False; width=64},
#{id=5859...; index=2; title=Column3; type=TEXT_NUMBER; validation=False;
....
You should be able to make API calls using an API Access Token that's owned by a trial Smartsheet account, and you can most certainly update a sheet that you own using an API Access Token you own.
I'd suspect that the 1004 error response is being caused by the fact that the contents of $put_headers isn't formatted properly. i.e., Smartsheet isn't able to accurately parse the headers you're attempting to specify in $put_headers in order to identify the Authorization header and read its value. When Smartsheet doesn't see the Authorization header in an inbound API request, it'll respond with the 1004 You are not authorized to perform this action. error response.
To troubleshoot this issue, I'd suggest that you use a tool like Fiddler to examine the outbound "Update Rows" request, paying special attention to what headers are present in the request. Then, if you discover that the Authorization header is absent from the request, you'll need to figure out how to specify multiple request headers in PowerShell, and update your code accordingly re how you're setting the value of $put_headers.
Update (adding PowerShell code):
I'm no PowerShell expert, but you might try replacing this line:
$put_headers = #{"Authorization" = "Bearer " + $apiKey, "Content-Type: application/json"}
With these lines instead:
$put_headers = #{}
$put_headers.Add("Authorization", "Bearer " + $apiKey)
$put_headers.Add("Content-Type", "application/json")
Thanks #kim-brandl
Or if you want to build the headers in a single command, you need to use syntax like this:
$put_headers = #{"Authorization" = "Bearer " + $apiKey; "Content-Type" = "application/json" }
Note also that your json payload has a few problems including:
Unmatched brackets
Extra square brackets on the outside (the body is an object, not an array)
Column and row ids must be numbers, not strings
I suggest you test the payload in a tool like postman first.