Forum Discussion

EntilZha's avatar
EntilZha
Iron Contributor
Dec 15, 2021

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

  • rbrbr's avatar
    rbrbr
    Copper 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 

    • EntilZha's avatar
      EntilZha
      Iron Contributor

      rbrbr 

      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.displayName

      If($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

      }
      }

Resources