{"id":9038,"date":"2025-07-08T03:09:29","date_gmt":"2025-07-08T03:09:29","guid":{"rendered":"https:\/\/pariswells.com\/blog\/?p=9038"},"modified":"2025-07-08T03:09:30","modified_gmt":"2025-07-08T03:09:30","slug":"users-with-weak-or-no-mfa","status":"publish","type":"post","link":"https:\/\/pariswells.com\/blog\/research\/users-with-weak-or-no-mfa","title":{"rendered":"Users with Weak or No MFA"},"content":{"rendered":"\n<pre class=\"wp-block-code\"><code class=\"\">#Requires -Modules Microsoft.Graph.Authentication, Microsoft.Graph.Users\n#Requires -Version 7.0\n\n&lt;#\n.SYNOPSIS\n    Checks Microsoft 365 users for weak or no MFA configurations and splits results into two lists.\n.DESCRIPTION\n    This script connects to Microsoft Graph and identifies users with no MFA or weak MFA methods (SMS or voice).\n    It generates separate reports for users with no MFA and users with weak MFA.\n.NOTES\n    Requires Microsoft Graph PowerShell SDK and appropriate permissions (UserAuthenticationMethod.Read.All, User.Read.All).\n    Must be run in PowerShell 7.0 or later.\n    Run with Global Admin or equivalent credentials.\n#>\n\n# Verify PowerShell version\nif ($PSVersionTable.PSVersion.Major -lt 7) {\n    Write-Error \"This script requires PowerShell 7.0 or later. Current version: $($PSVersionTable.PSVersion). Please run in PowerShell 7.\"\n    exit 1\n}\n\n# Connect to Microsoft Graph with required scopes\nConnect-MgGraph -Scopes \"UserAuthenticationMethod.Read.All\", \"User.Read.All\" -NoWelcome\n\n# Initialize report array\n$report = @()\n\n# Get all users\n$users = Get-MgUser -All -Select \"id,displayName,userPrincipalName,accountEnabled\"\n\nforeach ($user in $users) {\n    if (-not $user.AccountEnabled) { continue } # Skip disabled accounts\n\n    # Get authentication methods for the user\n    $authMethods = Get-MgUserAuthenticationMethod -UserId $user.Id\n\n    # Initialize MFA status\n    $mfaStatus = \"No MFA\"\n    $isWeakMFA = $false\n    $details = @()\n\n    foreach ($method in $authMethods) {\n        switch ($method.AdditionalProperties[\"@odata.type\"]) {\n            \"#microsoft.graph.microsoftAuthenticatorAuthenticationMethod\" {\n                $mfaStatus = \"Strong MFA\"\n                $details += \"Microsoft Authenticator\"\n            }\n            \"#microsoft.graph.fido2AuthenticationMethod\" {\n                $mfaStatus = \"Strong MFA\"\n                $details += \"FIDO2 Security Key\"\n            }\n            \"#microsoft.graph.windowsHelloForBusinessAuthenticationMethod\" {\n                $mfaStatus = \"Strong MFA\"\n                $details += \"Windows Hello\"\n            }\n            \"#microsoft.graph.phoneAuthenticationMethod\" {\n                $mfaStatus = \"Weak MFA\"\n                $isWeakMFA = $true\n                $phoneType = $method.AdditionalProperties[\"phoneType\"]\n                $details += \"Phone ($phoneType)\"\n            }\n            \"#microsoft.graph.passwordAuthenticationMethod\" {\n                # Password alone doesn't count as MFA\n                if ($mfaStatus -eq \"No MFA\") { $details += \"Password Only\" }\n            }\n        }\n    }\n\n    # Create report entry\n    $reportEntry = [PSCustomObject]@{\n        DisplayName       = $user.DisplayName\n        UserPrincipalName = $user.UserPrincipalName\n        MFAStatus         = $mfaStatus\n        IsWeakMFA         = $isWeakMFA\n        AuthMethods       = ($details -join \", \")\n    }\n\n    $report += $reportEntry\n}\n\n# Disconnect from Microsoft Graph\nDisconnect-MgGraph -ErrorAction SilentlyContinue\n\n# Split into two lists\n$noMFAUsers = $report | Where-Object { $_.MFAStatus -eq \"No MFA\" }\n$weakMFAUsers = $report | Where-Object { $_.IsWeakMFA }\n\n# Output No MFA users to console\nWrite-Host \"`nUsers with No MFA:\" -ForegroundColor Red\nif ($noMFAUsers.Count -eq 0) {\n    Write-Host \"No users found with no MFA.\" -ForegroundColor Green\n} else {\n    $noMFAUsers | Format-Table DisplayName, UserPrincipalName, MFAStatus, AuthMethods -AutoSize\n}\n\n# Output Weak MFA users to console\nWrite-Host \"`nUsers with Weak MFA:\" -ForegroundColor Yellow\nif ($weakMFAUsers.Count -eq 0) {\n    Write-Host \"No users found with weak MFA.\" -ForegroundColor Green\n} else {\n    $weakMFAUsers | Format-Table DisplayName, UserPrincipalName, MFAStatus, AuthMethods -AutoSize\n}\n\n# Export to CSV\n$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'\n$noMFACsvPath = \"NoMFAReport_$timestamp.csv\"\n$weakMFACsvPath = \"WeakMFAReport_$timestamp.csv\"\n\nif ($noMFAUsers.Count -gt 0) {\n    $noMFAUsers | Export-Csv -Path $noMFACsvPath -NoTypeInformation\n    Write-Host \"No MFA report exported to $noMFACsvPath\" -ForegroundColor Green\n} else {\n    Write-Host \"No MFA CSV not created (no users found).\" -ForegroundColor Yellow\n}\n\nif ($weakMFAUsers.Count -gt 0) {\n    $weakMFAUsers | Export-Csv -Path $weakMFACsvPath -NoTypeInformation\n    Write-Host \"Weak MFA report exported to $weakMFACsvPath\" -ForegroundColor Green\n} else {\n    Write-Host \"Weak MFA CSV not created (no users found).\" -ForegroundColor Yellow\n}\n\n# Summary\n$totalUsers = $users.Count\n$noMFACount = $noMFAUsers.Count\n$weakMFACount = $weakMFAUsers.Count\n\nWrite-Host \"`nSummary:\" -ForegroundColor Cyan\nWrite-Host \"Total Users: $totalUsers\"\nWrite-Host \"Users with No MFA: $noMFACount\"\nWrite-Host \"Users with Weak MFA: $weakMFACount\"<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-9038","post","type-post","status-publish","format-standard","hentry","category-research"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/posts\/9038","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/comments?post=9038"}],"version-history":[{"count":1,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/posts\/9038\/revisions"}],"predecessor-version":[{"id":9039,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/posts\/9038\/revisions\/9039"}],"wp:attachment":[{"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/media?parent=9038"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/categories?post=9038"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/tags?post=9038"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}