How to get MFA Status from Microsoft Entra

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:

  1. Open Microsoft Entra Admin Center
  2. Expand Protection
  3. Open Authentication Methods
  4. Click on User Registration Details
Microsoft Entra MFA Status

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 True, False, Both. Includes only the licensed, unlicensed, or all users.
  • 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.

Export MFA Status from Microsoft Entra

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.

12 thoughts on “How to get MFA Status from Microsoft Entra”

  1. 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

  2. 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.

      Install-Module Microsoft.Graph.Beta -AllowClobber -Force
      
      • 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 😉

  3. 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?

  4. 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.

Leave a Comment

0 Shares
Tweet
Pin
Share
Share