Collectors/EmailAuthDns.ps1

@{
    Name           = 'EmailAuthDns'
    FileName       = 'emailAuthDns'
    ApiVersion     = 'v1.0'
    RequiredScopes = @('Directory.Read.All')
    Description    = 'SPF, DKIM and DMARC DNS records for every verified domain (pure DNS; Graph is only used for the domain list).'
    Collect        = {
        $domains = @(Invoke-TLGraphRequest -Uri '/v1.0/domains' -All | Where-Object { $_.isVerified })

        $results = foreach ($domain in $domains) {
            $name = [string]$domain.id
            Write-Verbose ("Checking e-mail authentication DNS for {0}..." -f $name)

            # SPF
            $txtRecords = @(Resolve-TLDnsRecord -Name $name -Type TXT)
            $spfRecord = @($txtRecords | Where-Object { $_ -match '^v=spf1' }) | Select-Object -First 1
            $spfQualifier = $null
            if ($spfRecord) {
                if ($spfRecord -match '-all\s*$') { $spfQualifier = 'hardfail' }
                elseif ($spfRecord -match '~all\s*$') { $spfQualifier = 'softfail' }
                elseif ($spfRecord -match '\?all\s*$') { $spfQualifier = 'neutral' }
                elseif ($spfRecord -match '\+?all\s*$') { $spfQualifier = 'pass-all' }
            }

            # DKIM - Microsoft 365 publishes CNAMEs for selector1/selector2
            $selector1 = @(Resolve-TLDnsRecord -Name ("selector1._domainkey.{0}" -f $name) -Type CNAME)
            $selector2 = @(Resolve-TLDnsRecord -Name ("selector2._domainkey.{0}" -f $name) -Type CNAME)

            # DMARC
            $dmarcRecords = @(Resolve-TLDnsRecord -Name ("_dmarc.{0}" -f $name) -Type TXT)
            $dmarcRecord = @($dmarcRecords | Where-Object { $_ -match '^v=DMARC1' }) | Select-Object -First 1
            $dmarcPolicy = $null
            if ($dmarcRecord -and $dmarcRecord -match 'p\s*=\s*(none|quarantine|reject)') {
                $dmarcPolicy = $Matches[1].ToLowerInvariant()
            }

            [ordered]@{
                domain        = $name
                isOnMicrosoft = [bool]($name -like '*.onmicrosoft.com')
                isDefault     = [bool]$domain.isDefault
                # Technical domains (SBC trunks, third-party relays) carry no
                # 'Email' service - rules only enforce mail domains.
                isMailDomain  = [bool](@($domain.supportedServices) -contains 'Email')
                spf           = [ordered]@{
                    present   = [bool]$spfRecord
                    record    = $spfRecord
                    qualifier = $spfQualifier
                }
                dkim          = [ordered]@{
                    selector1 = [bool](@($selector1).Count -gt 0)
                    selector2 = [bool](@($selector2).Count -gt 0)
                    targets   = @(@($selector1) + @($selector2))
                }
                dmarc         = [ordered]@{
                    present = [bool]$dmarcRecord
                    record  = $dmarcRecord
                    policy  = $dmarcPolicy
                }
            }
        }

        , @($results)
    }
}