Private/Invoke-IntuneGraphRequest.ps1

<#
.SYNOPSIS
    Invokes Microsoft Graph REST API for Intune/DeviceManagement.
.DESCRIPTION
    Ensures Connect-MgGraph (or equivalent) is used, then calls Graph API.
#>

function Invoke-IntuneGraphRequest {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Method,
        [Parameter(Mandatory = $true)]
        [string]$Uri,
        [object]$Body = $null
    )

    if ($Uri -notmatch '^https://') {
        $Uri = "https://graph.microsoft.com/v1.0/$($Uri.TrimStart('/'))"
    }

    try {
        if (Get-Command -Name 'Invoke-MgGraphRequest' -ErrorAction SilentlyContinue) {
            $params = @{
                Method = $Method
                Uri    = $Uri
                ErrorAction = 'Stop'
            }
            if ($Body) {
                $params["Body"] = $Body
            }
            return Invoke-MgGraphRequest @params
        }

        $ctx = $null
        if (Get-Command -Name 'Get-MgContext' -ErrorAction SilentlyContinue) {
            $ctx = Get-MgContext
        }
        if (-not $ctx -or -not $ctx.TenantId) {
            throw "Not connected to Microsoft Graph. Run Connect-MgGraph or ensure Import-NLBaselineConfig + Connect-Intune first."
        }

        $token = $null
        if (Get-Command -Name 'Get-MgAccessToken' -ErrorAction SilentlyContinue) {
            $token = Get-MgAccessToken
        }
        if (-not $token) {
            throw "Could not obtain Graph access token. Connect via Connect-MgGraph."
        }

        $headers = @{
            "Authorization" = "Bearer $token"
            "Content-Type"  = "application/json"
        }
        $irmParams = @{
            Method  = $Method
            Uri     = $Uri
            Headers = $headers
        }
        if ($Body) {
            $irmParams["Body"] = if ($Body -is [string]) { $Body } else { $Body | ConvertTo-Json -Depth 20 }
        }
        return Invoke-RestMethod @irmParams
    }
    catch {
        $err = $_.Exception.Message
        $responseBody = ""
        $httpErr = $_.Exception
        if ($httpErr -is [System.Net.Http.HttpRequestException] -and $httpErr.Data.Contains('Response')) {
            try {
                $response = $httpErr.Data['Response']
                if ($response -and $response.Content) {
                    $responseBody = $response.Content.ReadAsStringAsync().Result
                }
            }
            catch {
                $null = 0  # Ignore response-body parse; primary error is preserved
            }
        }
        elseif ($_.Exception.Response) {
            try {
                $resp = $_.Exception.Response
                if ($resp.GetType().Name -eq 'HttpResponseMessage') {
                    $responseBody = $resp.Content.ReadAsStringAsync().Result
                }
                else {
                    $stream = $resp.GetResponseStream()
                    $reader = New-Object System.IO.StreamReader($stream)
                    $responseBody = $reader.ReadToEnd()
                    $reader.Close()
                    $stream.Close()
                }
            }
            catch {
                $null = 0  # Ignore response-body parse; primary error is preserved
            }
        }
        
        if ($err -match '401|Unauthorized') {
            Write-Error "Graph request failed (401 Unauthorized). Check: (1) Client secret expired – create a new one in Azure App Registration > Certificates & secrets and update config.json; (2) ClientId, TenantId, or ClientSecret incorrect in config.json; (3) App Registration disabled or deleted. See WIKI.md Troubleshooting."
        }
        elseif ($err -match '403|Forbidden|DeviceManagementConfiguration') {
            Write-Error "Graph request failed (403). Ensure the App Registration has Application permission 'DeviceManagementConfiguration.ReadWrite.All' and admin consent is granted. See API-PERMISSIONS.md."
        }
        elseif ($err -match '400|BadRequest') {
            $detailedErr = $err
            if ($responseBody) {
                try {
                    $errJson = $responseBody | ConvertFrom-Json -ErrorAction SilentlyContinue
                    if ($errJson.error.message) {
                        $detailedErr = $errJson.error.message
                    }
                    elseif ($errJson.error.innerError) {
                        $detailedErr = $errJson.error.innerError | ConvertTo-Json -Depth 5
                    }
                }
                catch {
                    $null = 0  # Ignore JSON parse; use generic error
                }
            }
            Write-Error "Graph request failed (400 BadRequest). $detailedErr"
        }
        else {
            Write-Error "Graph request failed: $err"
        }
        throw
    }
}