core/Invoke-GraphRequest.ps1
|
#Requires -Version 7.0 <# .SYNOPSIS Makes GET requests to the Microsoft Graph API with retry logic and pagination handling. .DESCRIPTION Invokes HTTP GET requests against the Microsoft Graph API. Handles pagination by automatically fetching all pages and merging results. Implements retry logic for transient failures (429, 502, 503, 504). .PARAMETER Uri The Graph API endpoint path (starting with '/') or full URL. .PARAMETER Token The Bearer token for Graph authentication as a SecureString. Falls back to $env:GRAPH_TOKEN. .PARAMETER Headers Additional headers to merge into the request. .EXAMPLE $result = Invoke-GraphRequest2 -Uri '/beta/deviceManagement/configurationPolicies' -Token $token #> function Invoke-GraphRequest2 { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$Uri, [SecureString]$Token = $null, [hashtable]$Headers = @{} ) #region Parameter Validation if ($null -eq $Token -and $env:GRAPH_TOKEN) { $Token = ConvertTo-SecureString -String $env:GRAPH_TOKEN -AsPlainText -Force } if ($null -eq $Token) { throw 'No token provided and $env:GRAPH_TOKEN is not set' } if ($Uri -notmatch '^https?://') { $Uri = "https://graph.microsoft.com$Uri" } #endregion Parameter Validation #region Retry Loop $maxRetries = 5 $retryCount = 0 $allResults = [System.Collections.Generic.List[object]]::new() do { try { $requestHeaders = $Headers.Clone() $requestHeaders['Content-Type'] = 'application/json' $plainToken = ConvertFrom-SecureString -SecureString $Token -AsPlainText $requestHeaders['Authorization'] = "Bearer $plainToken" Write-Verbose "GET $Uri (attempt $($retryCount + 1))" $response = Invoke-RestMethod -Uri $Uri -Method Get -Headers $requestHeaders -ErrorAction Stop if ($response.PSObject.Properties['value']) { foreach ($item in $response.value) { $allResults.Add($item) } if ($response.PSObject.Properties['@odata.nextLink']) { $Uri = $response.'@odata.nextLink' Write-Verbose "Next page: $Uri" continue } return , $allResults.ToArray() } else { return $response } } catch { $ex = $_ $code = $null if ($ex.Exception.psobject.Properties['Response'] -and $ex.Exception.Response) { $code = [int]$ex.Exception.Response.StatusCode } if ($code -in @(429, 502, 503, 504) -and $retryCount -lt $maxRetries) { $wait = 10 if ($code -eq 429 -and $ex.Exception.Response) { $ra = $ex.Exception.Response.Headers.GetValues('Retry-After') | Select-Object -First 1 if ($ra) { $wait = [int]$ra } } $retryCount++ Write-Verbose "Status $code — retrying in $wait s (retry $retryCount of $maxRetries)" Start-Sleep -Seconds $wait continue } throw $ex } } while ($true) #endregion Retry Loop } Export-ModuleMember -Function Invoke-GraphRequest2 |