Powershell - Invoke-RestMethod - POST nested JSON - powershell

I'm trying to interact with an API to POST some data to a service we use, in order to add some email addresses to a policy. This will eventually take a list of email addresses and loop through it for each one, but for now I'm just trying to get it to work with a single address.
I'm using Powershell 7, I would say I'm intermediate with powershell, but this is my first foray into interacting with an API, using JSON, and using the Invoke-RESTMethod commandlet. The API reference for the service shows lots of code examples in other languages, just not PS!
Here's the problem I'm running in to. I'm trying to formulate a -Body statement that looks to be 3 elements instead of two. Here is what that example statement looks like in CURL:
{
"custodians": [
{
"emailId": "john.doe#someorg.com"
}
],
"action": "add"
}
The Action:Add part is fine, easy peasy. I'm trying to figure out how to correctly format the custodians part. I cant figure out how to do 3 elements instead of two. I've tried different combinations of {} [] and () to no avail:
"custodians" = ({"emailId" = "emailaddress#place.com"})
gives me an "The assignment expression is not valid" error
"custodians" = [{"emailId" = "emailaddress#place.com"}]
and
"custodians" = [#{"emailId" = "emailaddress#place.com"}]
give me an "Missing type name after '['." error.
These, as well as a few other combinations I've typed out, all showed formatting errors in VSCode so I already knew they wouldn't work, I'm just not sure why. I'm sure I just haven't cracked the right combination of #, {}, [] or () but I cant seem to find any info online (probably because I'm not using the right vocabulary or phrasing in my searches) that shows how to format with three elements.
If its relevant or helpful, here is a larger code sample of the whole query Im working on. Assume the Auth headers are fine (I can request my auth tokens and do GETs without issue so far):
$headers = #{
"Authorization" = $bearerAuthValue
"Accept" = "application/json"
"Content Type" = "application/json"
}
$body = #{
"custodians" = #(emailId = "emailaddress#place.com"),
"Action" = "add"
}
$uri = "https://thisisanaddress.com"
Invoke-RestMethod -Method 'POST' -Uri $uri -Headers $headers -Body $body

You are looking at having multiple emailids in custodians?
$body = #{
custodians = #(
#{emailId = 'emailaddress#place.com' }
#{emailId = 'emailaddress#place.com' }
#{emailId = 'emailaddress#place.com' }
)
Action = 'add'
}
Output:
$body | ConvertTo-Json
{
"Action": "add",
"custodians": [
{
"emailId": "emailaddress#place.com"
},
{
"emailId": "emailaddress#place.com"
},
{
"emailId": "emailaddress#place.com"
}
]
}

Related

Use Graph API and PowerShell to add item to SharePoint list to 'Person or Group' with multiple values

I'm having hard time trying to accomplish adding new entries to SharePoint list which one of fields type it's 'People or Group' with multiple selection allowed. I'm able to add entry without People field filled, but when I'm trying to somehow compose JSON having people included, I get error 'invalid request' or just field with people is not filled. I know that I have to use some SharePoint ID for user but somehow I'm unable to add it anyway...
Code I'm using:
$apiUrl = "$($resource)/v1.0/sites/$SharepointSiteId/lists/$SharepointListId/items"
$paramsItemToAdd = #{
fields = #{
Title = "Test"
field_1 = "1111"
Field2= "Some Text"
Name = "Name"
Type = "22222"
Date = (get-date).ToString("yyyy-MM-dd")
Ownership = #(
,#{
lookupid=25;
lookupvalue="User2ndName, Name";
email="Name.User2ndName#company.com"
}
)
}
}
$payload = ConvertTo-Json $paramsItemToAdd -Depth 5
$Data = Invoke-RestMethod -Headers #{Authorization = "Bearer $($TokenResponse.access_token)"} -ContentType "application/json" -Uri $apiUrl -Method Post -Body $payload
My JSON looks like this:
{
"fields": {
"Title": "Test",
"Field2": "Some Text",
"Type ": "22222",
"Name": "Name",
"Ownership": [
{
"email": "Name.User2ndName#company.com",
"lookupvalue": "User2ndName, Name",
"lookupid": 25
}
],
"field_1": "11111",
"Date": "2022-11-25"
}
}
I added few people to one of the field and done GET method to see how they are defined when they are added manually:
Ownership : {#{LookupId=43; LookupValue=U1_2ndName, U1_Name; Email=U1_Name.U1_2ndName#company.com},
#{LookupId=25; LookupValue=U2_2ndName, U2_Name; Email=U2_Name.U2_2ndName#company.com}}
But this really doesn't gives me any idea how should I change my code to get working one eventually. Tried many different combinations of above like with just LookupId as value, or removing email and lookupvalue from field - still nothing. I spent so much time trying to achieve this, I saw many different articles but I'm still unable to see what I'm doing wrong. Maybe I'm missing something obvious...
Just want to highlight - I know that it must be something wrong with way how I'm defining 'Ownership' field, because when I removed that completely from JSON, I'm able to add entries to SharePoint list without errors. So it must be something around how 'Ownership' field should be defined.

Append data to Google Spreadsheet using API and Invoke-RestMethod

I'm doing my first steps with Google API. Currently I have a need to add some data to existing spreadsheet. For some purposes I need to use Invoke-RestMethod instead of any already created modules.
For a test purpose I wanted to add single entry for now. I'm using this code:
$requestUri = "https://sheets.googleapis.com/v4/spreadsheets/$($SpreadsheetID)/values/$("SheetName!A1:F195"):append
$body = #{
range = 'SheetName!A1:F195';
values = #{
'Field1' = 'Test';
'Field2' = 'Test';
'Field3' = 'Test';
'Field4' = 'Test';
'Field5' = 'Test';
'Link' = 'https://test.com'
};
};
$test = ConvertTo-Json -InputObject $body
$response = Invoke-RestMethod -Headers #{Authorization = "Bearer $accessToken"} -Uri $requestUri -Method POST -ContentType 'application/json' -Body $test
As a response, I'm receiving this error 400 - Bad request.
Sheet already containing some data. Field names in my $body variable are exactly same like in spreadsheet. A196 is first empty cell in my spreadsheet. I was kind of expecting that it will just enter those values into that row basically.
I still didn't know what is wrong, so I decided to use Google API Playground. When I filled everything, request looks like this:
POST /v4/spreadsheets/<SpreadsheetID>/values/SheetName!A1:F195:append HTTP/1.1
Host: sheets.googleapis.com
Content-length: 385
Content-type: application/json
Authorization: Bearer <code>
{
'range': 'SheetName!A1:F195',
'values': {
'Field1':'Test',
'Field2':'Test',
'Field3':'Test',
'Field4':'Test',
'Field5':'Test',
'Link':'https://test.com'
}
}
As a response I'm getting this:
"code": 400,
"details": [
{
"fieldViolations": [
{
"field": "data.values",
"description": "Invalid JSON payload received. Unknown name \"Field1\" at 'data.values': Cannot find field."
},
{
"field": "data.values",
"description": "Invalid JSON payload received. Unknown name \"Field1\" at 'data.values': Cannot find field."
},
{
"field": "data.values",
"description": "Invalid JSON payload received. Unknown name \"Field1\" at 'data.values': Cannot find field."
},
{
"field": "data.values",
"description": "Invalid JSON payload received. Unknown name \"Field1\" at 'data.values': Cannot find field."
},
{
"field": "data.values",
"description": "Invalid JSON payload received. Unknown name \"Field1\" at 'data.values': Cannot find field."
},
{
"field": "data.values",
"description": "Invalid JSON payload received. Unknown name \"Link\" at 'data.values': Cannot find field."
}
],
"#type": "type.googleapis.com/google.rpc.BadRequest"
}
]
But I really have not clue what is wrong here. I'm fighting with this some time already, but I'm out of ideas ATM. Appreciate if somebody could point me to correct route.
#EDIT
I find information that while appending, range should cover existing table. However chaning range to A1:F195 gives exactly same error.
Finally! I found some solution by trying different things. Maybe it's not best one, but definitelly one that actually works. The case was that actually $body in specific form. One that worked (at least not displaying same error) was this:
{
'range': 'SheetName!A1:F195',
'values': [
[
'Test',
'Test',
'Test',
'Test',
'Test',
'https://test.com'
]
]
}
So putting values in double square brackets and just values. Using this I was able to get another error in API Playground:
{
"error": {
"status": "INVALID_ARGUMENT",
"message": "'valueInputOption' is required but not specified",
"code": 400
}
}
So after adding ?valueInputOption=USER_ENTERED to $requestUri I was at last able to have row added.
Working code in Powershell looks like this:
$requestUri = "https://sheets.googleapis.com/v4/spreadsheets/$($SpreadsheetID)/values/$("SheetName!A:F"):append?valueInputOption=USER_ENTERED"
$body = #"
{
'range': 'SheetName!A:F',
'values': [
[
'Test',
'Test',
'Test',
'Test',
'Test',
'https://test.com'
]
]
}
"#
$response = Invoke-RestMethod -Headers #{Authorization = "Bearer $accessToken"} -Uri $requestUri -Method POST -ContentType 'application/json' -Body $body
If somebody know other way how $body could be defined to be acceptable by API - I would gladly see this!
#EDIT
Also I have found reason why it wasn't work in first approach. Thanks to more web searching I realized that values in JSON according to documentation is actually array of arrays (which is basically what I "hardcoded" in above example!).
"Correct way" of doing it looks like this:
$requestUri = "https://sheets.googleapis.com/v4/spreadsheets/$($SpreadsheetID)/values/$("SheetName!A:F"):append?valueInputOption=USER_ENTERED"
$body = #{
range = 'SheetName!A:F';
values = #(
,#(
'Test',
'Test',
'Test',
'Test',
'Test',
'https://test.com'
);
);
};
$test = ConvertTo-Json -InputObject $body
$response = Invoke-RestMethod -Headers #{Authorization = "Bearer $accessToken"} -Uri $requestUri -Method POST -ContentType 'application/json' -Body $test

Purview API Create glossary terms

I am trying to reference the following page
https://learn.microsoft.com/en-us/rest/api/purview/catalogdataplane/glossary/create-glossary-term?tabs=HTTP
$glossarytermcreateendpoint = 'https://pv-chn-asiabi-sea-dev.purview.azure.com/catalog/api/atlas/v2/glossary'
$Glossaryterm = #{
qualifiedName = "sample"
name= "sample"
longDescription = "sample"
shortDescription = "sample"
status = "Draft"
}
$jsonbody = $Glossaryterm | ConvertTo-Json
$Responsepost = Invoke-WebRequest -Uri $glossarytermcreateendpoint -Method Post -headers $headers -Body $jsonbody -ContentType 'application/json'
I am ok to get status 200
however in the Purview workspace , I can't find the new term is created
Questions
How to make it works? Able to search ?
How to create a glossary term under specific term template .?
$glossarytermcreateendpoint = 'https://pv-chn-asiabi-sea-dev.purview.azure.com/catalog/api/atlas/v2/glossary'
From your script, I noticed that the API you called is to create a glossary, not a term, so you can't find the new term you created.
The right API to create a term is to append /term to the end of your current API:
$glossarytermcreateendpoint = 'https://pv-chn-asiabi-sea-dev.purview.azure.com/catalog/api/atlas/v2/glossary/term'
Let me know if this solves your problem.

Syntax for groovy to duplicate PowerShell Invoke-RestMethod

I'm trying to duplicate the PowerShell Invoke-RestMethod to something similar in Groovy (groovy is standard we use in our coded pipeline).
I've done a lot of searching without success. I was wondering if I can get some help or suggestions on a possible alternative if there isn't a similar call?
The 3 three lines of PowerShell I'm trying to duplicate in Groovy are:
$tokenrequest = #{ "grant_type" = "password"; "username" = "adminuser"; "password" = "adminpassword" }
$token = Invoke-RestMethod -Uri "http://abcd.com/executionmanager/api/Token" -ContentType application/x-www-form-urlencoded -Headers #{ Authorization = ("OAuth2")} -Method POST -Body $tokenrequest
$token = $token.access_token
there are a lot of libraries to do that
the following one quite unknown
//download dependency from maven repository
#Grab(group='acme.groovy', module='acmehttp', version='20180403')
import groovyx.acme.net.AcmeHTTP
def ctx = AcmeHTTP.post(
url: "https://httpbin.org/post", //the url could be used for tests
//define body as map as soon as we are sending it as www-form
body: [ "grant_type": "password", "username": "adminuser", "password": "adminpassword" ],
headers:[
"content-type":"application/x-www-form-urlencoded",
"Authorization": "OAuth2"
],
)
assert ctx.response.code==200
//the url https://httpbin.org/post always returns json
assert ctx.response.contentType =~ "/json"
assert ctx.response.body instanceof Map
println ctx.response.body
//i guess your token should be accessible like this:
//println ctx.response.body.access_token

Corrupt Documents in new DocuSign envelope

I'm doing a POC to demonstrate DocuSign programmatically creating and routing an envelope containing a simple document. I'm using PowerShell and the JSON API. Login and the Create Envelope work without complaint, but the resulting Word doc routed to me for signature contains gibberish. I believe I have the base64 encoding and headers right. Any thoughts about what I'm doing wrong?
The entire POC is pasted below. I've just removed the ID, Password, Integrator Key, etc. Thanks!
function boundry {
[System.Guid]::NewGuid().ToString()
}
###this is the corrected code###
function encodeFile {
param ([string]$fileName)
[System.Convert]::ToBase64String([IO.File]::ReadAllBytes((Resolve-Path $fileName).ProviderPath))
}
function logonParams {
[string] $userName = 'DocuSign user name'
[string] $password = 'DocuSign password'
[string] $integratorKey = 'DocuSign Integrator Key'
#"
{
"Username" : "$userName",
"Password" : "$password",
"IntegratorKey" : "$integratorKey"
}
"#
}
function logon {
[string] $loginURL = 'https://demo.docusign.net/restapi/v2/login_information'
$headers =
#{
"X-DocuSign-Authentication"=$(logonParams);
"accept"="application/json";
"content-type"="application/json";
}
$r = Invoke-WebRequest -uri $loginURL -headers $headers -method GET
$responseInfo = $r.content | ConvertFrom-Json
$baseURL = $responseInfo.loginAccounts.baseURL
#return the base URL for the next call
$baseURL
}
function createEnvelope {
param ([string]$file1,
[string]$baseURL
)
[string]$boundry = boundry
$headers =
#{
"X-DocuSign-Authentication"=$(logonParams);
"accept"="application/json";
"content-type"="multipart/form-data; boundary=$boundry";
}
[string]$formData = #"
--$boundry
Content-Type: application/json
{
"status":"sent",
"emailBlurb":"Please sign.",
"emailSubject": "Contract $(date)",
"documents": [{
"name": "$file1",
"documentId":"1",
"order":"1"
}],
"recipients": {
"signers" : [{
"email": "recipient#somecompany.com",
"name": "Recipient Name",
"recipientId":"1",
}]
}
}
--$boundry
Content-Type: application/msword
Content-Transfer-Encoding: base64
Content-Disposition: file; filename="$file1";documentid=1
$(encodeFile $file1)
--$boundry--
"#
$envelopeURL = "$baseURL/envelopes"
Invoke-WebRequest -uri $envelopeURL -headers $headers -body $formData -method POST
}
$baseURL = logon
createEnvelope "test.doc" $baseURL
Try changing your Content-Type header value. I'm not sure if application/msword works here, I think the proper mime-type for .docx is
application/vnd.openxmlformats-officedocument.wordprocessingml.document
See this previous SO post for a more complete list of mime-types:
What is a correct mime type for docx, pptx etc?
I solved it with assistance from the DocuSign support team. You can enable server side logging in DocuSign which is very helpful. From the old UI (not available in new UI as of June '15) choose Preferences from the dropdown next to your ID/photo. Then select Permissions under Member Options on the left. Check "Enable API Request Logging." After you run your test, the Download API Request Logs button becomes active.
It was pretty clear from the logs that my encoding was wrong. Here's the correct version:
function encodeFile {
param ([string]$fileName)
[System.Convert]::ToBase64String([IO.File]::ReadAllBytes((Resolve-Path $fileName).ProviderPath))
}
I've updated this in the original code with the question so feel free to use it.