When you enable the security defaults or enable per-user the MFA, then at some point you want to check if and what they have configured for the Multifactor Authentication. Which methods are they using, can they use passwordless sign-in, etc.
It’s now possible to view the configured MFA methods for your users in Microsoft Entra. We can also view which methods are most used and of course which users don’t have MFA configured yet.
In this article, we will look at the built-in reporting features in Microsoft Entra and I will show you how you can easily export the information with PowerShell to an Excel file.
Microsoft Entra MFA Report
Microsoft is moving from a per-user MFA configuration to conditional access policies. This way you will have more control over when users need to sign in with MFA and when not. But to configure conditional access policies you will need to have an Azure P1 or P2 license.
Note
To use the features in this article, you will need to have the Azure P1 or P2 license, without it, you won’t be able to view the MFA report or export the data with PowerShell.
Checkout this article instead
So before you enable conditional access policies it’s a good idea to check what MFA methods your users are currently using. You probably want to steer to password-less authentication, but to do this your users will need to have configured the correct methods.
To view the registered methods in Microsoft Entra, you will need to navigate to the Authentication Methods report:
- Open Microsoft Entra Admin Center
- Expand Protection
- Open Authentication Methods
- Click on User Registration Details
You can use the filter option at the top of the page to view the users without MFA or users that don’t have configured the self-service password reset (SSPR) for example.
The Activity page will give you some nice overviews of the methods that are most used, and some statistics of the percentage of users without MFA for example.
Get Entra MFA Status with PowerShell
I have created PowerShell scripts before to get the MFA status of your users with PowerShell. If you don’t have an Azure P1 or P2 license, then you can use this script to get the status.
The script in this article will get the MFA status from Microsoft Entra using the Microsoft Graph. The advantage of this report is that it will get the default method, show all registered methods, and if the user is MFA and Passwordless capable.
Note
To get the default method, you will need to use the beta module. Make sure that you have installed the beta modules as well with:Install-Module Microsoft.Graph.Beta -AllowClobber -Force
To get the exact same data as you see in the Microsoft Entra, you can simply use the cmdlet Get-MgReportAuthenticationMethodUserRegistrationDetail
. Good to know is that this cmdlet will only return accounts that are enabled.
You will need to have the Microsoft Graph PowerShell module installed and connect to Graph with the Reports.Read.All
scope:
# Connect to Graph Connect-MgGraph -Scopes "Reports.Read.All" # Get all report data # Note, we are using the beta module here, so we can get the default method as well Get-MgBetaReportAuthenticationMethodUserRegistrationDetail
Complete Script
I have created a script that gives you a bit more control over the users you want to include in your export. For example, I often want to check the licensed users only, or quickly verify the MFA methods of the admins.
You can use the following parameters to filter the results:
- AdminsOnly –
$true or $false
. Will export only users with an admin role - Licensed –
. Includes only the licensed, unlicensed, or all users.True, False, Both
- withOutMFAOnly –
$true or $false
. Export only the users without MFA - UserPrincipalName –
<string>
. Allows you to specify a userprincipal name to retrieve
The script will get all the report data first, and then filter out the requested users. It’s technically also possible to get only the report data from the selected users, but that turned out to be much slower than getting all the data first.
To get the users, we will make a separate request to Microsoft Graph to get the users with the applied filters. If you look at the Get-Users
function in the script, you will see a Select array. These are the properties that are included in the export. You can expand this list to your needs, for example, to get department information or the job title of the user.
The script will export the following data to a CSV file:
- Display name
- Email Address
- UserPrincipalName
- User type (member or guest)
- is Admin
- MFA Capable (when true, then the users has configured MFA)
- MFA Default method
- MFA Secondary method
- MFA registered methods
- Passwordless capable
- SSPR Registered
- SSPR Capable
- Manager of the user
You can get the latest version of the script here from my Github repository. To run the script, save the file to your work directory and run the script with the optional parameters in PowerShell. Read more about running PowerShell scripts in this article.
<# .Synopsis Get the MFA status for all users, admin or selected users from Microsoft Entra .DESCRIPTION This script will get the Azure MFA Status for your users. You can query all the users, admins only or a single user. It will return the MFA Status, MFA type, Default method, SSRP, and registered devices. .NOTES Name: Get-MgEntraMFAStatus Author: R. Mens - LazyAdmin.nl Version: 1.2 DateCreated: Feb 2024 Purpose/Change: Fixed licensed and enabled users .LINK https://lazyadmin.nl #> [CmdletBinding(DefaultParameterSetName="Default")] param( [Parameter( Mandatory = $false, ParameterSetName = "UserPrincipalName", HelpMessage = "Enter a single UserPrincipalName or a comma separted list of UserPrincipalNames", Position = 0 )] [string[]]$UserPrincipalName, [Parameter( Mandatory = $false, ValueFromPipeline = $false, ParameterSetName = "AdminsOnly" )] # Get only the users that are an admin [switch]$adminsOnly = $false, [Parameter( Mandatory = $false, ValueFromPipeline = $false, ParameterSetName = "allUsers" )] [Alias("Licensed")] [ValidateSet("true", "false", "both")] # Check only the MFA status of users that have license [string]$IsLicensed = 'true', [Parameter( Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = "allUsers" )] # Get only the users that don't have MFA enabled [switch]$withOutMFAOnly = $false, [Parameter( Mandatory = $false, HelpMessage = "Enter path to save the CSV file" )] [string]$CSVPath = ".\EntraMFAStatus-$((Get-Date -format "MMM-dd-yyyy").ToString()).csv" ) Function ConnectTo-MgGraph { # Check if MS Graph module is installed if (-not(Get-InstalledModule Microsoft.Graph)) { Write-Host "Microsoft Graph module not found" -ForegroundColor Black -BackgroundColor Yellow $install = Read-Host "Do you want to install the Microsoft Graph Module?" if ($install -match "[yY]") { Install-Module Microsoft.Graph -Repository PSGallery -Scope CurrentUser -AllowClobber -Force }else{ Write-Host "Microsoft Graph module is required." -ForegroundColor Black -BackgroundColor Yellow exit } } # Connect to Graph Write-Host "Connecting to Microsoft Graph" -ForegroundColor Cyan Connect-MgGraph -Scopes "Reports.Read.All" -NoWelcome } Function Get-Admins{ <# .SYNOPSIS Get all user with an Admin role #> process{ $admins = Get-MgDirectoryRole | Select-Object DisplayName, Id | %{$role = $_.DisplayName; Get-MgDirectoryRoleMember -DirectoryRoleId $_.id | where {$_.AdditionalProperties."@odata.type" -eq "#microsoft.graph.user"} | % {Get-MgUser -userid $_.id } } | Select @{Name="Role"; Expression = {$role}}, DisplayName, UserPrincipalName, Mail, Id | Sort-Object -Property Mail -Unique return $admins } } Function Get-Users { # Set the properties to retrieve $select = @( 'mail' 'userprincipalname' 'displayname' ) $properties = $select + "AssignedLicenses" # Get-MgBetaReportAuthenticationMethodUserRegistrationDetail returns only enabled accounts # so we get only enabled accounts here as well. $filter = "AccountEnabled eq true and UserType eq 'member'" # Check if UserPrincipalName(s) are given if ($UserPrincipalName) { Write-host "Get specific users" -ForegroundColor Cyan $users = @() foreach ($user in $UserPrincipalName) { try { $users += Get-MgUser -UserId $user -Property $properties | select $select -ErrorAction Stop } catch { [PSCustomObject]@{ DisplayName = " - Not found" UserPrincipalName = $User isAdmin = $null MFAEnabled = $null } } } }elseif($adminsOnly) { Write-host "Get admins only" -ForegroundColor Cyan $users = @() foreach ($admin in $admins) { $users += Get-MgUser -UserId $admin.UserPrincipalName -Property $properties | select $select } }else{ if ($IsLicensed -eq 'true') { # Get only licensed users $users = Get-MgUser -Filter $filter -Property $properties -all | Where-Object {($_.AssignedLicenses).count -gt 0} | select $select }elseif ($IsLicensed -eq 'false') { # Get only users without license $users = Get-MgUser -Filter $filter -Property $properties -all | Where-Object {($_.AssignedLicenses).count -eq 0} | select $select }else{ $users = Get-MgUser -Filter $filter -Property $properties -all | select $select } } return $users } Function Get-Manager { <# .SYNOPSIS Get the manager users #> param( [Parameter(Mandatory = $true)] $userId ) process { $manager = Get-MgUser -UserId $userId -ExpandProperty manager | Select @{Name = 'name'; Expression = {$_.Manager.AdditionalProperties.displayName}} return $manager.name } } Function Get-MFAStatusUsers { <# .SYNOPSIS Get all AD users #> process { Write-Host "Collecting users" -ForegroundColor Cyan # Collect users to get $users = Get-Users # Get all MFA Report data $reportData = Get-MgBetaReportAuthenticationMethodUserRegistrationDetail Write-Host "Processing" $users.count "users" -ForegroundColor Cyan # Collect and loop through all users foreach ($reportUser in $reportData) { # Check if we want this users in the output # Note - it's faster to get all report data and then filter it on the selected users, # then getting the report data for each individual user if ($reportUser.UserPrincipalName -notin $users.UserPrincipalName) { continue } Write-Host "- Processing $($reportUser.UserDisplayName)" -ForegroundColor Cyan # Get the user data from the users array $userData = $users | where-object {$_.UserPrincipalName -eq $reportUser.UserPrincipalName} # Get the manager of the user (optional) $manager = Get-Manager -userId $reportUser.id # Create output object [pscustomobject]@{ "Name" = $userData.DisplayName "Emailaddress" = $userData.mail "UserPrincipalName" = $reportUser.UserPrincipalName "User Type" = $reportUser.UserType "isAdmin" = $reportUser.IsAdmin "MFA Capable" = $reportUser.IsMfaCapable "MFA Default method" = $reportUser.DefaultMfaMethod "MFA Secondary method" = $reportUser.UserPreferredMethodForSecondaryAuthentication "MFA Methods Registered" = $reportUser.MethodsRegistered -join ", " "Passwordless Capable" = $reportUser.IsPasswordlessCapable "SSPR Registered" = $reportUser.IsSsprRegistered "SSPR Capable" = $reportUser.IsSsprCapable "Manager" = $manager } } } } # Connect to Graph ConnectTo-MgGraph # Get Admins # Get all users with admin role $admins = $null if ($adminsOnly) { $admins = Get-Admins } # Get MFA Status Get-MFAStatusUsers | Sort-Object Name | Export-CSV -Path $CSVPath -NoTypeInformation if ((Get-Item $CSVPath).Length -gt 0) { Write-Host "Report finished and saved in $CSVPath" -ForegroundColor Green # Open the CSV file Invoke-Item $CSVPath }else{ Write-Host "Failed to create report" -ForegroundColor Red }
Wrapping Up
Keeping track of the MFA configuration of your users is important to keep your Microsoft 3675 tenant secure. The new reports in Microsoft Entra allow you to easily track those users who don’t have completed the MFA setup process or are using outdated MFA methods.
Even though the report in Microsoft Entra provides the most needed information, with PowerShell we can include contact details, user’s manager, or department details which can be really helpful.
I hope you liked this article and script, if you have any questions, just drop a comment below.
Hi Rudy, I keep getting this error when running the script. What do I miss.
PS C:\Global.net> & ‘.\MFA report.ps1’ -IsLicensed $true -withOutMFAOnly
Connecting to Microsoft Graph
Collecting users
Get-MgUser : Der opstod en eller flere fejl.
At C:\Global.net\MFA report.ps1:145 char:7
+ $users = Get-MgUser -Filter $filter -Property $properties -all …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-MgUser_List], AggregateException
+ FullyQualifiedErrorId : System.AggregateException,Microsoft.Graph.PowerShell.Cmdlets.GetMgUser_List
Do you have an Azure p1 or p2 license and the Microsoft Graph Beta module installed?
Hmm. It took some Entra time, but now it works without me doing anything.
So i guess it must have been the access rights to graph.
I used your script to get an overview of the MFA status and MFA method of our users.
1) How do I get a list of the MFA methods for all user accounts in Entra ID (so enabled, disabled, licensed and unlicensed)?
The Enabled parameter is already set on ‘both’ as default so I think this is correct in my use case. The output includes only enabled users with and without MFA.
2) Another thing is that the default method is empty for all users but the MFA Secondary method is filled with the default MFA sign method of the users. Am I missing something?
Set enabled indeed to both and IsLicensed to false (which will get both licensed and unlicensed users)
The default method is still part of the beta module only, I have fixed the script for that. Just make sure that you have installed the Graph beta module as well.
For 2) Confirmed that with the graph beta module the default MFA method is exported. Thank you.
For 1) I only get the enabled users with -enabled both -Islicensed $false parameters. Verified by adding the AccountEnabled status in the export.
Even with using -enabled disabled parameter I only get enabled users. Isthis parameter working correctly?
3) What is the difference between the script on this page compared to your other graph scrip on https://lazyadmin.nl/powershell/msgraph-mfa-status/ ?
It seems that the Get-MgBetaReportAuthenticationMethodUserRegistrationDetail cmdlet doesn’t return disabled user accounts. Also if you check the Authentication methods in Entra you will notice that only enabled accounts are displayed.
I have updated the script (and article), so it will only get enabled accounts now. The islicensed parameter is fixed as well.
The other script will also get the same information, but is a bit slower, because it has to do a call to the Graph API to get the default MFA method for every user. But it does include disabled users 😉
Thank you for checking this out and changing the script. I understand the difference between the scripts but was confused by the almost similar name.
Confirmed that the other script can give an overview of enabled and disabled users. https://lazyadmin.nl/powershell/msgraph-mfa-status/
Thanks for the good article
When I run
Connect-MgGraph -Scopes “Reports.Read.All”
and
Get-MgBetaReportAuthenticationMethodUserRegistrationDetail
I get an error somehow with:
Get-MgReportAuthenticationMethodUserRegistrationDetail : Calling principal does not have required MSGraph permissions
AuditLog.Read.All
Do you know how this can be?
How have you authenticated the connect-mggraph? Seems like an API permissions error in the Azure Application.
Hi Ruud,
Thank you for the nice script.
I noticed a small issue when exporting the results.
Get-MgBetaReportAuthenticationMethodUserRegistrationDetail is used in #190.
When I change to Get-MgReportAuthenticationMethodUserRegistrationDetail the script is working without the need of Graph Beta powershell modules.
The beta cmdlet is indeed not needed. Have removed it.