Checks/EXO/MET-EXO008-QuarantineRetention.ps1

try {
    $spamRules    = @(Get-HostedContentFilterRule    -ErrorAction Stop | Sort-Object Priority)
    $spamPolicies = @(Get-HostedContentFilterPolicy  -ErrorAction Stop)
}
catch {
    New-METCheckResult -CheckId 'MET-EXO008' -Category EXO -Name 'Quarantine Retention' `
        -Result Fail -Severity Low -AffectedObject 'Hosted Content Filter Policies' `
        -Finding 'Unable to retrieve anti-spam policies' `
        -Recommendation 'Ensure the account has Security Reader or higher permissions.' `
        -ReferenceUrl 'https://aka.ms/mdo-quarantine-retention' -ErrorMessage $_.ToString()
    return
}

$ruleByPolicy = @{}
foreach ($r in $spamRules) { $ruleByPolicy[$r.HostedContentFilterPolicy] = $r }

$antiPhishNote = 'This same retention value also governs anti-phishing quarantine (spoof/impersonation) for the same recipients - there is no separate anti-phish retention setting.'

foreach ($policy in $spamPolicies) {
    $isDefault = $policy.IsDefault -eq $true
    $rule      = $ruleByPolicy[$policy.Name]

    if (-not $isDefault -and (-not $rule -or $rule.State -ne 'Enabled')) { continue }

    $presetTier = Get-METPresetSecurityPolicyTier -Name $policy.Name
    $isPreset = [bool]$presetTier

    $scope = if ($isDefault) {
        'catch-all (default - applies to all uncovered recipients)'
    } elseif ($isPreset) {
        "$presetTier preset security policy"
    } else {
        Get-METRuleScope -Rule $rule
    }
    $label     = "$($policy.Name) [$scope]"
    $retention = $policy.QuarantineRetentionPeriod

    if ($isPreset) {
        if ($null -ne $retention -and $retention -lt 30) {
            New-METCheckResult -CheckId 'MET-EXO008' -Category EXO -Name 'Quarantine Retention' `
                -Result Warning -Severity Low -AffectedObject $label `
                -Finding "Quarantine retention period is $retention days, below the 30-day value Microsoft guarantees for $presetTier preset security policies. $antiPhishNote" `
                -Recommendation "Preset-generated policies are Microsoft-managed and not editable - Set-HostedContentFilterPolicy will error against '$($policy.Name)'. Treat this as an unexpected Microsoft-side anomaly to investigate (e.g. via a support request), not an admin misconfiguration to remediate." `
                -ReferenceUrl 'https://aka.ms/mdo-quarantine-retention'
        }
        else {
            New-METCheckResult -CheckId 'MET-EXO008' -Category EXO -Name 'Quarantine Retention' `
                -Result Pass -Severity Low -AffectedObject $label `
                -Finding "Quarantine retention period is $retention days, fixed by the $presetTier preset security policy and not admin-configurable. $antiPhishNote" `
                -ReferenceUrl 'https://aka.ms/mdo-quarantine-retention'
        }
        continue
    }

    if ($null -ne $retention -and $retention -lt 30) {
        New-METCheckResult -CheckId 'MET-EXO008' -Category EXO -Name 'Quarantine Retention' `
            -Result Fail -Severity Low -AffectedObject $label `
            -Finding "Quarantine retention period is $retention days - Microsoft recommends 30 days for Standard and Strict profiles. $antiPhishNote" `
            -Recommendation "Run: Set-HostedContentFilterPolicy -Identity '$($policy.Name)' -QuarantineRetentionPeriod 30. A 30-day retention window gives end users and admins adequate time to review and release false positives before messages are purged." `
            -ReferenceUrl 'https://aka.ms/mdo-quarantine-retention'
    }
    else {
        New-METCheckResult -CheckId 'MET-EXO008' -Category EXO -Name 'Quarantine Retention' `
            -Result Pass -Severity Low -AffectedObject $label `
            -Finding "Quarantine retention period is $retention days. $antiPhishNote" `
            -ReferenceUrl 'https://aka.ms/mdo-quarantine-retention'
    }
}