Entra/Get-MfaReport.ps1

<#
.SYNOPSIS
    Reports MFA registration details for all users in Entra ID.
.DESCRIPTION
    Queries the Microsoft Graph authentication methods user registration details
    endpoint to produce a per-user MFA and SSPR registration report. Shows whether
    each user is MFA registered, MFA capable, passwordless capable, SSPR registered,
    SSPR capable, and which methods they have enrolled. Essential for security
    assessments and MFA/SSPR adoption tracking.
 
    Requires Microsoft.Graph.Reports module and the following permissions:
    AuditLog.Read.All, UserAuthenticationMethod.Read.All
.PARAMETER OutputPath
    Optional path to export results as CSV. If not specified, results are returned
    to the pipeline.
.EXAMPLE
    PS> . .\Common\Connect-Service.ps1
    PS> Connect-Service -Service Graph -Scopes 'AuditLog.Read.All','UserAuthenticationMethod.Read.All'
    PS> .\Entra\Get-MfaReport.ps1
 
    Displays MFA registration status for all users.
.EXAMPLE
    PS> .\Entra\Get-MfaReport.ps1 -OutputPath '.\mfa-report.csv'
 
    Exports MFA registration details to CSV for review.
#>

[CmdletBinding()]
param(
    [Parameter()]
    [ValidateNotNullOrEmpty()]
    [string]$OutputPath
)

$ErrorActionPreference = 'Stop'

function Get-MfaMethodStrength {
    <#
    .SYNOPSIS
        Classifies the strongest MFA method from a list of registered methods.
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter()]
        [string[]]$Methods
    )

    $phishingResistant = @(
        'fido2'
        'windowsHelloForBusiness'
        'x509CertificateMultiFactor'
        'passKeyDeviceBound'
        'passKeyDeviceBoundAuthenticator'
    )
    $standard = @(
        'microsoftAuthenticatorPush'
        'microsoftAuthenticatorPasswordless'
        'softwareOneTimePasscode'
    )
    $weak = @(
        'mobilePhone'
        'alternateMobilePhone'
        'voiceAlternateMobile'
        'email'
    )

    if (-not $Methods -or $Methods.Count -eq 0) { return 'None' }
    if ($Methods | Where-Object { $_ -in $phishingResistant }) { return 'Phishing-Resistant' }
    if ($Methods | Where-Object { $_ -in $standard }) { return 'Standard' }
    if ($Methods | Where-Object { $_ -in $weak }) { return 'Weak' }
    return 'Unknown'
}

# Verify Graph connection
if (-not (Assert-GraphConnection)) { return }

# Ensure required Graph submodule is loaded (PS 7.x does not auto-import)
Import-Module -Name Microsoft.Graph.Reports -ErrorAction Stop

# Retrieve MFA registration details
try {
    Write-Verbose "Retrieving authentication method registration details..."
    $registrationDetails = Get-MgReportAuthenticationMethodUserRegistrationDetail -All -ErrorAction Stop
}
catch {
    Write-Warning "Could not retrieve MFA registration details (requires Azure AD Premium P1/P2): $($_.Exception.Message)"
    return
}

$allDetails = @($registrationDetails)
Write-Verbose "Processing MFA details for $($allDetails.Count) users..."

if ($allDetails.Count -eq 0) {
    Write-Verbose "No MFA registration details found"
    return
}

$report = foreach ($detail in $allDetails) {
    $methodsRegistered = if ($detail.MethodsRegistered) {
        ($detail.MethodsRegistered | Sort-Object) -join '; '
    }
    else {
        ''
    }

    [PSCustomObject]@{
        UserPrincipalName     = $detail.UserPrincipalName
        UserDisplayName       = $detail.UserDisplayName
        IsMfaRegistered       = $detail.IsMfaRegistered
        IsMfaCapable          = $detail.IsMfaCapable
        IsPasswordlessCapable = $detail.IsPasswordlessCapable
        IsSsprRegistered      = $detail.IsSsprRegistered
        IsSsprCapable         = $detail.IsSsprCapable
        MethodsRegistered     = $methodsRegistered
        DefaultMfaMethod      = $detail.DefaultMfaMethod
        MfaStrength           = (Get-MfaMethodStrength -Methods @($detail.MethodsRegistered))
        IsAdmin               = $detail.IsAdmin
    }
}

$report = @($report) | Sort-Object -Property UserPrincipalName

Write-Verbose "Found $($report.Count) user MFA registration records"

if ($OutputPath) {
    $report | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
    Write-Output "Exported MFA report ($($report.Count) users) to $OutputPath"
}
else {
    Write-Output $report
}