Microsoft 365 MFA CHECK
A quick PowerShell Script to interrogate MS365/EntraID and show all sign-in enabled entities, and whether they have MFA setup.
The below script, will run, install any required modules, and then ask you to authenticate into Microsoft Office 365 (using the normal 365 authentication GUI), after which, assuming you have administrative access to MS365, it will identify all active sign-in enabled entities, and provide information as to whether they are MFA protected, or just have an authentication method for Self-Service-Password-Reset (SSPR).
NOTE: If you have SSPR setup and only email addresses listed, this will be taken as SSPR use and not MFA, and therefore, you will see that MFA is not enabled.
Your data will be shown within the PowerShell window, but also in an exploded window with some filtering options, such as the below.

Download the code below, save it as ms365-mfa-check.ps1, and then run it (subject to your security settings) via .\ms365-mfa-check.ps1 within that location when running an administrative PowerShell session.
As always, we recommend checking the scripts yourselves and ensuring you are happy with these before use. Cyber Tec Security accepts no responsibility and provides no warranty for the code included within the scripts provided below:
<#
=========================================================================================================
MFA Configured Report (Absolute registration evidence)
- Interactive GUI login (standard Microsoft sign-in window)
- Uses Microsoft Graph "userRegistrationDetails" report
- Shows:
* MFAConfigured (IsMfaRegistered)
* MFAMethodsRegistered (methods that can satisfy MFA)
* SSPRMethodsRegistered (recovery/SSPR methods like email)
* AllMethodsRegistered (raw combined list)
- Includes Members + Guests (as returned by the report)
- Notes:
* This is a REPORTING dataset (can lag). For live truth for one user, use the helper at bottom.
T * Script is provided "as is" and whilst tested, Cyber Tec Security provides no warranty for the code
=========================================================================================================
#>
Clear-Host
$ErrorActionPreference = "Stop"
Write-Host "Starting MFA Configured Report..." -ForegroundColor Cyan
Write-Host ""
# --- Ensure NuGet provider ---
if (-not (Get-PackageProvider -Name NuGet -ErrorAction SilentlyContinue)) {
Write-Host "Installing NuGet provider..." -ForegroundColor Yellow
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Scope CurrentUser -Force | Out-Null
}
# --- Ensure Microsoft Graph PowerShell is installed (only if missing) ---
$needInstall = -not (Get-Module -ListAvailable -Name Microsoft.Graph.Authentication)
if ($needInstall) {
Write-Host "Microsoft Graph PowerShell not found. Installing Microsoft.Graph..." -ForegroundColor Yellow
Install-Module Microsoft.Graph -Scope CurrentUser -AllowClobber -Force
}
# --- Import only what we need ---
Import-Module Microsoft.Graph.Authentication -ErrorAction Stop
Import-Module Microsoft.Graph.Reports -ErrorAction Stop
# --- Connect (GUI login) ---
Write-Host "Connecting to Microsoft Graph (GUI sign-in will appear)..." -ForegroundColor Cyan
Connect-MgGraph -Scopes "AuditLog.Read.All","User.Read.All" | Out-Null
# Use beta profile for best coverage of registration details in some tenants
try {
Select-MgProfile -Name "beta"
} catch {
# If profile switching isn't available, continue anyway
}
Write-Host ""
Write-Host "Pulling user registration details (this can take a little while in large tenants)..." -ForegroundColor Cyan
# --- Pull registration report ---
$details = Get-MgReportAuthenticationMethodUserRegistrationDetail -All
# --- Helpers ---
function Convert-ToStringArray {
param(
[Parameter(Mandatory=$false)]
$Value
)
if ($null -eq $Value) { return @() }
# Sometimes it arrives as an array, sometimes as a single string, occasionally as something else
if ($Value -is [System.Array]) { return @($Value) }
if ($Value -is [string]) {
$s = $Value.Trim()
if ([string]::IsNullOrWhiteSpace($s)) { return @() }
# If it looks comma-separated, split it; otherwise treat as single token
if ($s -like "*,*") {
return @($s.Split(",") | ForEach-Object { $_.Trim() } | Where-Object { $_ })
} else {
return @($s)
}
}
# Fallback: stringify and attempt split
$asString = ($Value | Out-String).Trim()
if ([string]::IsNullOrWhiteSpace($asString)) { return @() }
if ($asString -like "*,*") {
return @($asString.Split(",") | ForEach-Object { $_.Trim() } | Where-Object { $_ })
}
return @($asString)
}
# SSPR/recovery-ish methods we want to separate (these do NOT mean MFA configured)
# Note: the report commonly uses "email" (SSPR). Keep the list conservative.
$ssprLike = @(
"email",
"alternateEmail",
"alternateMobilePhone"
)
# --- Build report ---
$report = $details | ForEach-Object {
$methods = Convert-ToStringArray -Value $_.MethodsRegistered
$ssprMethods = @($methods | Where-Object { $_ -in $ssprLike })
$mfaMethods = @($methods | Where-Object { $_ -notin $ssprLike })
[pscustomobject]@{
DisplayName = $_.UserDisplayName
UserPrincipalName = $_.UserPrincipalName
UserType = $_.UserType
# Graph report flag: user has registered MFA methods (best high-level indicator)
MFAConfigured = [bool]$_.IsMfaRegistered
# Split for clarity
MFAMethodsRegistered = ($mfaMethods -join ", ")
SSPRMethodsRegistered = ($ssprMethods -join ", ")
# Raw combined list (useful for troubleshooting)
AllMethodsRegistered = ($methods -join ", ")
# Extra context
IsMfaCapable = [bool]$_.IsMfaCapable
IsSsprRegistered = [bool]$_.IsSsprRegistered
IsPasswordlessCapable = [bool]$_.IsPasswordlessCapable
LastUpdated = $_.LastUpdatedDateTime
}
} | Sort-Object UserType, DisplayName
Write-Host ""
Write-Host "===== MFA CONFIGURED REPORT (Registration Evidence) =====" -ForegroundColor Green
# Console view
$report |
Select-Object DisplayName,
UserPrincipalName,
UserType,
MFAConfigured,
MFAMethodsRegistered,
SSPRMethodsRegistered,
AllMethodsRegistered,
IsMfaCapable,
IsSsprRegistered,
LastUpdated |
Format-Table -AutoSize
# Optional: Grid view
try {
$report |
Select-Object DisplayName,
UserPrincipalName,
UserType,
MFAConfigured,
MFAMethodsRegistered,
SSPRMethodsRegistered,
AllMethodsRegistered,
IsMfaCapable,
IsSsprRegistered,
LastUpdated |
Out-GridView -Title "MFA Configured Report (Registered Methods)"
} catch {}
# Optional: Export
# $report | Export-Csv ".\MFA_Configured_Report.csv" -NoTypeInformation -Encoding UTF8
Write-Host ""
Write-Host "Done." -ForegroundColor Cyan
# --- Optional: Live (non-report) check for ONE user (uncomment to use) ---
# This is definitive and updates immediately, but is slower at scale.
# Requires extra scope: UserAuthenticationMethod.Read.All
#
# Disconnect-MgGraph
# Connect-MgGraph -Scopes "User.Read.All","UserAuthenticationMethod.Read.All" | Out-Null
# $userUpn = "user@domain.com"
# Get-MgUserAuthenticationMethod -UserId $userUpn -All |
# Select-Object @{n="MethodType";e={$_.AdditionalProperties.'@odata.type'}}, Id |
# Format-Table -AutoSize