Private/Invoke-EMOSGraphRequest.ps1
|
function Invoke-EMOSGraphRequest { <# .SYNOPSIS Wraps Invoke-MgGraphRequest with automatic pagination and 429 retry logic. .DESCRIPTION Issues a GET to the Microsoft Graph API. Automatically follows @odata.nextLink to retrieve all pages, and retries on HTTP 429 (throttling) by honouring the Retry-After response header. All pages are returned as a single flat array. .PARAMETER Uri The initial Graph API URI (relative or absolute). .PARAMETER MaxRetries Maximum number of retry attempts on 429. Default: 5. .OUTPUTS Array of value items across all pages. #> [CmdletBinding()] [OutputType([object[]])] param( [Parameter(Mandatory)] [string]$Uri, [hashtable]$Headers = @{}, [ValidateRange(1, 10)] [int]$MaxRetries = 5 ) $results = [System.Collections.Generic.List[object]]::new() $currentUri = $Uri $pageNumber = 0 do { $pageNumber++ $attempt = 0 $response = $null while ($attempt -le $MaxRetries) { try { Write-Verbose "EMOS Graph GET (page $pageNumber, attempt $($attempt+1)): $currentUri" $response = Invoke-MgGraphRequest -Method GET -Uri $currentUri -OutputType PSObject -Headers $Headers break # success — exit retry loop } catch { # Detect 429 — check message (works without Graph SDK loaded) $is429 = $_.Exception.Message -match '429|Too\s+Many\s+Requests' # Also check response status if available (live Graph SDK path) if (-not $is429) { try { $statusCode = $_.Exception.Response.StatusCode.value__ if ($statusCode -eq 429) { $is429 = $true } } catch { } } if ($is429 -and $attempt -lt $MaxRetries) { $retryAfter = 30 # safe default # Try to read Retry-After from the exception response headers try { $raHeader = $_.Exception.Response.Headers.GetValues('Retry-After') | Select-Object -First 1 if ($raHeader -and [int]::TryParse($raHeader, [ref]$null)) { $retryAfter = [int]$raHeader } } catch { } $attempt++ Write-Warning "EMOS: Graph throttled (429). Waiting ${retryAfter}s before retry $attempt/$MaxRetries..." Start-Sleep -Seconds $retryAfter } else { # Non-429 or exhausted retries — rethrow throw } } } if ($null -eq $response) { throw "EMOS: Graph request failed after $MaxRetries retries: $currentUri" } # Collect this page's items if ($null -ne $response.value) { $results.AddRange([object[]]$response.value) } else { # Single-object response (no .value wrapper) $results.Add($response) } $currentUri = $response.'@odata.nextLink' } while ($currentUri) Write-Verbose "EMOS Graph: retrieved $($results.Count) item(s) across $pageNumber page(s) from $Uri" return $results.ToArray() } |