Skip to content

Instantly share code, notes, and snippets.

@tijldeneut
Created July 8, 2026 08:17
Show Gist options
  • Select an option

  • Save tijldeneut/2dea8476ddfe96a77e9f251536976b41 to your computer and use it in GitHub Desktop.

Select an option

Save tijldeneut/2dea8476ddfe96a77e9f251536976b41 to your computer and use it in GitHub Desktop.
Script to find users on a EntraID Tenant without MFA and without activity, output is a CSV (delimited with ";")
<#
.SYNOPSIS
Get-MFAReport.ps1
.DESCRIPTION
Export Microsoft 365 per-user MFA report with Micrososoft Graph PowerShell.
Also list recent activity to find stale accounts which have no MFA enabled due to not having logged in recently
.LINK
www.alitajran.com/export-office-365-users-mfa-status-with-powershell/
.NOTES
Written by: ALI TAJRAN
Website: www.alitajran.com
LinkedIn: linkedin.com/in/alitajran
V3 by: Photubias
.CHANGELOG
V1.00, 04/04/2021 - Initial version
V2.00, 08/04/2024 - Rewritten for Microsoft Graph PowerShell
V3.00, 07/07/2026 - Added SignIn logs to detect stale users
.USAGE (PowerShell or PowerShell Core or PWSH on Linux)
Install-Module Microsoft.Graph
Connect-MgGraph -Scopes "User.Read.all","UserAuthenticationMethod.Read.All","UserAuthenticationMethod.ReadWrite.All","AuditLog.Read.all" -UseDeviceAuthentication
.\Get-MFAReport.ps1
#>
if (-Not (Get-MgOrganization -ErrorAction SilentlyContinue)) {
# Connect to Microsoft Graph with the required scopes
Write-Host "[i] Connecting first" -ForegroundColor Cyan
Connect-MgGraph -Scopes "User.Read.All", "Policy.ReadWrite.AuthenticationMethod", "UserAuthenticationMethod.Read.All" -NoWelcome
}
$Tenant = Get-MgOrganization
Write-Host "[+] Connected to Tenant '$($Tenant.DisplayName)' ($($Tenant.Id))" -ForegroundColor Green
# CSV export file path
$CSVfile = "MFAUsers.csv"
# Get properties
$Properties = @(
'Id',
'DisplayName',
'UserPrincipalName',
'UserType',
'Mail',
'ProxyAddresses',
'AccountEnabled',
'CreatedDateTime'
)
Write-Host "[!] ##### Warning: this script will take a long time (30min+ for 500 users) to generate '$CSVfile'" -ForegroundColor Cyan
Read-Host -Prompt " Press any key to continue or Ctrl+C to quit" | Out-Null
# Get all users
[array]$Users = Get-MgUser -All -Property $Properties | Select-Object $Properties
# Initialize the report list
$Report = [System.Collections.Generic.List[Object]]::new()
# Check if any users were retrieved
if (-not $Users) {
Write-Host "No users found. Exiting script." -ForegroundColor Red
return
}
# Initialize progress counter
$counter = 0
$totalUsers = $Users.Count
# Loop through each user and get their MFA settings
foreach ($User in $Users) {
$counter++
$upn = $User.UserPrincipalName
# Calculate percentage completion
$percentComplete = [math]::Round(($counter / $totalUsers) * 100)
# Define progress bar parameters with user principal name
$progressParams = @{
Activity = "Processing Users"
Status = "User $($counter) of $totalUsers - $($User.UserPrincipalName) - $percentComplete% Complete"
PercentComplete = $percentComplete
}
Write-Progress @progressParams
# Get MFA settings
$MFAStateUri = "https://graph.microsoft.com/beta/users/$($User.Id)/authentication/requirements"
$Data = Invoke-MgGraphRequest -Uri $MFAStateUri -Method GET
# Get the default MFA method
$DefaultMFAUri = "https://graph.microsoft.com/beta/users/$($User.Id)/authentication/signInPreferences"
$DefaultMFAMethod = Invoke-MgGraphRequest -Uri $DefaultMFAUri -Method GET
# Determine the MFA default method
if ($DefaultMFAMethod.userPreferredMethodForSecondaryAuthentication) {
$MFAMethod = $DefaultMFAMethod.userPreferredMethodForSecondaryAuthentication
Switch ($MFAMethod) {
"push" { $MFAMethod = "Microsoft authenticator app" }
"oath" { $MFAMethod = "Authenticator app or hardware token" }
"voiceMobile" { $MFAMethod = "Mobile phone" }
"voiceAlternateMobile" { $MFAMethod = "Alternate mobile phone" }
"voiceOffice" { $MFAMethod = "Office phone" }
"sms" { $MFAMethod = "SMS" }
Default { $MFAMethod = "Unknown method" }
}
}
else {
$MFAMethod = "Not Enabled"
}
# Filter only the aliases
$Aliases = ($User.ProxyAddresses | Where-Object { $_ -clike "smtp*" } | ForEach-Object { $_ -replace "smtp:", "" }) -join ', '
# Get most recent Sign In activity, can be empty if Guest/Member user
$SignInProperties = @('AppDisplayName','IPAddress','ResourceDisplayName','CreatedDateTime')
$UserSignIn = Get-MGAuditLogSignIn -Filter "userPrincipalName eq '$upn'" -Top 1 -Property $SignInProperties | Select-Object $SignInProperties
if ( $UserSignIn ) { $LastSignInApp = $UserSignIn.AppDisplayName + " ($($UserSignIn.IPAddress))" }
else { $LastSignInApp = "" }
# If Sign In is empty (e.g. Guest or Member User or due to very recent actions), there should be an audit trail
$AuditProperties = @('ActivityDisplayName','Result','Category','ActivityDateTime')
$UserAudit = Get-MgAuditLogDirectoryAudit -Filter "targetResources/any(t:t/userPrincipalName eq '$upn')" -Top 1 -Property $AuditProperties | Select-Object $AuditProperties
# Calculate "Please Disable Or Not" Risk Column (MFA is "Not Enabled", User Enabled and no recent SignIns or Audit Logs)
if ( $User.AccountEnabled -And $MFAMethod -eq "Not Enabled" -And -Not $UserSignIn -And -Not $UserAudit ) {
$UserRisk = "HIGHRISK"
} else {$UserRisk = "OK"}
# Create a report line for each user
$ReportLine = [PSCustomObject][ordered]@{
UserPrincipalName = $User.UserPrincipalName
DisplayName = $User.DisplayName
MFAState = $Data.PerUserMfaState
MFADefaultMethod = $MFAMethod
PrimarySMTP = $User.Mail
Aliases = $Aliases
UserType = $User.UserType
AccountEnabled = $User.AccountEnabled
CreatedDateTime = $User.CreatedDateTime
LastSignIn = $UserSignIn.CreatedDateTime
LastSignInApp = $LastSignInApp
LastAudit = $UserAudit.ActivityDateTime
LastAuditAction = $LastAudit.ActivityDisplayName
UserAtRisk = $UserRisk
}
$Report.Add($ReportLine)
## Uncomment this to stop after x users
#if ($counter -eq 10){break}
}
# Complete the progress bar
Write-Progress -Activity "Processing Users" -Completed
# Display the report in a grid view, only on Windows
if (-Not ($PSVersionTable.Platform -And $PSVersionTable -Ne "Linux")){
$Report | Out-GridView -Title "Microsoft 365 per-user MFA Report"
}
# Export the report to a CSV file
$Report | Export-Csv -Delimiter ';' -Path $CSVfile -NoTypeInformation -Encoding utf8
Write-Host "Microsoft 365 per-user MFA Report is in $CSVfile" -ForegroundColor Cyan
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment