Blog Post

ITOps Talk Blog
2 MIN READ

PowerShell Basics: How to check if MFA is enabled in Azure and Office 365

AnthonyBartolo's avatar
Jun 25, 2020

Security governance has been top of mind for most since the onslaught of human malware has the masses working from home.  This comes with new concerns surrounding identity protection and actually proving that remote users are who they say they are in order to be allowed access to organizational data.  Multi-factor Authentication (MFA) is a great tool to ensure this however the task of knowing which user has it enabled can be tedious. 

 

Enter PowerShell to the rescue to automate reporting of this process.

 

The following script will report on your organizations MFA status per user and report on which users are admins.  The latter being even more crucial that MFA is enabled.

 

Function Get-AzureMFAStatus {

    [CmdletBinding()]
    param(
        [Parameter(
            Position=0,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true
            )]

        [string[]]   $UserPrincipalName,         
        [int]        $MaxResults = 4000,
        [bool]       $isLicensed = $true,
        [switch]     $SkipAdminCheck
    )
 
    BEGIN {
        if ($SkipAdminCheck.IsPresent) {
            $AdminUsers = Get-MsolRole -ErrorAction Stop | foreach {Get-MsolRoleMember -RoleObjectId $_.ObjectID} | Where-Object {$_.EmailAddress -ne $null} | Select EmailAddress -Unique | Sort-Object EmailAddress
        }
    }
 
    PROCESS {
        if ($UserPrincipalName) {
            foreach ($User in $UserPrincipalName) {
                try {
                    Get-MsolUser -UserPrincipalName $User -ErrorAction Stop | select DisplayName, UserPrincipalName, `
                        @{Name = 'isAdmin'; Expression = {if ($SkipAdminCheck) {Write-Output "-"} else {if ($AdminUsers -match $_.UserPrincipalName) {Write-Output $true} else {Write-Output $false}}}}, `
                        @{Name = 'MFAEnabled'; Expression={if ($_.StrongAuthenticationMethods) {Write-Output $true} else {Write-Output $false}}}
                              
                } catch {
                    $Object = [pscustomobject]@{
                        DisplayName       = '_NotSynced'
                        UserPrincipalName = $User
                        isAdmin           = '-'
                        MFAEnabled        = '-' 
                    }
                    Write-Output $Object
                }
            }
        } else {
            $AllUsers = Get-MsolUser -MaxResults $MaxResults | Where-Object {$_.IsLicensed -eq $isLicensed} | select DisplayName, UserPrincipalName, `
                @{Name = 'isAdmin'; Expression = {if ($SkipAdminCheck) {Write-Output "-"} else {if ($AdminUsers -match $_.UserPrincipalName) {Write-Output $true} else {Write-Output $false}}}}, `
                @{Name = 'MFAEnabled'; Expression={if ($_.StrongAuthenticationMethods) {Write-Output $true} else {Write-Output $false}}}
 
            Write-Output $AllUsers | Sort-Object isAdmin, MFAEnabled -Descending
        }
    }
    END {}
}

As always, please share your comments below on bettering the above script or any questions you may have.

 

Updated Apr 27, 2021
Version 3.0
  • Anders Rask's avatar
    Anders Rask
    Copper Contributor

    Very useful script!

     

    A small thing you might want to fix is regarding checking if [switch] parameters are used: the "correct" way is 

    if ($SkipAdminCheck.IsPresent) { .. }
  • D0mp13's avatar
    D0mp13
    Copper Contributor

    I stumbled upon this article and this script is wrong in the core of it.


    There is a difference between having StrongAuthenticationMethods configured and enabling MFA. You can register an alternate email or phonenumber without MFA being enforced or enabled.


    To know if a user is MFA enabled or not is stored in StrongAuthenticationRequirements their state.


    f.e.:

    $Req = (Get-MsolUser -UserPrincipalName $UserObject.UserPrincipalName).StrongAuthenticationRequirements
    if (($Req.State -eq "Enabled") -or ($Req.State -eq "Enforced")){
          Write-Host "MFA Enabled or Enforced"
    }

    I even suspect that this bug is present in the Extension for Azure MFA v1.0.1.35 (8/17/2020) because we see the same behavior when we use the NPS extension. It sends an MFA challenge even if MFA isn't enabled/enforced.
    (https://www.microsoft.com/en-us/download/details.aspx?id=54688)

  • SukantaRay's avatar
    SukantaRay
    Copper Contributor

    Hi D0mp13 

    If MFA, Enabled via Conditional Access, then the state can be shown as Disabled too. 

    Seems to me better to check the StrongAuthenticationMethods set or not and get the default one to know the MFA method

    Get-MsolUser -UserPrincipalName <<upn@domain.com>> | select DisplayName,UserPrincipalName,@{Name="MFA Status"; Expression={ if( $_.StrongAuthenticationMethods.IsDefault -eq $true) {($_.StrongAuthenticationMethods | Where IsDefault -eq $True).MethodType} else { "Disabled"}}}

     

  • Antonio Palumbo's avatar
    Antonio Palumbo
    Copper Contributor

    This way is easier:

    How to get a list of users without MFA enabled:
    $UserCredential = Get-Credential Connect-MsolService -Credential $UserCredential
    Get-MsolUser -All | where {$_.StrongAuthenticationMethods.Count -eq 0} | Select-Object -Property UserPrincipalName | Sort-Object userprincipalname

  • Dusty's avatar
    Dusty
    Copper Contributor

    With the MSOnline Powershell module now deprecated, how can this be done with the Azure AD module?

  • TechJollof's avatar
    TechJollof
    Copper Contributor

    Hi Guys, good day.
    AnthonyBartolo 
    You try the following script below. It takes into consideration the actual status of the user from the legacy portal.

    $AllUsers = Get-MsolUser -All:$True | Sort-Object DisplayName #-ErrorAction SilentlyContinue
     
    # Check if a UserPrincipalName is given
    # Get the MFA status for the given user(s) if they exist
     
     
    $AllMFAData =@()
    $Methods = @{
      "OneWaySMS"="SMS token"
      "TwoWayVoiceMobile"="Phone call verification" 
      "PhoneAppOTP" = "Hardware token or authenticator app" 
      "PhoneAppNotification" = "Authenticator app" 
    }
     
    foreach ($MsolUser in $AllUsers) {
     
      try {
          $MFAResults =  [PSCustomObject]@{
              DisplayName       = $MsolUser.DisplayName
              UserPrincipalName = $MsolUser.UserPrincipalName
              DefualtMFAMethod  = ($MsolUser.StrongAuthenticationMethods | ? {$_.isDefault -eq $true}).MethodType
              MFAEnforced       = if($MsolUser.StrongAuthenticationRequirements) {$MsolUser.StrongAuthenticationRequirements.State } else {"Diabled"}
              MFAMethods        = if($MsolUser.StrongAuthenticationMethods){($MsolUser.StrongAuthenticationMethods.MethodType | % { $Methods[$_]}) -join ","}else{"No Methods"}
            }
       }
       catch {
        $MFAResults = [PSCustomObject]@{
           DisplayName       = " - Not found"
           UserPrincipalName = $MsolUser
           DefualtMFAMethod  = $null
           MFAEnforced       = $null
           MFAMethods        = $null
         }
       }
       $AllMFAData += $MFAResults
     
    }
     
    $AllMFAData | Export-Csv $Home\Downloads\MFAResultsReport.csv -NoTypeInformation