Public/Invoke-IBMGrapAPIBatching.ps1

function Invoke-IBMGrapAPIBatching {

    <#
    .SYNOPSIS
        Executes a paged request to the Microsoft Graph API and retrieves all results.

    .DESCRIPTION
        The Invoke-IBMPagingRequest function handles pagination when making requests to the Microsoft Graph API.
        It retrieves all pages of results from the specified URI and returns the complete collection of data.

    .NOTES
        Author: Florian Salzmann | @FlorianSLZ | https://scloud.work
        Version: 1.0
        Date: 2024-08-05

        Changelog:
        - 2024-08-05: 1.0 Initial version
        
    #>



    param (
        [parameter(Mandatory = $true, HelpMessage = "Specify the collection of devices to process.")]
        [ValidateNotNullOrEmpty()]
        [array]$Objects2Process,
        
        [parameter(Mandatory = $true, HelpMessage = "Specify the URI for the Microsoft Graph API call, ID variable is {0}. e.g deviceManagement/managedDevices/{0}/syncDevice/")]
        [ValidateNotNullOrEmpty()]
        [string]$ActionURI, 

        [parameter(Mandatory = $true, HelpMessage = "Specify the HTTP method for the Microsoft Graph API call.")]
        [ValidateNotNullOrEmpty()]
        [string]$Method, 

        [parameter(Mandatory = $false, HelpMessage = "Readable title for Progress bar and log.")]
        [ValidateNotNullOrEmpty()]
        [string]$ActionTitle, 

        [parameter(Mandatory = $false, HelpMessage = "Specify the body for the Microsoft Graph API call. (optional)")]
        [ValidateNotNullOrEmpty()]
        [array]$BodySingle = @{}, 

        [parameter(Mandatory = $false, HelpMessage = "Graph version to use, default is v1.0.")]
        [ValidateNotNullOrEmpty()]
        [string]$GraphVersion = "v1.0", 

        [parameter(Mandatory = $false, HelpMessage = "Size of batches, max is 20.")]
        [ValidateNotNullOrEmpty()]
        [int]$BatchSize = 20, # Max is 20: https://learn.microsoft.com/en-us/graph/json-batching#batch-size-limitations

        [parameter(Mandatory = $false, HelpMessage = "Graph URI for batching / batch requests.")]
        [ValidateNotNullOrEmpty()]
        [string]$BatchURI = "https://graph.microsoft.com/$GraphVersion/`$batch"

    )


    if(!$ActionTitle){  $ActionTitle = "$ActionURI" }

    $ResponseCollection = [System.Collections.Generic.List[System.Object]]::new()

    for ($i = 0; $i -lt $Objects2Process.Length; $i += $BatchSize) {
        Write-Progress -Id 0 -Activity "$ActionTitle" -Status "Processing $($i) of $($Objects2Process.count)" -PercentComplete (($i/$Objects2Process.Count) * 100)
        Write-Verbose "Processing chunk starting at index $i" 

        # Calculate the end index for the current chunk
        $end = [math]::Min($i + $BatchSize, $Objects2Process.Length)

        # Handle single item case
        if ($end - $i -eq 1) {
            Write-Verbose "Processing last/single item at index $i"
            $requestParams = @{
                "Method"      = $Method
                "Uri"         = "https://graph.microsoft.com/$GraphVersion/$ActionURI" -f $($Objects2Process[$i]) 
                "ContentType" = "application/json"
                "Body"        = if ($BodySingle.Count -gt 0) { $BodySingle } else { @{} }
            }        
        }else{
            # Create the batch requests
            $requests = for ($j = $i; $j -lt $end; $j++) {
                [PSCustomObject]@{
                    "id"     = "$($j + 1)"
                    "Method" = $Method
                    "URL"    = $ActionURI -f $($Objects2Process[$j])
                    "Headers" = @{ "Content-Type" = "application/json" }
                    "Body"    = if ($BodySingle.Count -gt 0) { $BodySingle } else { @{} }
                }
            }
            
            # Convert the requests to JSON with appropriate depth
            $Body4requests = @{ "requests" = $requests } | ConvertTo-Json -Depth 4

            # Define the parameters for the request
            $requestParams = @{
                "Method"      = "POST"
                "Uri"         = $BatchURI
                "ContentType" = "application/json"
                "Body"        = $Body4requests
            }
        }
        
        # Send the request
        try {

            $response = Invoke-MgGraphRequest @requestParams
            Write-Verbose "Responses: $($response.responses.status)"

            $response.responses | ForEach-Object { $ResponseCollection.Add([pscustomobject]$PSItem.body) }

        }
        catch {
            Write-Error "Error occurred during request: $_"
        }
    }

    Write-Host "$ActionTitle of $($Objects2Process.count) devices completed." -ForegroundColor Green

    return $ResponseCollection
    
}