public/cisa/exchange/Test-MtCisaDmarcRecordReject.ps1

<#
.SYNOPSIS
    Checks state of DMARC records for all exo domains

.DESCRIPTION
    The DMARC message rejection option SHALL be p=reject.

.EXAMPLE
    Test-MtCisaDmarcRecordReject

    Returns true if DMARC record with reject policy exists for every domain

.LINK
    https://maester.dev/docs/commands/Test-MtCisaDmarcRecordReject
#>

function Test-MtCisaDmarcRecordReject {
    [CmdletBinding()]
    [OutputType([bool])]
    param(
        # Check 2nd Level Domains Explicitly per CISA
        [switch]$Strict
    )

    if(!(Test-MtConnection ExchangeOnline)){
        Add-MtTestResultDetail -SkippedBecause NotConnectedExchange
        return $null
    }

    $acceptedDomains = Get-MtExo -Request AcceptedDomain
    <# Parked domains should have DMARC with reject policy
    $sendingDomains = $acceptedDomains | Where-Object {`
        -not $_.SendingFromDomainDisabled
    }
    #>

    $expandedDomains = @()
    foreach($domain in $acceptedDomains){
        # If it's the coexistence domain (contoso.mail.onmicrosoft.com), skip the 2nd level check.
        # If it's the initial domain (contoso.onmicrosoft.com), skip the 2nd level check. We cannot manage onmicrosoft.com.
        if ($domain.IsCoexistenceDomain -or $domain.InitialDomain) {
            $expandedDomains += [PSCustomObject]@{
                DomainName = $domain.DomainName
                IsCoexistenceDomain = $domain.IsCoexistenceDomain
            }
            continue
        }

        #This regex does NOT capture for third level domain scenarios
        #e.g., example.co.uk; example.ny.us;
        $matchDomain = "(?:^|\.)(?'second'\w+.\w+$)"
        $dmarcMatch = $domain.domainname -match $matchDomain
        if($dmarcMatch){
            $expandedDomains += [PSCustomObject]@{
                DomainName = $Matches.second
                IsCoexistenceDomain = $domain.IsCoexistenceDomain
            }
            if($domain.domainname -ne $Matches.second){
                $expandedDomains += [PSCustomObject]@{
                    DomainName = $domain.domainname
                    IsCoexistenceDomain = $domain.IsCoexistenceDomain
                }
            }
        }else{
            $expandedDomains += [PSCustomObject]@{
                DomainName = $domain.domainname
                IsCoexistenceDomain = $domain.IsCoexistenceDomain
            }
        }
    }

    # Sort and remove duplicate Domains
    $expandedDomains = $expandedDomains |  Sort-Object DomainName, IsCoexistenceDomain -Unique

    $dmarcRecords = @()
    foreach($domain in $expandedDomains){
        $dmarcRecord = Get-MailAuthenticationRecord -DomainName $domain.DomainName -Records DMARC
        $dmarcRecord | Add-Member -MemberType NoteProperty -Name "pass" -Value "Failed"
        $dmarcRecord | Add-Member -MemberType NoteProperty -Name "reason" -Value ""

        $checkType = $dmarcRecord.dmarcRecord.GetType().Name -eq "DMARCRecord"

        if($domain.IsCoexistenceDomain){
            $dmarcRecord.pass = "Skipped"
            $dmarcRecord.reason = "Not applicable for coexistence domain"
        }elseif($checkType -and $dmarcRecord.dmarcRecord.policy -eq "reject"){
            $dmarcRecord.pass = "Passed"
        }elseif($checkType -and $dmarcRecord.dmarcRecord.policy -ne "reject"){
            $dmarcRecord.reason = "Policy is not reject"
        }elseif($checkType -and $dmarcRecord.dmarcRecord.policySubdomain -in @("none","quarantine")){
            $dmarcRecord.reason = "Subdomain policy is not reject"
        }elseif($dmarcRecord.dmarcRecord -like "*not available"){
            $dmarcRecord.pass = "Skipped"
            $dmarcRecord.reason = $dmarcRecord.dmarcRecord
        }else{
            $dmarcRecord.reason = $dmarcRecord.dmarcRecord
        }

        $dmarcRecords += $dmarcRecord
    }

    if("Failed" -in $dmarcRecords.pass -and $Strict){
        $testResult = $false
    }elseif("Failed" -in $dmarcRecords.pass -and -not $Strict){
        if("Failed" -in ($dmarcRecords|Where-Object{$_.domain -in $acceptedDomains.DomainName}).pass){
            $testResult = $false
        }else{
            $testResult = $true
        }
    }elseif("Failed" -notin $dmarcRecords.pass -and "Passed" -notin $dmarcRecords.pass){
        Add-MtTestResultDetail -SkippedBecause NotSupported
        return $null
    }else{
        $testResult = $true
    }

    if($testResult){
        $testResultMarkdown = "Well done. Your tenant's domains have a DMARC record with reject policy. Review report targets.`n`n%TestResult%"
    }else{
        $testResultMarkdown = "Your tenant's domains do not have a DMARC record with reject policy.`n`n%TestResult%"
    }

    $passResult = "✅ Pass"
    $failResult = "❌ Fail"
    $skipResult = "🗄️ Skip"
    $result = "| Domain | Result | Reason | Policy | Subdomain Policy |`n"
    $result += "| --- | --- | --- | --- | --- |`n"
    foreach ($item in $dmarcRecords) {
        switch($item.pass){
            "Passed" {$itemResult = $passResult}
            "Skipped" {$itemResult = $skipResult}
            "Failed" {$itemResult = $failResult}
        }

        $result += "| $($item.domain) | $($itemResult) | $($item.reason) | $($item.dmarcRecord.policy) | $($item.dmarcRecord.policySubdomain) |`n"
    }

    $testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $result

    Add-MtTestResultDetail -Result $testResultMarkdown

    return $testResult
}