Src/Private/Get-AbrExoQuarantine.ps1

function Get-AbrExoQuarantine {
    <#
    .SYNOPSIS
    Documents Exchange Online quarantine policies and provides a quarantine summary snapshot.
    .NOTES
        Version: 0.1.0
        Author: Pai Wei Sing
    #>

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

    begin {
        Write-PScriboMessage -Message "Collecting Exchange Online Quarantine configuration for $TenantId."
        Show-AbrDebugExecutionTime -Start -TitleMessage 'Quarantine'
    }

    process {
        Section -Style Heading2 'Quarantine Policies' {
            Paragraph "The following section documents quarantine policies that control end-user notification and release permissions for quarantined messages in tenant $TenantId."
            BlankLine

            #region Quarantine Policies
            try {
                Write-Host " - Retrieving quarantine policies..."
                $QuarantinePolicies = Get-QuarantinePolicy -ErrorAction Stop | Sort-Object QuarantinePolicyType, Name

                if ($QuarantinePolicies -and @($QuarantinePolicies).Count -gt 0) {
                    $QpObj = [System.Collections.ArrayList]::new()
                    foreach ($Policy in $QuarantinePolicies) {
                        $qpInObj = [ordered] @{
                            'Policy Name'                   = $Policy.Name
                            'Policy Type'                   = $Policy.QuarantinePolicyType
                            'End-User Spam Notifications'   = $Policy.EndUserSpamNotificationFrequency -gt 0
                            'Notification Frequency (days)' = if ($Policy.EndUserSpamNotificationFrequency) { $Policy.EndUserSpamNotificationFrequency } else { 'Disabled' }
                            'End-User Quarantine Access'    = $Policy.EndUserQuarantinePermissionsValue
                            'Release to All Allowed'        = ($Policy.EndUserQuarantinePermissionsValue -band 4) -gt 0
                            'Delete Allowed'                = ($Policy.EndUserQuarantinePermissionsValue -band 8) -gt 0
                            'Preview Allowed'               = ($Policy.EndUserQuarantinePermissionsValue -band 2) -gt 0
                        }
                        $QpObj.Add([pscustomobject](ConvertTo-HashToYN $qpInObj)) | Out-Null
                    }

                    $null = (& {
                        if ($HealthCheck.ExchangeOnline.AntiSpam) {
                            # Flag policies that allow users to release messages to all recipients
                            $null = ($QpObj | Where-Object { $_.'Release to All Allowed' -eq 'Yes' } | Set-Style -Style Warning | Out-Null)
                        }
                    })

                    $QpTableParams = @{ Name = "Quarantine Policies - $TenantId"; List = $false; ColumnWidths = 18, 14, 12, 13, 13, 10, 10, 10 }
                    if ($Report.ShowTableCaptions) { $QpTableParams['Caption'] = "- $($QpTableParams.Name)" }
                    if ($QpObj.Count -gt 0) { $QpObj | Table @QpTableParams }
                    $script:ExcelSheets['Quarantine Policies'] = $QpObj
                } else {
                    Paragraph "No custom quarantine policies found. Default quarantine policies (AdminOnlyAccessPolicy, DefaultFullAccessPolicy) are in use."
                }
            } catch {
                Write-ExoError 'Quarantine' "Unable to retrieve quarantine policies: $($_.Exception.Message)"
                Paragraph "Unable to retrieve quarantine policy data: $($_.Exception.Message)"
            }
            #endregion

            #region Quarantine Message Summary (InfoLevel 2)
            if ($InfoLevel.AntiSpam -ge 2) {
                try {
                    Write-Host " - Retrieving quarantine message summary..."
                    # Get counts by type for the last 30 days - use a short window to avoid timeouts
                    $QuarantineMessages = Get-QuarantineMessage -PageSize 100 -ErrorAction SilentlyContinue

                    if ($QuarantineMessages) {
                        $QmSummary = $QuarantineMessages | Group-Object -Property Type | Sort-Object Count -Descending
                        $QmObj = [System.Collections.ArrayList]::new()
                        foreach ($Group in $QmSummary) {
                            $qmInObj = [ordered] @{
                                'Message Type'  = $Group.Name
                                'Count'         = $Group.Count
                            }
                            $QmObj.Add([pscustomobject]$qmInObj) | Out-Null
                        }

                        Section -Style Heading3 'Quarantine Message Snapshot (last 100)' {
                            Paragraph "The following table shows a snapshot of the most recent 100 quarantined messages grouped by type in tenant $TenantId."
                            BlankLine
                            $QmTableParams = @{ Name = "Quarantine Snapshot - $TenantId"; List = $false; ColumnWidths = 60, 40 }
                            if ($Report.ShowTableCaptions) { $QmTableParams['Caption'] = "- $($QmTableParams.Name)" }
                            if ($QmObj.Count -gt 0) { $QmObj | Table @QmTableParams }
                        }
                    }
                } catch {
                    # Quarantine message retrieval is best-effort, don't fail the section
                    Write-AbrDebugLog "Quarantine message snapshot skipped: $($_.Exception.Message)" 'DEBUG' 'Quarantine'
                }
            }
            #endregion
        }
    }

    end {
        Show-AbrDebugExecutionTime -End -TitleMessage 'Quarantine'
    }
}