Src/Private/Get-AbrIntuneWindowsUpdates.ps1

function Get-AbrIntuneWindowsUpdates {
    <#
    .SYNOPSIS
        Documents Windows Update for Business policies — Feature, Quality, and Driver update profiles.
    .DESCRIPTION
        Collects and reports on:
          - Windows Feature Update Profiles (beta/deviceManagement/windowsFeatureUpdateProfiles)
          - Windows Quality Update Profiles (beta/deviceManagement/windowsQualityUpdateProfiles)
          - Windows Driver Update Profiles (beta/deviceManagement/windowsDriverUpdateProfiles)
    .NOTES
        Version: 0.1.0
        Added: v0.2.51 — addresses CRITICAL gaps from gap tracker
    #>

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

    begin {
        Write-PScriboMessage -Message "Collecting Windows Update profiles for $TenantId."
        Show-AbrDebugExecutionTime -Start -TitleMessage 'Windows Updates'
    }

    process {
        #region Windows Feature Update Profiles
        try {
            Write-Host " - Retrieving Windows Feature Update Profiles..."
            $FeatureResp = $null
            try {
                $FeatureResp = Invoke-MgGraphRequest -Method GET `
                    -Uri "$($script:GraphEndpoint)/beta/deviceManagement/windowsFeatureUpdateProfiles?`$expand=assignments" `
                    -ErrorAction Stop
            } catch {
                Write-AbrDebugLog "windowsFeatureUpdateProfiles unavailable: $($_.Exception.Message)" 'WARN' 'WindowsUpdates'
            }
            if ($FeatureResp -and $FeatureResp.value -and @($FeatureResp.value).Count -gt 0) {
                Section -Style Heading2 'Windows Feature Update Profiles' {
                    Paragraph "Feature Update profiles control which Windows version devices upgrade to. The following documents all profiles configured in tenant $TenantId."
                    BlankLine
                    $FeatObj = [System.Collections.ArrayList]::new()
                    foreach ($Prof in ($FeatureResp.value | Sort-Object displayName)) {
                        $assignResolved = Resolve-IntuneAssignments -Assignments $Prof.assignments -CheckMemberCount:$script:CheckEmptyGroups
                        $rolloutLabel = if ($Prof.rolloutSettings) {
                            $rs = $Prof.rolloutSettings
                            $start = if ($rs -is [System.Collections.IDictionary]) { $rs['offerStartDateTimeInUTC'] } else { $rs.offerStartDateTimeInUTC }
                            if ($start) { "From: $(([datetime]$start).ToString('yyyy-MM-dd'))" } else { 'Immediate' }
                        } else { 'Immediate' }
                        $featRow = [ordered] @{
                            'Profile Name'      = $Prof.displayName
                            'Feature Version'   = if ($Prof.featureUpdateVersion) { $Prof.featureUpdateVersion } else { '--' }
                            'Rollout'           = $rolloutLabel
                            'Deferral (days)'   = if ($null -ne $Prof.deferralPeriodInDays) { "$($Prof.deferralPeriodInDays)" } else { '--' }
                            'Included Groups'   = $assignResolved.IncludedGroups
                            'Excluded Groups'   = $assignResolved.ExcludedGroups
                            'Last Modified'     = if ($Prof.lastModifiedDateTime) { ([datetime]$Prof.lastModifiedDateTime).ToString('yyyy-MM-dd') } else { '--' }
                        }
                        $FeatObj.Add([pscustomobject]$featRow) | Out-Null
                    }
                    $FeatParams = @{ Name = "Feature Update Profiles - $TenantId"; ColumnWidths = 22, 14, 14, 10, 18, 14, 8 }
                    if ($Report.ShowTableCaptions) { $FeatParams['Caption'] = "- $($FeatParams.Name)" }
                    $FeatObj | Table @FeatParams
                }
            }
        } catch {
            if (Test-AbrGraphForbidden -ErrorRecord $_) { Write-AbrPermissionError -Section 'Feature Update Profiles' -RequiredRole 'Intune Service Administrator or Global Administrator' }
            else { Write-AbrSectionError -Section 'Feature Update Profiles' -Message "$($_.Exception.Message)" }
        }
        #endregion

        #region Windows Quality Update Profiles (Expedite)
        try {
            Write-Host " - Retrieving Windows Quality Update Profiles..."
            $QualityResp = $null
            try {
                $QualityResp = Invoke-MgGraphRequest -Method GET `
                    -Uri "$($script:GraphEndpoint)/beta/deviceManagement/windowsQualityUpdateProfiles?`$expand=assignments" `
                    -ErrorAction Stop
            } catch {
                Write-AbrDebugLog "windowsQualityUpdateProfiles unavailable: $($_.Exception.Message)" 'WARN' 'WindowsUpdates'
            }
            if ($QualityResp -and $QualityResp.value -and @($QualityResp.value).Count -gt 0) {
                Section -Style Heading2 'Windows Quality Update Profiles (Expedite)' {
                    Paragraph "Quality Update profiles use expedite policies to push deadline-driven security updates. The following documents all profiles configured in tenant $TenantId."
                    BlankLine
                    $QualObj = [System.Collections.ArrayList]::new()
                    foreach ($Prof in ($QualityResp.value | Sort-Object displayName)) {
                        $assignResolved = Resolve-IntuneAssignments -Assignments $Prof.assignments -CheckMemberCount:$script:CheckEmptyGroups
                        $qualRow = [ordered] @{
                            'Profile Name'          = $Prof.displayName
                            'Expedite'              = if ($null -ne $Prof.expeditedUpdateSettings) { 'Yes' } else { 'No' }
                            'Days Until Forced Reboot' = if ($Prof.expeditedUpdateSettings) {
                                                            $eu = $Prof.expeditedUpdateSettings
                                                            $d = if ($eu -is [System.Collections.IDictionary]) { $eu['daysUntilForcedReboot'] } else { $eu.daysUntilForcedReboot }
                                                            if ($null -ne $d) { "$d" } else { '--' }
                                                        } else { '--' }
                            'Included Groups'       = $assignResolved.IncludedGroups
                            'Excluded Groups'       = $assignResolved.ExcludedGroups
                            'Last Modified'         = if ($Prof.lastModifiedDateTime) { ([datetime]$Prof.lastModifiedDateTime).ToString('yyyy-MM-dd') } else { '--' }
                        }
                        $QualObj.Add([pscustomobject]$qualRow) | Out-Null
                    }
                    $QualParams = @{ Name = "Quality Update Profiles - $TenantId"; ColumnWidths = 24, 10, 18, 20, 18, 10 }
                    if ($Report.ShowTableCaptions) { $QualParams['Caption'] = "- $($QualParams.Name)" }
                    $QualObj | Table @QualParams
                }
            }
        } catch {
            if (Test-AbrGraphForbidden -ErrorRecord $_) { Write-AbrPermissionError -Section 'Quality Update Profiles' -RequiredRole 'Intune Service Administrator or Global Administrator' }
            else { Write-AbrSectionError -Section 'Quality Update Profiles' -Message "$($_.Exception.Message)" }
        }
        #endregion

        #region Windows Driver Update Profiles
        try {
            Write-Host " - Retrieving Windows Driver Update Profiles..."
            $DriverResp = $null
            try {
                $DriverResp = Invoke-MgGraphRequest -Method GET `
                    -Uri "$($script:GraphEndpoint)/beta/deviceManagement/windowsDriverUpdateProfiles?`$expand=assignments" `
                    -ErrorAction Stop
            } catch {
                Write-AbrDebugLog "windowsDriverUpdateProfiles unavailable: $($_.Exception.Message)" 'WARN' 'WindowsUpdates'
            }
            if ($DriverResp -and $DriverResp.value -and @($DriverResp.value).Count -gt 0) {
                Section -Style Heading2 'Windows Driver Update Profiles' {
                    Paragraph "Driver Update profiles control automatic vs manual driver approval policies. The following documents all profiles configured in tenant $TenantId."
                    BlankLine
                    $DrvObj = [System.Collections.ArrayList]::new()
                    foreach ($Prof in ($DriverResp.value | Sort-Object displayName)) {
                        $assignResolved = Resolve-IntuneAssignments -Assignments $Prof.assignments -CheckMemberCount:$script:CheckEmptyGroups
                        $approvalLabel = switch ($Prof.approvalType) {
                            'automatic' { 'Automatic' }
                            'manual'    { 'Manual' }
                            default     { if ($Prof.approvalType) { $Prof.approvalType } else { '--' } }
                        }
                        $drvRow = [ordered] @{
                            'Profile Name'      = $Prof.displayName
                            'Approval Type'     = $approvalLabel
                            'Deployment Delay'  = if ($null -ne $Prof.deploymentDeferralInDays) { "$($Prof.deploymentDeferralInDays) days" } else { '--' }
                            'Included Groups'   = $assignResolved.IncludedGroups
                            'Excluded Groups'   = $assignResolved.ExcludedGroups
                            'Last Modified'     = if ($Prof.lastModifiedDateTime) { ([datetime]$Prof.lastModifiedDateTime).ToString('yyyy-MM-dd') } else { '--' }
                        }
                        $DrvObj.Add([pscustomobject]$drvRow) | Out-Null
                    }
                    $DrvParams = @{ Name = "Driver Update Profiles - $TenantId"; ColumnWidths = 25, 14, 15, 20, 18, 8 }
                    if ($Report.ShowTableCaptions) { $DrvParams['Caption'] = "- $($DrvParams.Name)" }
                    $DrvObj | Table @DrvParams
                }
            }
        } catch {
            if (Test-AbrGraphForbidden -ErrorRecord $_) { Write-AbrPermissionError -Section 'Driver Update Profiles' -RequiredRole 'Intune Service Administrator or Global Administrator' }
            else { Write-AbrSectionError -Section 'Driver Update Profiles' -Message "$($_.Exception.Message)" }
        }
        #endregion
    }

    end { Show-AbrDebugExecutionTime -End -TitleMessage 'Windows Updates' }
}