Private/Get-GraphPagedResults.ps1
|
function Get-GraphPagedResults { <# .SYNOPSIS Fetches all pages of a Graph API paginated response .DESCRIPTION Handles the @odata.nextLink pagination pattern used by Microsoft Graph API. Can either accumulate all results and return them, or invoke a scriptblock per page for streaming/processing scenarios. .PARAMETER Uri The initial Graph API URI (relative, e.g., "beta/deviceManagement/configurationPolicies") .PARAMETER Headers Optional headers to include in the request .PARAMETER ProcessItems Optional scriptblock invoked with each page's .value array. When provided, items are NOT accumulated — the caller handles them in the scriptblock. .EXAMPLE # Accumulate all results $allPolicies = Get-GraphPagedResults -Uri "beta/deviceManagement/configurationPolicies" .EXAMPLE # Process each page (streaming) Get-GraphPagedResults -Uri "beta/groups" -ProcessItems { param($items) $items | ForEach-Object { ... } } #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Uri, [Parameter()] [hashtable]$Headers, [Parameter()] [scriptblock]$ProcessItems ) $results = [System.Collections.Generic.List[object]]::new() $listUri = $Uri do { $params = @{ Method = 'GET' Uri = $listUri ErrorAction = 'Stop' } if ($Headers) { $params['Headers'] = $Headers } $response = $null try { $response = Invoke-MgGraphRequest @params } catch { # Invoke-MgGraphRequest deserializes JSON into a Dictionary which throws # on duplicate keys. Fall back to raw HTTP response + ConvertFrom-Json # (returns PSCustomObject where last-key-wins, no error). if ($_.Exception.Message -like '*Item has already been added*' -or ($_.Exception.InnerException -and $_.Exception.InnerException.Message -like '*Item has already been added*')) { Write-Verbose "Dictionary deserialization failed for '$listUri', retrying with raw HTTP response" $httpResponse = Invoke-MgGraphRequest @params -OutputType HttpResponseMessage $jsonContent = $httpResponse.Content.ReadAsStringAsync().GetAwaiter().GetResult() $response = $jsonContent | ConvertFrom-Json } else { throw } } $responseValue = if ($null -ne $response.value) { $response.value } else { @() } if ($ProcessItems) { & $ProcessItems $responseValue } else { if ($responseValue) { $results.AddRange(@($responseValue)) } } $listUri = $response.'@odata.nextLink' } while ($listUri) if (-not $ProcessItems) { return , $results.ToArray() } } |