How to send a BatchRequest to google API with PowerShell - powershell

I am working with the Google API to automate tasks for distribution groups.
I understand how to send individual requests, but I want to be able to send BatchRequests, and this seems to be a challenge as the format is not consistent like for example all JSON so i can convert the body once and make my request.
I am reading through the documentation from Google and I cant figure out how to achieve that with Powershell.
https://developers.google.com/admin-sdk/directory/v1/guides/batch
I see there is already a .NET library for that, but I struggle to understand how to use it.
https://googleapis.dev/dotnet/Google.Apis/latest/api/Google.Apis.Requests.BatchRequest.html
I cant understand how I can do nested HTTP request inside a HTTP request with PowerShell.
I have no idea how I can even write it, so here is a draft that I am thinking over, and I will be editing throughout this post.
$batchUrl = 'https://www.googleapis.com/batch/admin/directory_v1'
$header = #{
"Content-type" = "multipart/mixed; boundary=batch_test"
"Authorization" = "Bearer <token>"
}
$psBody = [ordered] #{
method = "POST /admin/directory/v1/groups/vtest#gs.vasil.com/members"
"content-type" = "application/json"
email = "vas#gs.vasil.com"
role = "MEMBER"
}
$JSONbody = $psBody | ConvertTo-Json
Invoke-RestMethod -Uri $batchUrl -Method POST -Headers $header -Body $JSONbody
I really want to understand that OData structure, any documentation/guide reference are welcome.

I figured it out, the format needed was literally what Google posted, therefore I just had to adjust the body to what I want.
The example bellow is just Prove of Concept and would need some functions to automate the population from an Array.
$URL = 'https://www.googleapis.com/batch/admin/directory_v1'
$boundary = [System.Guid]::NewGuid().ToString();
$LF = "`r`n";
$header = #{
"Content-type" = "multipart/mixed; boundary=$boundary"
"Authorization" = "Bearer <token>"
}
$request1 = [pscustomobject][ordered]#{
email = "vas1#gs.vasil.com"
role = "MEMBER"
} | ConvertTo-Json
$request2 = [pscustomobject][ordered]#{
email = "vas2#gs.vasil.com"
role = "MEMBER"
} | ConvertTo-Json
$nestedBody = (
"--$boundary",
"Content-Type: application/http$LF",
"POST /admin/directory/v1/groups/vtest#gs.vasil.com/members",
"Content-Type: application/json$LF",
"$($request1)$LF",
"--$boundary--",
"Content-Type: application/http$LF",
"POST /admin/directory/v1/groups/vtest#gs.vasil.com/members",
"Content-Type: application/json$LF",
"$($request2)$LF",
"--$boundary--"
) -join $LF
$sendRequest = #{
Uri = $URL
header = $header
Method = "Post"
Body = $nestedBody
}
Invoke-RestMethod #sendRequest
The way I got to that conclusions was by installing the Fiddler Proxy and see how my request look like in practice, check screenshot bellow.

Related

Correct body contents to create a mobileAppContentFile resource using the MsGraph REST API?

I'm attempting to automate the update process for a Line-of-Business app in Intune.
Here is my current code:
function New-AppContentFile {
param(
[Parameter(Mandatory=$true)][System.String]$AppId,
[Parameter(Mandatory=$true)][System.String]$AppContentId
)
$Uri = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/8dda54ec-992a-478d-984a-260cd59c1c33/microsoft.graph.mobileLobApp/contentVersions/1/files"
$script:ReqBody = [ordered]#{
"#odata.type" = "#microsoft.graph.mobileAppContentFile"
"name" = "google.msi"
"size" = 4
"sizeEncrypted" = 13
"manifest" = $null
"isCommitted" = $false
} | ConvertTo-Json
$header = #{
Authorization = "Bearer $($token.AccessToken)"
"content-length" = $ReqBody.Length
"Accept" = "application/json"
}
Invoke-RestMethod -Uri $Uri -Headers $header -Method Post -Body $ReqBody -ContentType "application/json"
}
A few things that I've noticed when testing, the endpoint listed on the REST API is most likely incorrect. If I use a GET method on the following URL "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/8dda54ec-992a-478d-984a-260cd59c1c33/microsoft.graph.mobileLobApp/contentVersions/1/files" I will get a return. If I use the one mentioned in the REST API it returns a (400) Bad Request.
I've also tested numerous variations of the body to include all or some of the properties listed in the example request. Referencing the first of the two references listed below (Line 617), the body in theory should work as the following:
$ReqBody = [ordered]#{
"#odata.type" = "#microsoft.graph.mobileAppContentFile"
"name" = "google.msi"
"size" = 4
"sizeEncrypted" = 13
"manifest" = $null
}
In the example request, the "azureStorageUri" property is specified. In my case I can't specify that, this POST should generate the azureStorageUri so that I can upload the file to that storage endpoint.
I've used these two sources as a reference:
https://github.com/microsoftgraph/powershell-intune-samples/blob/master/LOB_Application/Application_LOB_Add.ps1
https://github.com/MSEndpointMgr/IntuneWin32App/blob/master/Public/Add-IntuneWin32App.ps1
And the following documentation from the Microsoft Graph REST API
https://learn.microsoft.com/en-us/graph/api/intune-apps-mobileappcontentfile-create?view=graph-rest-beta
I should also note that I have tested this with both the BETA and v1.0 of the MsGraph REST API.

Jira Rest Api in Powershell

Could you help me?
I am trying to create an issue in Jira using the Powershell Invoke-WebRequest cmdlet. And I am getting 400 Bad Request error.
I was able to send a successful request using Postman, so I made sure the body syntax is correct and I have sufficient rights.
My code:
$body = #{
"fields" = #{
"project"=
#{
"key"= "ProjectKey"
}
"summary"= "Test"
"description"= "Test"
"issuetype" =#{
"id"= "10705"
}
"priority"= #{
"id"= "18"
}
"reporter"= #{"name"= "MyName"}
}
}
$Headers = #{
Authorization = "Basic QWxla0Zblablablablablablabla" #I took it from Postman
}
$restapiuri = "https://jira.domain.com/rest/api/2/issue"
Invoke-RestMethod -Uri $restapiuri -ContentType "application/json" -Body $body -Method POST -Headers $Headers
for example, I can successfully execute
Invoke-RestMethod "https://jira.domain.com/rest/api/2/issue/createmeta" -Headers $Headers
I've already spent a lot of time-solving this problem, but still can't create an issue.
Any help, please ๐Ÿ™๐Ÿ™๐Ÿ™
For basic authentication with Jira SERVER, the credentials need to be supplied within the header in Base64 encoding, which you need to do before supplying it via the Powershell Invoke-WebRequest method. Here's how to do it:
$username = "The username here"
$password = "The password or token here"
# Convert the username + password into a Base64 encoded hash for basic authentication
$pair = "${username}:${password}"
$bytes = [System.Text.Encoding]::ASCII.GetBytes($pair)
$base64 = [System.Convert]::ToBase64String($bytes)
$headers = #{ Authorization = "Basic $base64" }
Next, in PowerShell, if you build the body of the request as a table, like you've shown, don't don't need to wrap the table elements with inverted commas, just leave them as is, but you do need to convert the table to JSON format before submitting it, like this:
$body = #{
fields = #{
project = #{
key = "ProjectKey"
}
issuetype = #{
id = "10705" # Or refer to the Issue type by its name eg. name = "Story"
}
summary = "Test"
}
}
# Convert the body to JSON format
$body = $body | ConvertTo-Json -Depth 50
I'm assuming your $Uri string contains an actual URL to a Jira Server, not the example 'jira.domain.com'
Start with a simple request in the body, like the one I've shown, that contains only the minimum required to create the Issue, which will check your basic code is working before making the request more complex.

jira rest-api attach file to issue using powershell

I want to attach a file to an jira issue , i am able to do it with postman, but have tried several ways without break through.
my code looks like this
function ConvertTo-Base64($string) {
$bytes = [System.Text.Encoding]::UTF8.GetBytes($string);
$encoded = [System.Convert]::ToBase64String($bytes);
return $encoded;
}
function Get-HttpBasicHeader([string]$username, [string]$password, $Headers = #{}) {
$b64 = ConvertTo-Base64 "$($username):$($Password)"
$Headers["Authorization"] = "Basic $b64"
$Headers["X-Atlassian-Token"] = "nocheck"
return $Headers
}
$restapiuri = "https://xxxx.xxxx.com/rest/api//2/issue/test-8442/attachments"
$headers = Get-HttpBasicHeader "xxxxxx" "xxxxxxxx"
$myfile = "C:\TEMP\out.txt"
$fileBytes = [System.IO.File]::ReadAllBytes($myfile);
$fileEnc = [System.Text.Encoding]::GetEncoding('UTF-8').GetString($fileBytes);
$boundary = [guid]::NewGuid().ToString()
$LF = "`r`n";
$body = '(
"--$boundary",
"Content-Disposition: form-data; name=`"fil`"; filename=`"out.txt`"",
"Content-Type: application/octet-stream$LF",
$fileEnc,
"--$boundary--$LF"
) -join $LF
'
Invoke-RestMethod -uri $restapiuri -Headers $headers -Method POST -ContentType "multipart/form-data; boundary=`"$boundary`"" -Body $body
In postman i am doing a post request with
Authorization
Basic Auth
headers
header X-Atlassian-Token = o-check
Content-Type = multipart/form-data
Body form-data
key value
file filepath
The powershell doesn't return any errors, but it does not attach any file either
I have tried several examples without luck so if any of you have any ideas on have to do this in powershell i would be glad.
I am on powershell version
Major Minor Build Revision
5 1 14409 1018
I found the following that looks to be a good fit for this scenario.
function Upload-JiraFile($jiraTicket, $filepath, $authorization)
{
$wc = new-object System.Net.WebClient
$wc.Headers.Add("Authorization", $authorization)
$wc.Headers.Add("X-Atlassian-Token", "nocheck")
$wc.UploadFile("$URIPath/issue/$jiraTicket/attachments", $filepath)
}
Here is how you could use it with the bare minimal modification to the code you have today:
$URIPath = "https://xxxx.xxxx.com/rest/api/2"
Upload-JiraFile -JiraTicket test-8442 -FilePath c:\temp\MyJpg.jpg `
-Authorization $Headers["Authorization"]
Excerpted from this thread.

Empty body content when doing a PUT request to Wiki pages

Azure DevOps Services REST API 5.0 - Wiki Pages
I'm trying to dynamically update an Azure DevOps Wiki page with the newest commits on top whenever a change is introduced to a repository.
When I try to PUT the commit history into a Wiki page the content field within the body is empty.
The request is done via Powershell and looks like this:
function postToWiki($Commits) {
$wikiUrl = "https://dev.azure.com/$organization/$project/_apis/wiki/wikis/Ekonomiredovisning.wiki/pages?path=MyWikiPage&api-version=5.0"
$Etag = getWikiPageVersion
$headers = #{
Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN";
'If-Match' = $Etag}
$body = #{ content = $Commits }
$json = $body | ConvertTo-Json
Invoke-WebRequest -Uri $wikiUrl -Headers $headers -Body $json -ContentType "application/json" -Method Put
}
Additional information that can be useful:
It works when I send simpler strings like "Hello"
The JSON being sent is valid according to https://jsonlint.com/.
The $Commit variable is pretty huge, up to 6000 lines.
I have the same problem today. My body contained special HTML caracters wich are not escaped in the request.
Try to escape the special chars (รฉรจ...).
In PowerShell, you can use the following assembly:
Add-Type -AssemblyName System.Web
$encodedBody = [System.Web.HttpUtility]::HtmlEncode($Commits)
Result:
# Construct the wiki REST URI
# $uri = $WikiUri +$WikiPath + $($contentPackage.version)
$uri = "$($env:WikiUri)$($contentPackage)&api-version=5.0"
# Encode and convert to json
Add-Type -AssemblyName System.Web
$encodedContent = [System.Web.HttpUtility]::HtmlEncode($content)
$data = #{ Content=$encodedContent; } | ConvertTo-Json;
# Set Request
$params = #{uri = "$($uri)";
Method = 'PUT';
Headers = $header;
ContentType = "application/json";
Body = $data;
}
# Call
Invoke-WebRequest #params

Error 400 bad request when creating a group calendar event in powershell with Microsoft Graph API

I'm trying to create an event in a calendar in an Office 365 group via powershell.
This is my first experience to do this type of programming so sorry if my question will be very basic :-)
First, I created a simple json file (calendar.json)
{
"start":
{
"dateTime":"2017-03-12T17:00:00.0000000",
"timeZone":"UTC"
},
"end":
{
"dateTime":"2017-03-12T17:30:00.0000000",
"timeZone":"UTC"
},
"responseStatus": {
"response": "None"
},
"iCalUId": "null",
"isReminderOn": false,
"subject": "Test Event created from API"
}
Then I create the event with these steps:
Use a tested powershell function that give me the token
Add header with this code:
$headers = #{}
$headers.Add('Authorization','Bearer ' + $token.AccessToken)
$headers.Add('Content-Type',"application/json")
Because I'm starting now, I convert the json file in an object and then the object in json (I know, it's quite stupid, but I've done so beacuse I have no knowledge of json and how convert without error in powershell code)
$json = ConvertFrom-Json -InputObject (Gc 'C:\Users\mmangiante\OneDrive - Interactive Media S.p.A\Office 365\calendar.json'-Raw)
$body = ConvertTo-Json $json
Call the Invoke-RestMethod
response = Invoke-RestMethod 'https://graph.microsoft.com/v1.0/groups/768afb0c-bafd-4272-b855-6b317a3a9953/calendar/events' -Method Post -Headers $headers -Body $json
What is returned is a 400 bad request.
It's the same error of Sending Microsoft Graph request events returns 400
Given the answer given to that question I modified my code to return the error:
try{$restp=Invoke-RestMethod 'https://graph.microsoft.com/v1.0/groups/768afb0c-bafd-4272-b855-6b317a3a9953/calendar/events' -Method Post -Headers $headers -Body $json
} catch {$err=$_}
$err
like suggested in How do I get the body of a web request that returned 400 Bad Request from Invoke-RestMethod but I found nothing of interest.
The only thing that I found is that, at the time of writing, the Invoke-RestMethod doesn't return the full response as in this https://github.com/PowerShell/PowerShell/issues/2193
I suppose my json is not "well formed", but I don't know why.
Does anyone have a suggestion?
This formatting has worked for me in the past. Let me know if this resolves your issues:
$headers = #{
"Authorization" = ("Bearer {0}" -f $token);
"Content-Type" = "application/json";
}
$body = #{
Name1 = 'Value1';
Name2 = 'Value2';
}
$bodyJSON = $body | ConvertTo-Json
Invoke-RestMethod -Method Post -Uri <API HERE> -Headers $headers -Body $bodyJSON -OutFile $output
Thanks Shawn,
I tried your suggestion but without luck; I have done other test with 2 other api and found discordant results.
The first test is to use the graph api related to contact, creating a contact as in https://graph.microsoft.io/en-us/docs/api-reference/v1.0/api/user_post_contacts
I created the powershell representation of the sample json:
$contactbody = #{
givenName = 'Ciccio';
surname = 'Patacca';
emailAddresses = #(
#{
address = 'cicciopatacca#cicciociccio.com'
name = 'Ciccio Patacca'
}
)
businessPhones = (
'+39 555 123 4567'
)
}
and then converted in json
$contactbody = $contactbody | ConvertTo-Json
I retrieved from Azure Portal the object ID related to my user in my company and so I call the rest method:
$response = Invoke-RestMethod 'https://graph.microsoft.com/v1.0/users/e37d2dbe-bdaf-4098-9bb0-03be8c653f7d/contact' -Method Post -Headers $headers -Body $contactbody
The final result is a 400 bad request error.
So, I tried another example and reused some code retrieved from a google search.
This time I copied the powershell json representation as in the sample and converted:
$body = #{"displayName"="ps-blog"; "mailEnabled"=$false; "groupTypes"=#("Unified"); "securityEnabled"=$false; "mailNickname"="ps1" } | ConvertTo-Json
and, as stated in https://graph.microsoft.io/en-us/docs/api-reference/v1.0/api/group_post_groups I called the rest method
$response = Invoke-RestMethod 'https://graph.microsoft.com/v1.0/groups' -Method Post -Headers $headers -Body $body
and this worked.
So I thought that the previously powershell representations of the sample with the error were not correctly formed (even if when I printed it they are equal to the samples on the graph api); for test, I rewritten the last powershell of the body as this:
$body = #{
displayName = 'TestGraphGroup';
mailEnabled = $true;
groupTypes = #('Unified')
securityEnabled = $false;
mailNickname = 'TestGraphGroup'
}
$body = $body | ConvertTo-Json
and invoked last method: it worked again.
So, where I'm doing wrong?
A new SDK was released that makes this easier.
Checkout instructions on how to use it here