private/Invoke-odscexApiRequest.ps1

function Invoke-odscexApiRequest {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string] $Resource,

        [Parameter(Mandatory = $true)]
        [Microsoft.PowerShell.Commands.WebRequestMethod] $Method,

        [Parameter(Mandatory = $false)]
        [string] $Body,

        [Parameter(Mandatory = $false)]
        [switch] $DoNotUsePrefer,

        [Parameter(Mandatory = $false)]
        [switch] $AllPages,

        [Parameter(Mandatory = $false)]
        [ValidateRange(1, 10)]
        [int] $MaxRetryCount = 5,

        [Parameter(Mandatory = $false)]
        [ValidateRange(1, 300)]
        [int] $RetryDelaySeconds = 2
    )

    begin {
        $Token = $script:ODSCEXToken

        if ((!$Token.ExpiresOn) -or
            (!$Token.AccessToken) -or
            ($Token.ExpiresOn -le (Get-Date))) {
            Write-Verbose 'No usable Microsoft Graph token is available.'
            Write-Error 'Please run Connect-odscex first.' -ErrorAction Stop
        }
    }

    process {
        $Headers = @{
            Authorization = "Bearer $($Token.AccessToken)"
        }

        if (!($DoNotUsePrefer.IsPresent)) {
            $Headers.Prefer = 'apiversion=2.1'
        }

        $GraphEndpoint = if ($script:ODSCEXGraphEndpoint) { $script:ODSCEXGraphEndpoint.TrimEnd('/') } else { 'https://graph.microsoft.com' }
        $Uri = if ($Resource -match '^https://') { $Resource } else { "$GraphEndpoint/v1.0/$($Resource)" }
        $Results = New-Object System.Collections.Generic.List[object]
        $NextUri = $Uri

        do {
            $Attempt = 0
            $Response = $null
            $Succeeded = $false

            while (-not $Succeeded) {
                $Request = @{
                    Uri = $NextUri
                    ContentType = 'application/json'
                    Headers = $Headers
                    Method = $Method
                    UseBasicParsing = $true
                }

                if ($Body -and $NextUri -eq $Uri) {
                    $Request.Body = $Body
                }

                try {
                    $RawResponse = Invoke-WebRequest @Request
                    $Response = if ([string]::IsNullOrWhiteSpace($RawResponse.Content)) {
                        $null
                    } else {
                        ConvertFrom-Json -InputObject $RawResponse.Content
                    }
                    $Succeeded = $true
                } catch {
                    $Attempt++
                    $StatusCode = $null
                    $RetryAfter = $null
                    $GraphRequestId = $null

                    if ($_.Exception.Response) {
                        $StatusCode = [int]$_.Exception.Response.StatusCode
                        try { $RetryAfter = $_.Exception.Response.Headers['Retry-After'] } catch { $RetryAfter = $null }
                        try { $GraphRequestId = $_.Exception.Response.Headers['request-id'] } catch { $GraphRequestId = $null }
                        if (-not $RetryAfter) { try { $RetryAfter = $_.Exception.Response.GetResponseHeader('Retry-After') } catch { $RetryAfter = $null } }
                        if (-not $GraphRequestId) { try { $GraphRequestId = $_.Exception.Response.GetResponseHeader('request-id') } catch { $GraphRequestId = $null } }
                    }

                    $IsTransient = $StatusCode -in @(429, 500, 502, 503, 504)
                    if (($Attempt -le $MaxRetryCount) -and $IsTransient) {
                        $Delay = if ($RetryAfter) { [int]$RetryAfter } else { [Math]::Min(300, ($RetryDelaySeconds * [Math]::Pow(2, ($Attempt - 1)))) }
                        Write-Verbose "Microsoft Graph request was throttled or transiently failed with HTTP $StatusCode. Retrying in $Delay seconds. RequestId: $GraphRequestId"
                        Start-Sleep -Seconds $Delay
                    } else {
                        $Message = "Microsoft Graph request failed. Method: $Method. Resource: $Resource. StatusCode: $StatusCode. RequestId: $GraphRequestId. Error: $($_.Exception.Message)"
                        Write-Error $Message -ErrorAction Stop
                    }
                }
            }

            if ($AllPages -and $Response -and ($null -ne $Response.value)) {
                foreach ($Item in $Response.value) {
                    $Results.Add($Item) | Out-Null
                }
                $NextUri = $Response.'@odata.nextLink'
                $Method = [Microsoft.PowerShell.Commands.WebRequestMethod]::Get
                $Body = $null
            } else {
                return $Response
            }
        } while ($AllPages -and $NextUri)

        return $Results.ToArray()
    }
}