SHELL/2.4.4.ps1

$CheckId = "2.4.4"
$Title = "Ensure Zero-hour auto purge for Microsoft Teams is on"
$Level = "L1"
$BenchmarkType = "Automated"
$AuditCommands = [pscustomobject]@{
    TeamsProtectionPolicy = "Get-TeamsProtectionPolicy | Format-List ZapEnabled"
    TeamsProtectionPolicyRule = "Get-TeamsProtectionPolicyRule | Format-List ExceptIf*"
}

function Get-AvailableCommandName {
    param(
        [string[]]$Names
    )

    foreach ($Name in $Names) {
        if (Get-Command -Name $Name -ErrorAction SilentlyContinue) {
            return $Name
        }
    }

    return $null
}

function Get-NonEmptyValues {
    param(
        [AllowNull()]
        [object]$Value
    )

    $Values = @()
    foreach ($Item in @($Value)) {
        if ($null -eq $Item) {
            continue
        }

        $Text = ([string]$Item).Trim()
        if ([string]::IsNullOrWhiteSpace($Text)) {
            continue
        }

        if ($Text -eq "{}") {
            continue
        }

        if ($Text -eq "False") {
            continue
        }

        $Values += $Text
    }

    return @($Values | Select-Object -Unique)
}

try {
    $FailureReasons = [System.Collections.Generic.List[string]]::new()
    $ManualReasons = [System.Collections.Generic.List[string]]::new()
    $ErrorReasons = [System.Collections.Generic.List[string]]::new()

    $PolicyCmd = Get-AvailableCommandName -Names @("Get-TeamsProtectionPolicy")
    $RuleCmd = Get-AvailableCommandName -Names @("Get-TeamsProtectionPolicyRule")

    if (-not $PolicyCmd) {
        $ManualReasons.Add("Get-TeamsProtectionPolicy cmdlet is unavailable in the current session.")
    }

    if (-not $RuleCmd) {
        $ManualReasons.Add("Get-TeamsProtectionPolicyRule cmdlet is unavailable in the current session.")
    }

    $Policies = @()
    $Rules = @()

    if ($PolicyCmd) {
        $Policies = @(& $PolicyCmd)
        if (@($Policies).Count -eq 0) {
            $ErrorReasons.Add("Get-TeamsProtectionPolicy returned no result.")
        }
    }

    if ($RuleCmd) {
        $Rules = @(& $RuleCmd)
    }

    $PolicyZapReport = @()
    $ZapEnabledState = $null
    if (@($Policies).Count -gt 0) {
        $PolicyZapReport = foreach ($Policy in $Policies) {
            $Zap = $null
            if ($null -ne $Policy.PSObject.Properties['ZapEnabled']) {
                $Zap = [bool]$Policy.ZapEnabled
            }

            [pscustomobject]@{
                Identity = [string]$Policy.Identity
                Name = [string]$Policy.Name
                ZapEnabled = $Zap
            }
        }

        $KnownZap = @($PolicyZapReport | Where-Object { $null -ne $_.ZapEnabled })
        if (@($KnownZap).Count -eq 0) {
            $ManualReasons.Add("Unable to determine ZapEnabled value from Teams protection policy output.")
        }
        else {
            $ZapEnabledState = @($KnownZap | Where-Object { $_.ZapEnabled -eq $false }).Count -eq 0
            if (-not $ZapEnabledState) {
                $FailureReasons.Add("ZapEnabled is not True on all returned Teams protection policies.")
            }
        }
    }

    $ExclusionFindings = @()
    if ($RuleCmd -and @($Rules).Count -gt 0) {
        foreach ($Rule in $Rules) {
            $ExceptProps = @($Rule.PSObject.Properties | Where-Object { $_.Name -like "ExceptIf*" })
            $NonEmptyByProperty = [ordered]@{}

            foreach ($Prop in $ExceptProps) {
                $Values = Get-NonEmptyValues -Value $Prop.Value
                if (@($Values).Count -gt 0) {
                    $NonEmptyByProperty[$Prop.Name] = @($Values)
                }
            }

            if ($NonEmptyByProperty.Count -gt 0) {
                $ExclusionFindings += [pscustomobject]@{
                    Identity = [string]$Rule.Identity
                    Name = [string]$Rule.Name
                    Exclusions = [pscustomobject]$NonEmptyByProperty
                }
            }
        }
    }

    if ($RuleCmd -and @($ExclusionFindings).Count -gt 0 -and $FailureReasons.Count -eq 0 -and $ErrorReasons.Count -eq 0) {
        $ManualReasons.Add("One or more Teams protection exclusions (ExceptIf*) are configured and must be manually justified per CIS guidance.")
    }

    $Status = "PASS"
    $Pass = $true
    $ErrorMessage = $null

    if ($ErrorReasons.Count -gt 0) {
        $Status = "ERROR"
        $Pass = $null
        $ErrorMessage = ($ErrorReasons -join " ")
    }
    elseif ($FailureReasons.Count -gt 0) {
        $Status = "FAIL"
        $Pass = $false
        $ErrorMessage = ($FailureReasons -join " ")
    }
    elseif ($ManualReasons.Count -gt 0) {
        $Status = "MANUAL_REVIEW"
        $Pass = $null
        $ErrorMessage = "Manual review required: $($ManualReasons -join " ")"
    }

    [pscustomobject]@{
        CheckId = $CheckId
        Title = $Title
        Level = $Level
        BenchmarkType = $BenchmarkType
        Status = $Status
        Pass = $Pass
        Evidence = [pscustomobject]@{
            AuditCommands = $AuditCommands
            AuditUiReference = @(
                "Microsoft Defender > Settings > Email & collaboration > Microsoft Teams protection",
                "Ensure Zero-hour auto purge (ZAP) is On",
                "Review exclusions under 'Exclude these participants' and confirm they are justified"
            )
            PolicyCommandUsed = $PolicyCmd
            RuleCommandUsed = $RuleCmd
            PolicyCount = @($Policies).Count
            RuleCount = @($Rules).Count
            PolicyZapReport = @($PolicyZapReport)
            ExclusionFindings = @($ExclusionFindings)
            ManualReviewReasons = @($ManualReasons)
            SourceDocument = "CIS_Microsoft_365_Foundations_Benchmark_v6.0.1"
        }
        Error = $ErrorMessage
        Timestamp = Get-Date
    }
}
catch {
    [pscustomobject]@{
        CheckId = $CheckId
        Title = $Title
        Level = $Level
        BenchmarkType = $BenchmarkType
        Status = "ERROR"
        Pass = $null
        Evidence = [pscustomobject]@{
            AuditCommands = $AuditCommands
            SourceDocument = "CIS_Microsoft_365_Foundations_Benchmark_v6.0.1"
        }
        Error = $_.Exception.Message
        Timestamp = Get-Date
    }
}