Rules/MFA-002.no-admin-without-mfa.psd1

@{
    Id          = 'MFA-002'
    Title       = 'Every administrator is registered for MFA'
    Area        = 'MfaRegistration'
    Severity    = 'Critical'
    References  = @(
        'CIS Microsoft 365 Foundations Benchmark (MFA for administrative roles)'
        'https://learn.microsoft.com/entra/identity/authentication/howto-authentication-methods-activity'
    )
    Rationale   = 'An admin account without a registered MFA method cannot be protected by any MFA requirement - and is exactly the account an attacker registers their own method into.'
    Remediation = 'Register MFA (ideally phishing-resistant) for every account holding an admin role, starting with Global Administrators.'
    Test        = {
        param($Snapshot)
        $users = @($Snapshot.MfaRegistration.users)
        if ($users.Count -gt 0) {
            # Directory-sync service accounts are flagged as admins by the report
            # but never do interactive MFA - exclude them so this stays actionable.
            $syncUpns = Get-TLSyncAccountUpn -Snapshot $Snapshot
            $admins = @($users | Where-Object { $_.isAdmin -and -not $syncUpns.Contains([string]$_.userPrincipalName) })
            if ($admins.Count -eq 0) {
                return New-TLRuleResult -Status Manual -Evidence 'No interactive admin accounts in the registration report.'
            }
            $unregistered = @($admins | Where-Object { -not $_.isMfaRegistered })
            if ($unregistered.Count -eq 0) {
                return New-TLRuleResult -Status Pass -Evidence ("All {0} interactive admin accounts are MFA-registered (directory-sync accounts excluded)." -f $admins.Count)
            }
            return New-TLRuleResult -Status Fail -Evidence (@("{0} of {1} interactive admin accounts have no MFA registered." -f $unregistered.Count, $admins.Count) +
                @($unregistered | Select-Object -First 10 | ForEach-Object { $_.userPrincipalName }))
        }
        # Fallback for snapshots without a user list: trust the report summary.
        $summary = $Snapshot.MfaRegistration.summary
        if (-not $summary -or -not $summary.PSObject.Properties['adminsWithoutMfa']) {
            return New-TLRuleResult -Status Manual -Evidence 'Admin registration summary not present in the snapshot.'
        }
        if ([int]$summary.adminsWithoutMfa -eq 0) {
            New-TLRuleResult -Status Pass -Evidence ("All {0} admin accounts are MFA-registered." -f $summary.adminCount)
        }
        else {
            New-TLRuleResult -Status Fail -Evidence ("{0} of {1} admin accounts have no MFA registered." -f $summary.adminsWithoutMfa, $summary.adminCount)
        }
    }
}