Private/Get-PSUBatchDirectoryObjects.ps1

function Get-PSUBatchDirectoryObjects {
    <#
    .SYNOPSIS
      Batch-get directory objects from Microsoft Graph using /$batch to reduce per-object calls.
    .PARAMETER Ids
      Array of directory object ids to retrieve.
    .OUTPUTS
      Array of parsed Directory objects (has id, displayName, mail, userPrincipalName, @odata.type)
    #>

    param([string[]] $Ids)

    if (-not $Ids -or $Ids.Count -eq 0) { return @() }

    # Graph batch supports up to 20 requests per batch
    $chunkSize = 20
    $out = @()

    foreach ($chunk in ($Ids | ForEach-Object -Begin { $i=0 } -Process { [PSCustomObject]@{Index = [int]($i++); Id = $_ } } | Group-Object { [int]($_.Index / $chunkSize) } | ForEach-Object { $_.Group | Select-Object -ExpandProperty Id })) {
        # Build requests for this chunk
        $requests = @()
        $reqId = 1
        foreach ($id in $chunk) {
            $requests += @{
                id = "$reqId"
                method = "GET"
                url = "/directoryObjects/$id"
            }
            $reqId++
        }
        $body = @{ requests = $requests }

        try {
            $resp = Invoke-MgGraphRequest -Method POST -Uri '/$batch' -Body ($body | ConvertTo-Json -Depth 5) -ContentType 'application/json'
        } catch {
            Write-Verbose "Graph batch request failed: $_"
            # fall back: try per-id
            foreach ($id in $chunk) {
                try {
                    $obj = Get-MgDirectoryObject -DirectoryObjectId $id -ErrorAction Stop
                    if ($obj) { $out += $obj }
                } catch { Write-Verbose "Get-MgDirectoryObject failed for $id`: $_" }
            }
            continue
        }

        # resp has 'responses' array
        if ($resp -and $resp.responses) {
            foreach ($r in $resp.responses) {
                if ($r.status -ge 200 -and $r.status -lt 300) {
                    # content is a JSON object - parse
                    $contentJson = $r.body | ConvertTo-Json -Compress
                    # The SDK returns objects already - but to be safe, rehydrate minimal properties
                    $obj = $r.body
                    $out += $obj
                } else {
                    Write-Verbose "Batch sub-request failed (id $($r.id)) status $($r.status)"
                }
            }
        }
    }

    return $out
}