Private/Get-AssignmentFailures.ps1

function Get-AssignmentFailures {
    Write-Host "Fetching assignment failures..." -ForegroundColor Green

    $failedAssignments = [System.Collections.ArrayList]::new()

    # 1. Get App Install Failures
    # Note: App installation status endpoint requires specific permissions and may not be available in all environments
    <# Temporarily disabled due to endpoint availability
    Write-Host "Checking app installation failures..." -ForegroundColor Yellow
    try {
        $reportBody = @{
            filter = ""
            select = @(
                "DeviceName", "UserPrincipalName", "Platform", "AppVersion",
                "InstallState", "InstallStateDetail", "ErrorCode", "HexErrorCode",
                "ApplicationId", "AppInstallState", "AppInstallStateDetails",
                "LastModifiedDateTime", "DeviceId", "UserId", "UserName"
            )
            skip = 0
            top = 50
        } | ConvertTo-Json

        $allAppFailures = @()
        $skip = 0

        do {
            $reportBody = @{
                filter = ""
                select = @(
                    "DeviceName", "UserPrincipalName", "Platform", "AppVersion",
                    "InstallState", "InstallStateDetail", "ErrorCode", "HexErrorCode",
                    "ApplicationId", "AppInstallState", "AppInstallStateDetails",
                    "LastModifiedDateTime", "DeviceId", "UserId", "UserName"
                )
                skip = $skip
                top = 50
            } | ConvertTo-Json

            $uri = "$script:GraphEndpoint/beta/deviceManagement/reports/getMobileApplicationManagementAppStatusReport"
            $response = try {
                Invoke-MgGraphRequest -Uri $uri -Method POST -Body $reportBody
            } catch {
                # If the new endpoint fails, try the alternative endpoint
                $uri = "$script:GraphEndpoint/beta/deviceManagement/reports/getAppStatusOverviewReport"
                Invoke-MgGraphRequest -Uri $uri -Method POST -Body $reportBody
            }

            if ($response.values) {
                $appFailures = $response.values | Where-Object {
                    $_[6] -ne 0 -or # ErrorCode
                    $_[4] -eq "failed" -or # InstallState
                    $_[9] -eq "failed" # AppInstallState
                }

                foreach ($failure in $appFailures) {
                    $allAppFailures += [PSCustomObject]@{
                        Type = "App"
                        PolicyName = "Application ID: $($failure[8])" # ApplicationId
                        Target = if ($failure[1]) { "User: $($failure[1])" } else { "Device: $($failure[0])" }
                        ErrorCode = if ($failure[7]) { "Error: 0x$($failure[7])" } else { "Error: $($failure[6])" } # HexErrorCode or ErrorCode
                        ErrorDescription = if ($failure[5] -and $failure[10]) { "$($failure[5]) - $($failure[10])" } elseif ($failure[5]) { $failure[5] } elseif ($failure[10]) { $failure[10] } else { "Installation failed" }
                        LastAttempt = $failure[11] # LastModifiedDateTime
                    }
                }
                $skip += 50
            }
        } while ($response.values -and $response.values.Count -eq 50)

        Write-Host "Found $($allAppFailures.Count) app installation failures" -ForegroundColor Green
        $failedAssignments.AddRange($allAppFailures)
    }
    catch {
        Write-Host "Error fetching app installation failures: $($_.Exception.Message)" -ForegroundColor Red
    }
    #>


    # 2. Get Device Configuration Policy Failures
    Write-Host "Checking device configuration policy failures..." -ForegroundColor Yellow
    try {
        $configPoliciesUri = "$script:GraphEndpoint/beta/deviceManagement/deviceConfigurations"
        $configPolicies = (Invoke-MgGraphRequest -Uri $configPoliciesUri -Method GET).value

        foreach ($policy in $configPolicies) {
            $skip = 0
            do {
                $reportBody = @{
                    filter = "(PolicyBaseTypeName eq 'Microsoft.Management.Services.Api.DeviceConfiguration') and (PolicyId eq '$($policy.id)')"
                    select = @("DeviceName", "UPN", "PolicyStatus", "PspdpuLastModifiedTimeUtc")
                    skip   = $skip
                    top    = 50
                } | ConvertTo-Json

                $uri = "$script:GraphEndpoint/beta/deviceManagement/reports/getConfigurationPolicyDevicesReport"
                $response = Invoke-MgGraphRequest -Uri $uri -Method POST -Body $reportBody

                if ($response.values) {
                    $failures = $response.values | Where-Object {
                        $_[2] -in @("error", "conflict", "notApplicable")
                    }

                    foreach ($failure in $failures) {
                        $null = $failedAssignments.Add([PSCustomObject]@{
                                Type             = "Device Configuration"
                                PolicyName       = $policy.displayName
                                Target           = "Device: $($failure[0])"
                                ErrorCode        = "$($failure[2])"
                                ErrorDescription = if ($failure[1]) { "$($failure[1])" } else { "No additional details" }
                                LastAttempt      = $failure[3]
                            })
                    }
                    $skip += 50
                }
            } while ($response.values -and $response.values.Count -eq 50)
        }
    }
    catch {
        Write-Host "Error fetching device configuration failures: $($_.Exception.Message)" -ForegroundColor Red
    }

    # 3. Get Compliance Policy Failures
    Write-Host "Checking compliance policy failures..." -ForegroundColor Yellow
    try {
        $compliancePoliciesUri = "$script:GraphEndpoint/beta/deviceManagement/deviceCompliancePolicies"
        $compliancePolicies = (Invoke-MgGraphRequest -Uri $compliancePoliciesUri -Method GET).value

        foreach ($policy in $compliancePolicies) {
            $statusUri = "$script:GraphEndpoint/beta/deviceManagement/deviceCompliancePolicies('$($policy.id)')/deviceStatuses"
            $statuses = (Invoke-MgGraphRequest -Uri $statusUri -Method GET).value

            $failures = $statuses | Where-Object {
                $_.status -in @("error", "conflict", "notApplicable", "nonCompliant")
            }

            foreach ($failure in $failures) {
                $null = $failedAssignments.Add([PSCustomObject]@{
                        Type             = "Compliance Policy"
                        PolicyName       = $policy.displayName
                        Target           = "Device: $($failure.deviceDisplayName)"
                        ErrorCode        = "$($failure.status)"
                        ErrorDescription = if ($failure.userPrincipalName) { "$($failure.userPrincipalName)" } else { "No additional details" }
                        LastAttempt      = $failure.lastReportedDateTime
                    })
            }
        }
    }
    catch {
        Write-Host "Error fetching compliance policy failures: $($_.Exception.Message)" -ForegroundColor Red
    }

    return $failedAssignments
}