Private/Batching/Invoke-GraphRequestSafe.ps1
|
function Invoke-GraphRequestSafe { <#! .SYNOPSIS Invokes Microsoft Graph requests with retry/backoff for throttling and transient failures. .DESCRIPTION Wraps Invoke-MgGraphRequest and retries on 429 and common transient 5xx/408 errors. Honors Retry-After when available. #> [CmdletBinding()] param( [Parameter(Mandatory)][ValidateSet('GET','POST','PUT','PATCH','DELETE')][string]$Method, [Parameter(Mandatory)][string]$Uri, [object]$Body, [int]$MaxRetries = 6, [switch]$QuietMode ) $attempt = 0 while ($true) { try { if ($PSBoundParameters.ContainsKey('Body')) { return Invoke-MgGraphRequest -Method $Method -Uri $Uri -Body $Body -ErrorAction Stop } return Invoke-MgGraphRequest -Method $Method -Uri $Uri -ErrorAction Stop } catch { $attempt++ $status = $null try { if ($_.Exception.Response) { $status = $_.Exception.Response.StatusCode.value__ } } catch { Write-Verbose "Failed to read status code from Graph exception: $_" } $transient = @($status) -contains 429 -or @($status) -contains 408 -or @($status) -contains 500 -or @($status) -contains 502 -or @($status) -contains 503 -or @($status) -contains 504 if (-not $transient -or $attempt -gt $MaxRetries) { throw } $retryAfterSeconds = Get-GraphRetryAfterSeconds -ErrorRecord $_ $delaySeconds = if ($retryAfterSeconds) { $retryAfterSeconds } else { # exponential backoff with a small cap [int]([Math]::Min(60, [Math]::Pow(2, $attempt))) } if (-not $QuietMode) { Write-ModuleLog -Message "Graph request throttled/transient (HTTP $status). Retrying in $delaySeconds s (attempt $attempt/$MaxRetries)." -Level Warning } Start-Sleep -Seconds $delaySeconds } } } |