Public/Get-GkUserMfaStatus.ps1

function Get-GkUserMfaStatus {
    <#
    .SYNOPSIS
        Report per-user authentication-method registration and MFA capability from the
        authentication methods registration report.

    .DESCRIPTION
        Reads GET /reports/authenticationMethods/userRegistrationDetails, which returns a
        ready-made per-user view of registered methods and capability flags across the tenant in
        a single paged call — preferred over enumerating /users/{id}/authentication/methods per user.

        Capability distinction (per Microsoft Graph):
          * IsMfaCapable = registered a strong method that is ALLOWED by the auth methods policy.
          * IsMfaRegistered = registered a strong method (not necessarily policy-allowed).
        The data has some latency; LastUpdated (lastUpdatedDateTime) reflects when it was refreshed.

        Requires the AuditLog.Read.All scope (this endpoint does not accept Reports.Read.All).

    .PARAMETER NotMfaCapableOnly
        Return only users who are not MFA-capable (server-side filter isMfaCapable eq false).

    .PARAMETER AdminsOnly
        Return only users flagged as admins (server-side filter isAdmin eq true).

    .PARAMETER AsReport
        Flatten MethodsRegistered to a single '; '-joined string and add ReportGeneratedUtc.

    .EXAMPLE
        Get-GkUserMfaStatus -NotMfaCapableOnly | Sort-Object UserPrincipalName

        Every user who cannot perform MFA — the gap list.

    .EXAMPLE
        Get-GkUserMfaStatus -AdminsOnly | Where-Object { -not $_.IsMfaCapable }

        Admins without MFA capability — a priority remediation set.

    .EXAMPLE
        Get-GkUserMfaStatus -AsReport | Export-Csv .\mfa-status.csv -NoTypeInformation

    .OUTPUTS
        PSGraphKit.UserMfaStatus
    #>

    [CmdletBinding()]
    [OutputType('PSGraphKit.UserMfaStatus')]
    param(
        [switch] $NotMfaCapableOnly,

        [switch] $AdminsOnly,

        [switch] $AsReport
    )

    begin {
        Test-GkConnection -FunctionName 'Get-GkUserMfaStatus' | Out-Null
        $now = [datetime]::UtcNow
    }

    process {
        $filters = @()
        if ($NotMfaCapableOnly) { $filters += 'isMfaCapable eq false' }
        if ($AdminsOnly)        { $filters += 'isAdmin eq true' }

        $uri = '/reports/authenticationMethods/userRegistrationDetails'
        if ($filters.Count -gt 0) { $uri += '?$filter=' + ($filters -join ' and ') }

        $details = Invoke-GkGraphRequest -Uri $uri -CallerFunction 'Get-GkUserMfaStatus'

        foreach ($d in $details) {
            $methods = @(Get-GkDictValue $d 'methodsRegistered')

            $obj = [ordered]@{
                PSTypeName            = 'PSGraphKit.UserMfaStatus'
                UserPrincipalName     = [string](Get-GkDictValue $d 'userPrincipalName')
                DisplayName           = [string](Get-GkDictValue $d 'userDisplayName')
                IsAdmin               = [bool](Get-GkDictValue $d 'isAdmin')
                IsMfaCapable          = [bool](Get-GkDictValue $d 'isMfaCapable')
                IsMfaRegistered       = [bool](Get-GkDictValue $d 'isMfaRegistered')
                IsPasswordlessCapable = [bool](Get-GkDictValue $d 'isPasswordlessCapable')
                IsSsprCapable         = [bool](Get-GkDictValue $d 'isSsprCapable')
                IsSsprRegistered      = [bool](Get-GkDictValue $d 'isSsprRegistered')
                MethodsRegistered     = if ($AsReport) { $methods -join '; ' } else { $methods }
                MethodCount           = $methods.Count
                UserType              = [string](Get-GkDictValue $d 'userType')
                LastUpdated           = ConvertTo-GkDateTime (Get-GkDictValue $d 'lastUpdatedDateTime')
                Id                    = [string](Get-GkDictValue $d 'id')
            }
            if ($AsReport) { $obj['ReportGeneratedUtc'] = $now }
            [pscustomobject]$obj
        }
    }
}