Forum Discussion
Add additional Header Information to Batch Payload
I'm looking for a way to add additional header information to a Batch payload. In two of my Batch payload I'm wanting a @odata.count from the results but when I add Add("ConsistencyLevel", 'eventual') to the Header for my Invoke-RestMethod -method POST the command fails "(400) Bad request"
##################################################
$PayloadRequest = @{
id = $requestID
method = "GET"
url = "/groups/$GrpInfoID/members?`$count=true"
}
$BatchPayloadRequests += $PayloadRequest
##################################################
Question: How can I get a @odata.count from a payload in a $Batch Request
Thank You,
-Larry
2 Replies
- rbrbrCopper Contributor
Hi EntilZha, I'm facing a similar issue: instead of $count my batched requests use $select and I get the same 400 error response. I looked the documentation extensively and couldn't find anything about. The single request I'm batching have the "ConsistencyLevel", 'eventual' Header but it still fails. I also tried to add the header to the batched request, no success.
Anybody found a solution? TY
- EntilZhaIron Contributor
Below is an extract of what I have batching my requests and the header function i use to execute the batch request. I use two self sign certificate one for read-only Header and the other certificate for Read Write instead of using a Client Secret.
As to the Cert location, I install the cert under my and the service account Current User, for Task Scheduler to work service account. Also, within the script I check the date of the cert on each run, and the script will send an Email when cert date is with 20, 10, 5, and 1 days from expiration. On expiration the script will not run until cert is update.
Neither of these headers have "ConsistencyLevel" added to the header.
The only issue I have right now, is when I have a group with over 1000 members it only reports 999. When I have time I look into finding the solution.
Hope this information helps.
-Larry
##################HEADER FUNCTION########################
Function HeaderToken-RW
{
$TenantName = "XXXXXXXXXXXXXXXXXXXXXXXX"
$AppId = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
#$Certificate = Get-Item 'Cert:\CurrentUser\My\XXXXXXXXX THUMBPRINT xxxxxxxxxxxxxxxxxxxxxxx'
$Certificate = Get-Item "Cert:\CurrentUser\My\$Global:ThumbPrint"
$Scope = "https://graph.microsoft.com/.default"
# Create base64 hash of certificate
$CertificateBase64Hash = [System.Convert]::ToBase64String($Certificate.GetCertHash())
# Create JWT timestamp for expiration
$StartDate = (Get-Date "1970-01-01T00:00:00Z" ).ToUniversalTime()
#$StartDate = (Get-Date).ToUniversalTime()
$JWTExpirationTimeSpan = (New-TimeSpan -Start $StartDate -End (Get-Date).ToUniversalTime().AddMinutes(2)).TotalSeconds
$JWTExpiration = [math]::Round($JWTExpirationTimeSpan,0)
# Create JWT validity start timestamp
$NotBeforeExpirationTimeSpan = (New-TimeSpan -Start $StartDate -End ((Get-Date).ToUniversalTime())).TotalSeconds
$NotBefore = [math]::Round($NotBeforeExpirationTimeSpan,0)
# Create JWT header
$JWTHeader = @{
alg = "RS256"
typ = "JWT"
# Use the CertificateBase64Hash and replace/strip to match web encoding of base64
x5t = $CertificateBase64Hash -replace '\+','-' -replace '/','_' -replace '='
}
# Create JWT payload
$JWTPayLoad = @{
# What endpoint is allowed to use this JWT
aud = "https://login.microsoftonline.com/$TenantName/oauth2/token"
# Expiration timestamp
exp = $JWTExpiration
# Issuer = your application
iss = $AppId
# JWT ID: random guid
jti = [guid]::NewGuid()
# Not to be used before
nbf = $NotBefore
# JWT Subject
sub = $AppId
}
# Convert header and payload to base64
$JWTHeaderToByte = [System.Text.Encoding]::UTF8.GetBytes(($JWTHeader | ConvertTo-Json))
$EncodedHeader = [System.Convert]::ToBase64String($JWTHeaderToByte)
$JWTPayLoadToByte = [System.Text.Encoding]::UTF8.GetBytes(($JWTPayload | ConvertTo-Json))
$EncodedPayload = [System.Convert]::ToBase64String($JWTPayLoadToByte)
# Join header and Payload with "." to create a valid (unsigned) JWT
$JWT = $EncodedHeader + "." + $EncodedPayload
# Get the private key object of your certificate
$PrivateKey = ([System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($Certificate))
# Define RSA signature and hashing algorithm
$RSAPadding = [Security.Cryptography.RSASignaturePadding]::Pkcs1
$HashAlgorithm = [Security.Cryptography.HashAlgorithmName]::SHA256
# Create a signature of the JWT
$Signature = [Convert]::ToBase64String(
$PrivateKey.SignData([System.Text.Encoding]::UTF8.GetBytes($JWT),$HashAlgorithm,$RSAPadding)
) -replace '\+','-' -replace '/','_' -replace '='
# Join the signature to the JWT with "."
$JWT = $JWT + "." + $Signature
# Create a hash with body parameters
$Body = @{
client_id = $AppId
client_assertion = $JWT
client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
scope = $Scope
grant_type = "client_credentials"
}
$Url = "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token"
# Use the self-generated JWT as Authorization
$Header = @{
Authorization = "Bearer $JWT"
}
# Splat the parameters for Invoke-Restmethod for cleaner code
$PostSplat = @{
ContentType = 'application/x-www-form-urlencoded'
Method = 'POST'
Body = $Body
Uri = $Url
Headers = $Header
}
$Request = Invoke-RestMethod @PostSplat
# View access_token
# $Request.access_token
$RWHeader = @{Authorization = "$($Request.token_type) $($Request.access_token)"}
Return $RWHeader
}############### SCRIPT EXTRACTION ####################
########################################################
IF($GrpInfoCreateDate -gt '2021-07-31')
{
##################################################
$myRequest1 = @{
id = $requestID
method = "GET"
url = "/groups/$GrpInfoID/members?`$count=true"
}
$myBatchRequests += $myRequest1
##################################################
$requestID ++
$myRequest2 = @{
id = $requestID
method = "GET"
url = "/groups/$GrpInfoID/owners?`$count=true"
}
$myBatchRequests += $myRequest2
##################################################
$requestID ++
$myRequest3 = @{
id = $requestID
method = "GET"
url = "/teams/$GrpInfoID"
}
$myBatchRequests += $myRequest3
##################################################$allBatchRequests = @{
requests = $myBatchRequests
}##################################################
$batchBody = $allBatchRequests | ConvertTo-Json
$batchURL = "https://graph.microsoft.com/v1.0/`$batch"##################################################
$Header = HeaderToken-RW
$getBatchRequests = Invoke-RestMethod -Method POST -Headers $Header -Uri $batchURL -Body $batchBody -ContentType “application/json”foreach ($jobRMResult in $getBatchRequests.responses)
{
$BatchJobID = $jobRMResult.id
Write-host "###### Batch ID: $BatchJobID ############" -ForegroundColor Magenta
If($BatchJobID -eq 1)
{
$MemberCount = 0
$MemJobID = $jobRMResult.id
Foreach($Owners in $jobRMResult.body.value)
{
$MemberGUID = $Owners.id
$MemberMail = $owners.Mail
$MemberDisplayName = $owners.displayName
$MemberCount++
if($OwnerDisplayName -contains 'Microsoft.Azure.SyncFabric'){$OwnerCount = $OwnerCount - 1}
}
Write-host "JobID: $MemJobID ### Member Name: $MemberDisplayName ### Grp ID: $GrpInfoID ## Member Count: $MemberCount" -ForegroundColor Cyan
}
If($BatchJobID -eq 2)
{
#Owner/s of Group
$OwnerCount = 0
$OwnerJobID = $jobRMResult.id
Foreach($Owners in $jobRMResult.body.value)
{
$OwnerGUID = $Owners.id
$OwnerMail = $owners.Mail
$OwnerDisplayName = $owners.displayName
$OwnerCount++
if($OwnerDisplayName -contains 'Microsoft.Azure.SyncFabric'){$OwnerCount = $OwnerCount - 1}
}
Write-host "JobID: $OwnerJobID ### Owner Name: $OwnerDisplayName ### Grp ID: $GrpInfoID ## Owner Count: $OwnerCount" -ForegroundColor Gray
}
If($BatchJobID -eq 3)
{
# Teams Data
$TeamsJobID = $jobRMResult.id
$TeamStatus = $jobRMResult.body
$TeamActivated = $TeamStatus.isMembershipLimitedToOwners
$TeamArchived = $TeamStatus.isArchived
$TeamDisplayName = $TeamStatus.displayNameIf($TeamActivated -eq $True){$isActive = "Not Activated"}elseif($TeamActivated -eq $False){$isActive = "Activated"}
If($TeamArchived -eq $True){$isArchived = "Archived"}elseif($TeamArchived -eq $False){$isArchived = "Not Archived"}
Write-host "JobID: $TeamsJobID ### Team Name: $TeamDisplayName ### Grp ID: $GrpInfoID " -ForegroundColor Yellow}
}