Private/Graph/Get-DevicePolicyAssignmentsData.ps1

# Copyright (c) 2026 Sandy Zeng. All rights reserved.
# Source-available. All rights reserved. See LICENSE file.

<#
    Get-DevicePolicyAssignmentsData.ps1 — Retrieves configuration policies assigned to a specific device.
 
    Author: Sandy Zeng
    Project: IntuneDiff
 
    Version History:
    1.0.0 Initial release.
#>


function Get-DevicePolicyAssignmentsData {
    <#
    .SYNOPSIS
        Returns the list of configuration policies assigned to a device.
 
    .DESCRIPTION
        Calls reports/getConfigurationPoliciesReportForDevice with pagination via skip/top.
        Returns array of hashtables: PolicyId, PolicyName, PolicyBaseTypeName, PolicyStatus,
        PolicyStatusText, UPN, UserId.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$DeviceId,

        [switch]$IncludeAllStatuses
    )

    if ($DeviceId -notmatch '^[0-9a-fA-F\-]{36}$') {
        throw "Invalid DeviceId format: '$DeviceId'. Expected a GUID."
    }

    $pageSize = 50
    $skip = 0
    $totalRowCount = 0
    $schema = @()
    $allValues = New-Object System.Collections.Generic.List[object]

    while ($true) {
        $body = @{
            select  = @('IntuneDeviceId','PolicyBaseTypeName','PolicyId','PolicyStatus','UPN','UserId','PspdpuLastModifiedTimeUtc','PolicyName','UnifiedPolicyType')
            filter  = "((PolicyBaseTypeName eq 'Microsoft.Management.Services.Api.DeviceConfiguration') or (PolicyBaseTypeName eq 'DeviceManagementConfigurationPolicy') or (PolicyBaseTypeName eq 'DeviceConfigurationAdmxPolicy') or (PolicyBaseTypeName eq 'Microsoft.Management.Services.Api.DeviceManagementIntent')) and (IntuneDeviceId eq '$DeviceId')"
            skip    = $skip
            top     = $pageSize
            orderBy = @('PolicyName')
        }

        $response = Invoke-IntuneDiffRequest -Method POST -Uri '/beta/deviceManagement/reports/getConfigurationPoliciesReportForDevice' -Body $body
        if ($response -is [string]) { $response = $response | ConvertFrom-Json }

        if ($schema.Count -eq 0 -and $response.Schema) { $schema = $response.Schema }

        $pageValues = $response.Values
        if (-not $pageValues) { $pageValues = @() }
        if ($response.TotalRowCount) { $totalRowCount = [int]$response.TotalRowCount }

        if ($pageValues.Count -eq 0) { break }

        foreach ($row in $pageValues) { $allValues.Add($row) }
        $skip += $pageSize

        if ($totalRowCount -gt 0 -and $allValues.Count -ge $totalRowCount) { break }
    }

    $statusMap = @{ 0 = 'Succeeded'; 1 = 'Not applicable'; 2 = 'Succeeded'; 5 = 'Error'; 6 = 'Conflict' }
    $policies = New-Object System.Collections.Generic.List[object]

    foreach ($valueArray in $allValues) {
        $row = @($valueArray)
        $obj = @{}
        for ($i = 0; $i -lt $schema.Count; $i++) {
            $columnName = $schema[$i].Column
            $obj[$columnName] = $row[$i]
        }

        if ($null -ne $obj['PolicyStatus']) {
            $key = [int]$obj['PolicyStatus']
            if ($statusMap.ContainsKey($key)) {
                $obj['PolicyStatusText'] = $statusMap[$key]
            } else {
                $obj['PolicyStatusText'] = "Status $key"
            }
        }

        if (-not $IncludeAllStatuses -and $obj['PolicyStatus'] -eq 1) { continue }

        $policies.Add([pscustomobject]$obj)
    }

    return $policies.ToArray()
}