Src/Private/Get-AbrIntuneDeviceCompliance.ps1

function Get-AbrIntuneDeviceCompliance {
    <#
    .SYNOPSIS
    Documents Intune Device Compliance policies and their assignments.
    .DESCRIPTION
        Collects and reports on:
          - Compliance policy inventory (name, platform, state)
          - Per-policy settings summary (InfoLevel 2)
          - Policy assignments (groups targeted)
          - ACSC E8 and CIS M365 compliance assessments
    .NOTES
        Version: 0.1.0
        Author: Pai Wei Sing
    #>

    [CmdletBinding()]
    param (
        [Parameter(Position = 0, Mandatory)]
        [string]$TenantId
    )

    begin {
        Write-PScriboMessage -Message "Collecting Intune Device Compliance Policies for $TenantId."
        Show-AbrDebugExecutionTime -Start -TitleMessage 'Device Compliance'
    }

    process {
        Section -Style Heading2 'Device Compliance Policies' {
            Paragraph "The following section documents the Device Compliance Policies configured in tenant $TenantId."
            BlankLine

            try {
                Write-Host " - Retrieving compliance policies..."
                $PoliciesResp = Invoke-MgGraphRequest -Method GET `
                    -Uri "$($script:GraphEndpoint)/v1.0/deviceManagement/deviceCompliancePolicies?`$expand=assignments" `
                    -ErrorAction Stop
                $Policies = $PoliciesResp.value

                if ($Policies -and @($Policies).Count -gt 0) {

                    #region Compliance Policy Summary
                    $SumObj = [System.Collections.ArrayList]::new()
                    $UnassignedPolicies = 0
                    $WindowsBitLockerRequired = $false
                    $WindowsDefenderRequired  = $false
                    $WindowsOsMinVersionConfigured = $false
                    $MobileOsMinVersionConfigured  = $false
                    $MobileEncryptionRequired      = $false

                    foreach ($Policy in ($Policies | Sort-Object displayName)) {
                        $OdataType = $Policy.'@odata.type' -replace '#microsoft.graph.', ''
                        $Platform  = switch -Wildcard ($OdataType) {
                            '*Windows*'   { 'Windows' }
                            '*Ios*'       { 'iOS / iPadOS' }
                            '*Android*'   { 'Android' }
                            '*MacOs*'     { 'macOS' }
                            default       { $OdataType }
                        }
                        # Resolve assignment groups to display names
                        $assignResolved = Resolve-IntuneAssignments `
                            -Assignments $Policy.assignments `
                            -CheckMemberCount:$script:CheckEmptyGroups
                        $AssignedTo = $assignResolved.AssignmentSummary
                        if ($assignResolved.AssignmentSummary -eq 'Not assigned') { $null = ($UnassignedPolicies++) }
                        if ($assignResolved.HasEmptyGroup) {
                            Write-AbrDebugLog "Empty group in compliance policy '$($Policy.displayName)'" 'WARN' 'ASSIGNMENTS'
                        }

                        # Aggregate compliance-check metrics
                        if ($OdataType -like '*Windows*') {
                            if ($Policy.bitLockerEnabled          -eq $true) { $WindowsBitLockerRequired = $true }
                            if ($Policy.defenderEnabled           -eq $true -or $Policy.antivirusRequired -eq $true) { $WindowsDefenderRequired = $true }
                            if ($Policy.osMinimumVersion) { $WindowsOsMinVersionConfigured = $true }
                        }
                        if ($OdataType -like '*Ios*' -or $OdataType -like '*Android*') {
                            if ($Policy.osMinimumVersion)                    { $MobileOsMinVersionConfigured = $true }
                            if ($Policy.storageRequireEncryption -eq $true)  { $MobileEncryptionRequired = $true }
                        }

                        $scopeTagStr = if ($script:ResolveScopeTagNames -and $Policy.roleScopeTagIds) {
                            Get-IntuneScopeTagNames -ScopeTagIds $Policy.roleScopeTagIds
                        } elseif ($Policy.roleScopeTagIds -and $Policy.roleScopeTagIds.Count -gt 0) {
                            $Policy.roleScopeTagIds -join ', '
                        } else { 'Default' }
                        $policyInObj = [ordered] @{
                            'Policy Name'       = $Policy.displayName
                            'Platform'          = $Platform
                            'Included Groups'   = $assignResolved.IncludedGroups
                            'Excluded Groups'   = if ($script:ShowExcludedGroups) { $assignResolved.ExcludedGroups } else { $null }
                            'Scheduled Actions' = if ($Policy.scheduledActionsForRule) { @($Policy.scheduledActionsForRule).Count } else { 0 }
                            'Scope Tags'        = $scopeTagStr
                            'Last Modified'     = if ($Policy.lastModifiedDateTime) { ([datetime]$Policy.lastModifiedDateTime).ToString('yyyy-MM-dd') } else { '--' }
                        }
                        # Remove null keys (hidden columns)
                        $SumObj.Add([pscustomobject]$policyInObj) | Out-Null
                    }

                    $null = (& {
                        if ($HealthCheck.Intune.DeviceCompliance) {
                            $null = ($SumObj | Where-Object { $_.'Included Groups' -eq '--' } | Set-Style -Style Warning | Out-Null)
                            if ($script:CheckEmptyGroups) {
                                # Re-flag policies with empty groups
                                $null = ($SumObj | Where-Object {
                                    $p = $Policies | Where-Object { $_.displayName -eq $_.'Policy Name' }
                                    if ($p) {
                                        $r = Resolve-IntuneAssignments -Assignments $p.assignments -CheckMemberCount:$true
                                        $r.HasEmptyGroup
                                    }
                                } | Set-Style -Style Warning | Out-Null)
                            }
                        }
                    })

                    $SumTableParams = @{ Name = "Compliance Policy Summary - $TenantId"; ColumnWidths = 20, 11, 18, 15, 10, 9, 17 }
                    if ($Report.ShowTableCaptions) { $SumTableParams['Caption'] = "- $($SumTableParams.Name)" }
                    $SumObj | Table @SumTableParams

                    if (Get-IntuneExcelSheetEnabled -SheetKey 'CompliancePolicies') { $script:ExcelSheets['Compliance Policies'] = $SumObj }
                    if (Get-IntuneBackupSectionEnabled -SectionKey 'CompliancePolicies') { $script:BackupData['CompliancePolicies'] = $Policies }
                    #endregion

                    #region Detailed per-policy settings (InfoLevel 2)
                    if ($InfoLevel.DeviceCompliance -ge 2) {
                        foreach ($Policy in ($Policies | Sort-Object displayName)) {
                            $OdataType = $Policy.'@odata.type' -replace '#microsoft.graph.', ''
                            $Platform  = switch -Wildcard ($OdataType) {
                                '*Windows*'   { 'Windows' }
                                '*Ios*'       { 'iOS / iPadOS' }
                                '*Android*'   { 'Android' }
                                '*MacOs*'     { 'macOS' }
                                default       { $OdataType }
                            }
                            Section -Style Heading3 "$($Policy.displayName)" {
                                BlankLine
                                # Build 2-column key-value table (Setting | Value)
                                # One row per setting -- avoids column-count mismatch crash
                                $DetailObj = [System.Collections.ArrayList]::new()
                                $DetailObj.Add([pscustomobject]@{ Setting = 'Policy Name';   Value = $Policy.displayName }) | Out-Null
                                $DetailObj.Add([pscustomobject]@{ Setting = 'Platform';      Value = $Platform }) | Out-Null
                                $DetailObj.Add([pscustomobject]@{ Setting = 'Policy Type';   Value = $OdataType }) | Out-Null
                                $DetailObj.Add([pscustomobject]@{ Setting = 'Created';       Value = if ($Policy.createdDateTime)      { ([datetime]$Policy.createdDateTime).ToString('yyyy-MM-dd') }      else { '--' } }) | Out-Null
                                $DetailObj.Add([pscustomobject]@{ Setting = 'Last Modified'; Value = if ($Policy.lastModifiedDateTime) { ([datetime]$Policy.lastModifiedDateTime).ToString('yyyy-MM-dd') } else { '--' } }) | Out-Null

                                # Helper: map boolean compliance setting to Intune-accurate label
                                # $true = enforcement is ON (Required / Enabled / Blocked)
                                # $false = enforcement is OFF (Not Required / Not Enabled / Not Blocked)
                                $BoolToRequired  = { param($v) if ($v) { 'Required' }        else { 'Not Required' } }
                                $BoolToEnabled   = { param($v) if ($v) { 'Enabled' }         else { 'Not Enabled' } }
                                $BoolToBlocked   = { param($v) if ($v) { 'Blocked' }         else { 'Not Blocked' } }
                                $BoolToCompliant = { param($v) if ($v) { 'Required (Compliant)' } else { 'Not Required' } }

                                # Helper: map passwordRequiredType / passcodeRequiredType enum strings
                                $PasswordTypeLabel = {
                                    param($v)
                                    switch ($v) {
                                        'deviceDefault'          { 'Device Default' }
                                        'alphanumeric'           { 'Alphanumeric' }
                                        'numeric'                { 'Numeric' }
                                        'alphabetic'             { 'Alphabetic' }
                                        'numericComplex'         { 'Numeric Complex' }
                                        'anyType'                { 'Any Type' }
                                        'lowSecurityBiometric'   { 'Low Security Biometric' }
                                        default                  { $v }
                                    }
                                }

                                # Helper: map deviceThreatProtectionRequiredSecurityLevel enum
                                $ThreatLevelLabel = {
                                    param($v)
                                    switch ($v) {
                                        'unavailable'  { 'Not Configured' }
                                        'secured'      { 'Secured' }
                                        'low'          { 'Low' }
                                        'medium'       { 'Medium' }
                                        'high'         { 'High' }
                                        'notSet'       { 'Not Configured' }
                                        default        { if ($v) { $v } else { 'Not Configured' } }
                                    }
                                }

                                # Platform-specific settings
                                if ($OdataType -like '*Windows*') {
                                    if ($null -ne $Policy.bitLockerEnabled)           { $DetailObj.Add([pscustomobject]@{ Setting = 'BitLocker';                     Value = (& $BoolToRequired  $Policy.bitLockerEnabled) }) | Out-Null }
                                    if ($null -ne $Policy.codeIntegrityEnabled)       { $DetailObj.Add([pscustomobject]@{ Setting = 'Code Integrity';                Value = (& $BoolToRequired  $Policy.codeIntegrityEnabled) }) | Out-Null }
                                    if ($null -ne $Policy.secureBootEnabled)          { $DetailObj.Add([pscustomobject]@{ Setting = 'Secure Boot';                   Value = (& $BoolToRequired  $Policy.secureBootEnabled) }) | Out-Null }
                                    if ($null -ne $Policy.antivirusRequired)          { $DetailObj.Add([pscustomobject]@{ Setting = 'Antivirus';                     Value = (& $BoolToRequired  $Policy.antivirusRequired) }) | Out-Null }
                                    if ($null -ne $Policy.antispywareRequired)        { $DetailObj.Add([pscustomobject]@{ Setting = 'Antispyware';                   Value = (& $BoolToRequired  $Policy.antispywareRequired) }) | Out-Null }
                                    if ($null -ne $Policy.defenderEnabled)            { $DetailObj.Add([pscustomobject]@{ Setting = 'Microsoft Defender Antimalware'; Value = (& $BoolToRequired  $Policy.defenderEnabled) }) | Out-Null }
                                    if ($null -ne $Policy.firewallEnabled)            { $DetailObj.Add([pscustomobject]@{ Setting = 'Firewall';                      Value = (& $BoolToRequired  $Policy.firewallEnabled) }) | Out-Null }
                                    if ($null -ne $Policy.tpmRequired)                { $DetailObj.Add([pscustomobject]@{ Setting = 'TPM';                           Value = (& $BoolToRequired  $Policy.tpmRequired) }) | Out-Null }
                                    if ($null -ne $Policy.passwordRequired)           { $DetailObj.Add([pscustomobject]@{ Setting = 'Password';                      Value = (& $BoolToRequired  $Policy.passwordRequired) }) | Out-Null }
                                    if ($null -ne $Policy.passwordRequiredType -and $Policy.passwordRequiredType -ne 'deviceDefault') {
                                                                                        $DetailObj.Add([pscustomobject]@{ Setting = 'Password Type';                 Value = (& $PasswordTypeLabel $Policy.passwordRequiredType) }) | Out-Null }
                                    if ($null -ne $Policy.passwordMinimumLength -and $Policy.passwordMinimumLength -gt 0) {
                                                                                        $DetailObj.Add([pscustomobject]@{ Setting = 'Min Password Length';           Value = "$($Policy.passwordMinimumLength)" }) | Out-Null }
                                    if ($null -ne $Policy.passwordMinutesOfInactivityBeforeLock -and $Policy.passwordMinutesOfInactivityBeforeLock -gt 0) {
                                                                                        $DetailObj.Add([pscustomobject]@{ Setting = 'Max Inactivity Before Lock (min)'; Value = "$($Policy.passwordMinutesOfInactivityBeforeLock)" }) | Out-Null }
                                    if ($null -ne $Policy.passwordExpirationDays -and $Policy.passwordExpirationDays -gt 0) {
                                                                                        $DetailObj.Add([pscustomobject]@{ Setting = 'Password Expiration (days)';    Value = "$($Policy.passwordExpirationDays)" }) | Out-Null }
                                    if ($null -ne $Policy.passwordPreviousPasswordBlockCount -and $Policy.passwordPreviousPasswordBlockCount -gt 0) {
                                                                                        $DetailObj.Add([pscustomobject]@{ Setting = 'Previous Passwords Blocked';    Value = "$($Policy.passwordPreviousPasswordBlockCount)" }) | Out-Null }
                                    if ($Policy.osMinimumVersion)                     { $DetailObj.Add([pscustomobject]@{ Setting = 'Min OS Version';                Value = $Policy.osMinimumVersion }) | Out-Null }
                                    if ($Policy.osMaximumVersion)                     { $DetailObj.Add([pscustomobject]@{ Setting = 'Max OS Version';                Value = $Policy.osMaximumVersion }) | Out-Null }
                                    if ($null -ne $Policy.earlyLaunchAntiMalwareDriverEnabled) {
                                                                                        $DetailObj.Add([pscustomobject]@{ Setting = 'Early Launch Anti-Malware Driver'; Value = (& $BoolToRequired $Policy.earlyLaunchAntiMalwareDriverEnabled) }) | Out-Null }
                                    if ($null -ne $Policy.deviceThreatProtectionEnabled -and $Policy.deviceThreatProtectionEnabled) {
                                        $threatLevel = if ($Policy.deviceThreatProtectionRequiredSecurityLevel) { (& $ThreatLevelLabel $Policy.deviceThreatProtectionRequiredSecurityLevel) } else { 'Secured' }
                                        $DetailObj.Add([pscustomobject]@{ Setting = 'Device Threat Protection';      Value = 'Required' }) | Out-Null
                                        $DetailObj.Add([pscustomobject]@{ Setting = 'Required Threat Level';         Value = $threatLevel }) | Out-Null
                                    }
                                }
                                if ($OdataType -like '*Ios*') {
                                    if ($null -ne $Policy.passcodeRequired)               { $DetailObj.Add([pscustomobject]@{ Setting = 'Passcode';                      Value = (& $BoolToRequired  $Policy.passcodeRequired) }) | Out-Null }
                                    if ($null -ne $Policy.passcodeRequiredType -and $Policy.passcodeRequiredType -ne 'deviceDefault') {
                                                                                           $DetailObj.Add([pscustomobject]@{ Setting = 'Passcode Type';                  Value = (& $PasswordTypeLabel $Policy.passcodeRequiredType) }) | Out-Null }
                                    if ($null -ne $Policy.passcodeMinimumLength -and $Policy.passcodeMinimumLength -gt 0) {
                                                                                           $DetailObj.Add([pscustomobject]@{ Setting = 'Min Passcode Length';             Value = "$($Policy.passcodeMinimumLength)" }) | Out-Null }
                                    if ($null -ne $Policy.passcodeMinutesOfInactivityBeforeLock -and $Policy.passcodeMinutesOfInactivityBeforeLock -gt 0) {
                                                                                           $DetailObj.Add([pscustomobject]@{ Setting = 'Max Inactivity Before Lock (min)'; Value = "$($Policy.passcodeMinutesOfInactivityBeforeLock)" }) | Out-Null }
                                    if ($null -ne $Policy.passcodeExpirationDays -and $Policy.passcodeExpirationDays -gt 0) {
                                                                                           $DetailObj.Add([pscustomobject]@{ Setting = 'Passcode Expiration (days)';      Value = "$($Policy.passcodeExpirationDays)" }) | Out-Null }
                                    if ($null -ne $Policy.passcodePreviousPasscodeBlockCount -and $Policy.passcodePreviousPasscodeBlockCount -gt 0) {
                                                                                           $DetailObj.Add([pscustomobject]@{ Setting = 'Previous Passcodes Blocked';      Value = "$($Policy.passcodePreviousPasscodeBlockCount)" }) | Out-Null }
                                    if ($null -ne $Policy.jailbrokenDevice)               { $DetailObj.Add([pscustomobject]@{ Setting = 'Jailbroken Devices';             Value = (& $BoolToBlocked   $Policy.jailbrokenDevice) }) | Out-Null }
                                    if ($null -ne $Policy.managedEmailProfileRequired)    { $DetailObj.Add([pscustomobject]@{ Setting = 'Managed Email Profile';          Value = (& $BoolToRequired  $Policy.managedEmailProfileRequired) }) | Out-Null }
                                    if ($null -ne $Policy.deviceThreatProtectionEnabled -and $Policy.deviceThreatProtectionEnabled) {
                                        $threatLevel = if ($Policy.deviceThreatProtectionRequiredSecurityLevel) { (& $ThreatLevelLabel $Policy.deviceThreatProtectionRequiredSecurityLevel) } else { 'Secured' }
                                        $DetailObj.Add([pscustomobject]@{ Setting = 'Device Threat Protection';      Value = 'Required' }) | Out-Null
                                        $DetailObj.Add([pscustomobject]@{ Setting = 'Required Threat Level';         Value = $threatLevel }) | Out-Null
                                    } elseif ($null -ne $Policy.deviceThreatProtectionEnabled) {
                                        $DetailObj.Add([pscustomobject]@{ Setting = 'Device Threat Protection';      Value = 'Not Required' }) | Out-Null
                                    }
                                    if ($Policy.osMinimumVersion)                         { $DetailObj.Add([pscustomobject]@{ Setting = 'Min OS Version';               Value = $Policy.osMinimumVersion }) | Out-Null }
                                    if ($Policy.osMaximumVersion)                         { $DetailObj.Add([pscustomobject]@{ Setting = 'Max OS Version';               Value = $Policy.osMaximumVersion }) | Out-Null }
                                }
                                if ($OdataType -like '*Android*') {
                                    if ($null -ne $Policy.passwordRequired)               { $DetailObj.Add([pscustomobject]@{ Setting = 'Password';                     Value = (& $BoolToRequired  $Policy.passwordRequired) }) | Out-Null }
                                    if ($null -ne $Policy.passwordRequiredType -and $Policy.passwordRequiredType -ne 'deviceDefault') {
                                                                                           $DetailObj.Add([pscustomobject]@{ Setting = 'Password Type';                 Value = (& $PasswordTypeLabel $Policy.passwordRequiredType) }) | Out-Null }
                                    if ($null -ne $Policy.passwordMinimumLength -and $Policy.passwordMinimumLength -gt 0) {
                                                                                           $DetailObj.Add([pscustomobject]@{ Setting = 'Min Password Length';           Value = "$($Policy.passwordMinimumLength)" }) | Out-Null }
                                    if ($null -ne $Policy.passwordMinutesOfInactivityBeforeLock -and $Policy.passwordMinutesOfInactivityBeforeLock -gt 0) {
                                                                                           $DetailObj.Add([pscustomobject]@{ Setting = 'Max Inactivity Before Lock (min)'; Value = "$($Policy.passwordMinutesOfInactivityBeforeLock)" }) | Out-Null }
                                    if ($null -ne $Policy.passwordExpirationDays -and $Policy.passwordExpirationDays -gt 0) {
                                                                                           $DetailObj.Add([pscustomobject]@{ Setting = 'Password Expiration (days)';    Value = "$($Policy.passwordExpirationDays)" }) | Out-Null }
                                    if ($null -ne $Policy.passwordPreviousPasswordBlockCount -and $Policy.passwordPreviousPasswordBlockCount -gt 0) {
                                                                                           $DetailObj.Add([pscustomobject]@{ Setting = 'Previous Passwords Blocked';    Value = "$($Policy.passwordPreviousPasswordBlockCount)" }) | Out-Null }
                                    if ($null -ne $Policy.securityBlockJailbrokenDevices) { $DetailObj.Add([pscustomobject]@{ Setting = 'Rooted Devices';               Value = (& $BoolToBlocked   $Policy.securityBlockJailbrokenDevices) }) | Out-Null }
                                    if ($null -ne $Policy.storageRequireEncryption)       { $DetailObj.Add([pscustomobject]@{ Setting = 'Storage Encryption';           Value = (& $BoolToRequired  $Policy.storageRequireEncryption) }) | Out-Null }
                                    if ($null -ne $Policy.securityRequireSafetyNetAttestationBasicIntegrity) {
                                                                                           $DetailObj.Add([pscustomobject]@{ Setting = 'SafetyNet Basic Integrity';     Value = (& $BoolToRequired  $Policy.securityRequireSafetyNetAttestationBasicIntegrity) }) | Out-Null }
                                    if ($null -ne $Policy.securityRequireSafetyNetAttestationCertifiedDevice) {
                                                                                           $DetailObj.Add([pscustomobject]@{ Setting = 'SafetyNet Certified Device';    Value = (& $BoolToRequired  $Policy.securityRequireSafetyNetAttestationCertifiedDevice) }) | Out-Null }
                                    if ($null -ne $Policy.deviceThreatProtectionEnabled -and $Policy.deviceThreatProtectionEnabled) {
                                        $threatLevel = if ($Policy.deviceThreatProtectionRequiredSecurityLevel) { (& $ThreatLevelLabel $Policy.deviceThreatProtectionRequiredSecurityLevel) } else { 'Secured' }
                                        $DetailObj.Add([pscustomobject]@{ Setting = 'Device Threat Protection';      Value = 'Required' }) | Out-Null
                                        $DetailObj.Add([pscustomobject]@{ Setting = 'Required Threat Level';         Value = $threatLevel }) | Out-Null
                                    } elseif ($null -ne $Policy.deviceThreatProtectionEnabled) {
                                        $DetailObj.Add([pscustomobject]@{ Setting = 'Device Threat Protection';      Value = 'Not Required' }) | Out-Null
                                    }
                                    if ($Policy.osMinimumVersion)                         { $DetailObj.Add([pscustomobject]@{ Setting = 'Min OS Version';              Value = $Policy.osMinimumVersion }) | Out-Null }
                                    if ($Policy.osMaximumVersion)                         { $DetailObj.Add([pscustomobject]@{ Setting = 'Max OS Version';              Value = $Policy.osMaximumVersion }) | Out-Null }
                                }
                                if ($OdataType -like '*MacOs*') {
                                    if ($null -ne $Policy.passwordRequired)               { $DetailObj.Add([pscustomobject]@{ Setting = 'Password';                     Value = (& $BoolToRequired  $Policy.passwordRequired) }) | Out-Null }
                                    if ($null -ne $Policy.passwordRequiredType -and $Policy.passwordRequiredType -ne 'deviceDefault') {
                                                                                           $DetailObj.Add([pscustomobject]@{ Setting = 'Password Type';                 Value = (& $PasswordTypeLabel $Policy.passwordRequiredType) }) | Out-Null }
                                    if ($null -ne $Policy.passwordMinimumLength -and $Policy.passwordMinimumLength -gt 0) {
                                                                                           $DetailObj.Add([pscustomobject]@{ Setting = 'Min Password Length';           Value = "$($Policy.passwordMinimumLength)" }) | Out-Null }
                                    if ($null -ne $Policy.storageRequireEncryption)       { $DetailObj.Add([pscustomobject]@{ Setting = 'Storage Encryption';           Value = (& $BoolToRequired  $Policy.storageRequireEncryption) }) | Out-Null }
                                    if ($null -ne $Policy.firewallEnabled)                { $DetailObj.Add([pscustomobject]@{ Setting = 'Firewall';                     Value = (& $BoolToRequired  $Policy.firewallEnabled) }) | Out-Null }
                                    if ($null -ne $Policy.gatekeeperAllowedAppSource -and $Policy.gatekeeperAllowedAppSource -ne 'notConfigured') {
                                                                                           $DetailObj.Add([pscustomobject]@{ Setting = 'Gatekeeper Allowed App Source'; Value = $Policy.gatekeeperAllowedAppSource }) | Out-Null }
                                    if ($Policy.osMinimumVersion)                         { $DetailObj.Add([pscustomobject]@{ Setting = 'Min OS Version';               Value = $Policy.osMinimumVersion }) | Out-Null }
                                    if ($Policy.osMaximumVersion)                         { $DetailObj.Add([pscustomobject]@{ Setting = 'Max OS Version';               Value = $Policy.osMaximumVersion }) | Out-Null }
                                }
                                $DetailTableParams = @{ Name = "Policy Settings - $($Policy.displayName)"; ColumnWidths = 40, 60 }
                                if ($Report.ShowTableCaptions) { $DetailTableParams['Caption'] = "- $($DetailTableParams.Name)" }
                                $DetailObj | Table @DetailTableParams

                                if ($Policy.assignments -and @($Policy.assignments).Count -gt 0) {
                                    BlankLine
                                    $AssignObj = [System.Collections.ArrayList]::new()
                                    foreach ($Assignment in $Policy.assignments) {
                                        $Target = $Assignment.target
                                        $TargetType = ($Target.'@odata.type' -replace '#microsoft.graph.', '') -replace 'AssignmentTarget', ''
                                        $assignInObj = [ordered] @{
                                            'Target Type' = $TargetType
                                            'Group ID'    = if ($Target.groupId) { $Target.groupId } else { '--' }
                                        }
                                        $AssignObj.Add([pscustomobject]$assignInObj) | Out-Null
                                    }
                                    $AssignTableParams = @{ Name = "Assignments - $($Policy.displayName)"; ColumnWidths = 40, 60 }
                                    if ($Report.ShowTableCaptions) { $AssignTableParams['Caption'] = "- $($AssignTableParams.Name)" }
                                    $AssignObj | Table @AssignTableParams
                                }
                            }
                        }
                    }
                    #endregion

                    #region ACSC E8 Assessment
                    if ($script:IncludeACSCe8) {
                        BlankLine
                        Paragraph "ACSC Essential Eight Maturity Level Assessment -- Device Compliance Policies:"
                        BlankLine
                        try {
                            $_ComplianceVars = @{
                                'TotalCompliancePolicies'       = @($Policies).Count
                                'UnassignedPolicies'            = $UnassignedPolicies
                                'WindowsBitLockerRequired'      = $WindowsBitLockerRequired
                                'WindowsDefenderRequired'       = $WindowsDefenderRequired
                                'WindowsOsMinVersionConfigured' = $WindowsOsMinVersionConfigured
                                'MobileOsMinVersionConfigured'  = $MobileOsMinVersionConfigured
                                # Device metrics populated later in Devices section -- stub here
                                'NonCompliantDevices'   = if ($null -ne $script:NonCompliantCount)  { $script:NonCompliantCount }  else { 0 }
                                'NonCompliantPct'       = if ($null -ne $script:NonCompliantPct)    { $script:NonCompliantPct }    else { 0 }
                                'StaleDevices'          = if ($null -ne $script:StaleDeviceCount)   { $script:StaleDeviceCount }   else { 0 }
                                'StaleDevicePct'        = if ($null -ne $script:StaleDevicePct)     { $script:StaleDevicePct }     else { 0 }
                                'MobileEncryptionRequired'      = $MobileEncryptionRequired
                            }
                            $E8Checks = Build-AbrIntuneComplianceChecks `
                                -Definitions (Get-AbrIntuneE8Checks -Section 'DeviceCompliance') `
                                -Framework E8 `
                                -CallerVariables $_ComplianceVars
                            New-AbrIntuneE8AssessmentTable -Checks $E8Checks -Name 'Device Compliance' -TenantId $TenantId
                            if ($E8Checks) { $null = $script:E8AllChecks.AddRange([object[]](@($E8Checks | Select-Object @{N='Section';E={'DeviceCompliance'}}, ML, Control, Status, Detail))) }
                        } catch {
                                if (Test-AbrGraphForbidden -ErrorRecord $_) {
                                    Write-AbrPermissionError -Section 'E8 Device Compliance Assessment' -RequiredRole 'Intune Service Administrator or Global Administrator'
                                } else {
                                    Write-AbrSectionError -Section 'E8 Device Compliance Assessment' -Message "$($_.Exception.Message)"
                                }
                            }
                    }
                    #endregion

                    #region CIS Baseline Assessment
                    if ($script:IncludeCISBaseline) {
                        BlankLine
                        Paragraph "CIS Microsoft 365 Foundations Benchmark Assessment -- Device Compliance Policies:"
                        BlankLine
                        try {
                            $_ComplianceVars = @{
                                'TotalCompliancePolicies'   = @($Policies).Count
                                'UnassignedPolicies'        = $UnassignedPolicies
                                'NonCompliantDevices'       = if ($null -ne $script:NonCompliantCount) { $script:NonCompliantCount } else { 0 }
                                'NonCompliantPct'           = if ($null -ne $script:NonCompliantPct)   { $script:NonCompliantPct }   else { 0 }
                                'WindowsBitLockerRequired'  = $WindowsBitLockerRequired
                                'MobileEncryptionRequired'  = $MobileEncryptionRequired
                            }
                            $CISChecks = Build-AbrIntuneComplianceChecks `
                                -Definitions (Get-AbrIntuneCISChecks -Section 'DeviceCompliance') `
                                -Framework CIS `
                                -CallerVariables $_ComplianceVars
                            New-AbrIntuneCISAssessmentTable -Checks $CISChecks -Name 'Device Compliance' -TenantId $TenantId
                            if ($CISChecks) { $null = $script:CISAllChecks.AddRange([object[]](@($CISChecks | Select-Object @{N='Section';E={'DeviceCompliance'}}, CISControl, Level, Status, Detail))) }
                        } catch {
                                if (Test-AbrGraphForbidden -ErrorRecord $_) {
                                    Write-AbrPermissionError -Section 'CIS Device Compliance Assessment' -RequiredRole 'Intune Service Administrator or Global Administrator'
                                } else {
                                    Write-AbrSectionError -Section 'CIS Device Compliance Assessment' -Message "$($_.Exception.Message)"
                                }
                            }
                    }
                    #endregion

                } else {
                    Paragraph "No Device Compliance Policies found in tenant $TenantId."
                }

            } catch {
                    if (Test-AbrGraphForbidden -ErrorRecord $_) {
                        Write-AbrPermissionError -Section 'Device Compliance Policies' -RequiredRole 'Intune Service Administrator or Global Administrator'
                    } else {
                        Write-AbrSectionError -Section 'Device Compliance Policies' -Message "$($_.Exception.Message)"
                    }
                }
        }
    }

    end {
        Show-AbrDebugExecutionTime -End -TitleMessage 'Device Compliance'
    }
}