checks/entra/mfa-registration-coverage.ps1

# MFA registration coverage (% of users registered). Reporting needs Entra ID P1.
if (-not (Test-EntraLicence @('AAD_PREMIUM','AAD_PREMIUM_P2'))) { return Skip-Check 'MFA registration coverage' 'The authentication-methods registration report requires Entra ID P1 (not present).' }
$reg = Get-GraphAll 'https://graph.microsoft.com/v1.0/reports/authenticationMethods/userRegistrationDetails?$select=userPrincipalName,isMfaRegistered,userType'
$members = @($reg | Where-Object { $_.userType -eq 'member' })
$notReg = @($members | Where-Object { -not $_.isMfaRegistered })
$items = @()
if ($members.Count -gt 0 -and $notReg.Count -gt 0) {
    $pct = [math]::Round(100.0 * $notReg.Count / $members.Count)
    foreach ($u in ($notReg | Select-Object -First 200)) {
        $items += [pscustomobject]@{ Title = $u.userPrincipalName; Detail = 'Not registered for MFA' }
    }
    # lead line via the check Name reflects the coverage gap
    $script:covLine = "$($notReg.Count) of $($members.Count) members ($pct%) are not registered for MFA"
}
$regCount = $members.Count - $notReg.Count
$regPct = if ($members.Count) { [math]::Round(100.0 * $regCount / $members.Count) } else { 0 }
[pscustomobject]@{
    Name = if ($script:covLine) { "MFA registration gap — $script:covLine" } else { 'MFA registration coverage' }
    Severity = 'high'
    Evidence = "$regCount of $($members.Count) members ($regPct%) are registered for MFA"
    Fix = 'Drive MFA registration (registration campaign / combined security info) so enforcement policies actually protect every account.'
    Items = $items
}