Private/Invoke-MgGraphRequestWithRetry.ps1
|
function Invoke-MgGraphRequestWithRetry { <# .SYNOPSIS Wrapper around Invoke-MgGraphRequest with automatic 429 throttle handling. .DESCRIPTION Retries the request on HTTP 429 (Too Many Requests) using the Retry-After header value or exponential backoff (4s, 8s, 16s). Passes all parameters through to Invoke-MgGraphRequest via splatting. .PARAMETER Parameters Hashtable of parameters to pass to Invoke-MgGraphRequest (e.g. Method, Uri). .PARAMETER MaxRetries Maximum number of retry attempts after throttling. Default: 3. .EXAMPLE Invoke-MgGraphRequestWithRetry -Parameters @{ Method = 'GET'; Uri = $Uri } #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [hashtable]$Parameters, [Parameter(Mandatory = $false)] [ValidateRange(1, 10)] [int]$MaxRetries = 3 ) process { [int]$Attempt = 0 while ($true) { try { return Invoke-MgGraphRequest @Parameters } catch { $Attempt++ [string]$ErrorText = "$_" [bool]$IsThrottled = $ErrorText -match '\b429\b' if ($Attempt -ge $MaxRetries -or -not $IsThrottled) { throw } # Honour Retry-After header; fall back to exponential backoff (4, 8, 16 seconds) [int]$WaitSeconds = [Math]::Pow(2, $Attempt) * 2 if ($_.Exception.PSObject.Properties['Response'] -and $null -ne $_.Exception.Response) { [string]$RetryAfterHeader = $_.Exception.Response.Headers['Retry-After'] [int]$ParsedSeconds = 0 if (-not [string]::IsNullOrEmpty($RetryAfterHeader) -and [int]::TryParse($RetryAfterHeader, [ref]$ParsedSeconds)) { $WaitSeconds = $ParsedSeconds } } Write-Warning "Graph API throttled (429). Waiting $WaitSeconds seconds before retry $Attempt/$MaxRetries..." Start-Sleep -Seconds $WaitSeconds } } } } |