private/Invoke-odscApiRequest.ps1

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

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

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

        [Parameter(Mandatory = $false)]
        [hashtable] $Headers,

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

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

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

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

    begin {
        $Token = $script:ODSToken

        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-odsc first.' -ErrorAction Stop
        }
    }

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

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

        if ($Headers) {
            foreach ($Key in $Headers.Keys) {
                $RequestHeaders[$Key] = $Headers[$Key]
            }
        }

        $GraphEndpoint = if ($script:ODSGraphEndpoint) { $script:ODSGraphEndpoint.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 = $RequestHeaders
                    Method = $Method
                    UseBasicParsing = $true
                }

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

                try {
                    $RawResponse = Invoke-WebRequest @Request
                    $Content = $RawResponse.Content
                    if ($Content -is [byte[]]) {
                        $Content = [System.Text.Encoding]::UTF8.GetString($Content)
                    }

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

                    if ($_.Exception.Response) {
                        try { $StatusCode = [int]$_.Exception.Response.StatusCode } catch { $StatusCode = $null }
                        $RetryAfter = Get-odscRetryAfterDelay -Response $_.Exception.Response
                        $GraphRequestId = Get-odscResponseHeader -Response $_.Exception.Response -Name 'request-id'
                    }

                    $IsTransient = $StatusCode -in @(429, 500, 502, 503, 504)
                    if (($Attempt -le $MaxRetryCount) -and $IsTransient) {
                        $Delay = if ($null -ne $RetryAfter) { $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()
    }
}