Functions/Invoke-GraphAPI.ps1

<#
.SYNOPSIS
    Invokes the Microsoft Graph API with the provided URI and access token.
.DESCRIPTION
    This function calls the Microsoft Graph API with the provided URI and access token. It handles pagination and retries in case of throttling or unauthorized responses.
.PARAMETER Uri
    The URI of the Microsoft Graph API endpoint to call.
.PARAMETER Method
    The HTTP method to use for the API call (e.g. GET, POST, etc.).
.PARAMETER ODataQuery
    An optional OData query to include in the API call.
.EXAMPLE
    $response = Invoke-GraphAPI -Uri "https://graph.microsoft.com/v1.0/users" -Method "GET"
    Returns a response object containing information about the users in the organization.
#>

function Invoke-GraphAPI {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Uri,
        [string]$Method,
        [Parameter(Mandatory = $false)]
        [string]$ODataQuery
    )

    Begin {
        $ErrorActionPreference = 'Stop'
        $now = Get-Date
        $retryCount = 0
        [int]$MaxRetries = 3
        [int]$RetryDelaySeconds = 3
    }

    Process {
        # Check if access token is still valid
        if ($null -eq $global:AzureADAccessToken -or $global:AzureADAccessTokenExpiration -lt $now) {
            Write-Host "Access token is not valid. Please run the Get-AzureADAccessToken cmdlet again."
            return
        }
        
        # Call the Graph API with the access token
        $headers = @{
            'Authorization' = "Bearer $($global:AzureADAccessToken)"
        }
       
        do {
            try {        
                $response = Invoke-RestMethod -Uri $Uri -Headers $headers -Method $Method
                # Handle pagination
            
                while ($response.'@odata.nextLink' -or ($response.'@odata.nextLink' -and ($nextResponse.value.Length -gt 0))) {
                    $nextLink = $response.'@odata.nextLink'            
                    write-host "Next link is: " $nextLink
                    $nextResponse = Invoke-RestMethod -Uri $nextLink -Headers $headers
                    if($nextResponse.value.Length -eq 0){
                        write-host "The next response is empty. Exiting loop."
                        break}
                    $response.value += $nextResponse.value
                    $response.'@odata.nextLink' = $nextResponse.'@odata.nextLink'
                }
                return $response
            }
            catch {
                $statusCode = $_.Exception.Response.StatusCode
                switch ($statusCode) {
                    429 {
                        if ($retryCount -lt $MaxRetries) {
                            Write-Host "Throttled. Retrying in $RetryDelaySeconds seconds..."
                            Start-Sleep -Seconds $RetryDelaySeconds
                            $retryCount++
                            continue
                        }
                        else {
                            throw $_
                        }
                    }
                    NotFound {
                        Write-Host "HTTP returned a 404 not found response for the provided endpoint: " 
                        write-host $Uri 
                        return $response.Error.Message
                        break
                    }
                    Unauthorized {
                        Write-Host "HTTP returned a 401 unauthorized response for the provided endpoint: " 
                        write-host $Uri 
                        write-host "RequestID is: " $response.headers
                        $retryCount++
                        start-sleep -Seconds $RetryDelaySeconds
                        write-host "Retrying...the current retry count is $retryCount"
                        continue
                    }
                    BadRequest{
                        Write-Host "HTTP returned a 400 bad request response for the provided endpoint: " 
                        write-host $Uri 
                        return $response.Error.Message
                        break
                    }
                    default {
                        throw $_
                        
                    }
                }
            }
     
        }while ($retryCount -lt $MaxRetries)
    }
    end {
        if ($retryCount -eq $MaxRetries) {
            write-host "Max retries exceeded. Exiting script."
            exit
        }  
    }    
}