Src/Private/Get-AbrEntraIDAuthMethods.ps1

function Get-AbrEntraIDAuthMethods {
    <#
    .SYNOPSIS
    Documents the Entra ID Authentication Methods Policy configuration.
    .DESCRIPTION
        Collects and reports on:
          - Authentication Methods Policy overview (which methods are enabled tenant-wide)
          - Per-method configuration detail (target groups, restrictions, settings)
          - Per-user registered authentication methods
          - Temporary Access Pass (TAP) policy
          - FIDO2 security key policy
          - Microsoft Authenticator policy (number matching, additional context)
          - Password policy (smart lockout)
    .NOTES
        Version: 0.1.20
        Author: Pai Wei Sing
    #>

    [CmdletBinding()]
    param (
        [Parameter(Position = 0, Mandatory)]
        [string]$TenantId
    )

    begin {
        Write-PScriboMessage -Message "Collecting Entra ID Authentication Methods for tenant $TenantId." 
        Show-AbrDebugExecutionTime -Start -TitleMessage 'Authentication Methods'
    }

    process {
        #region Authentication Methods
        Section -Style Heading2 'Authentication Methods' {
            Paragraph "The following section documents the Authentication Methods Policy configured in tenant $TenantId."
            BlankLine

            #region Authentication Methods Policy -- method enablement overview
            try {
                Write-Host " - Retrieving Authentication Methods Policy..."
                $AuthPolicy = Get-MgPolicyAuthenticationMethodPolicy -ErrorAction Stop

                # Detect system-preferred MFA setting (systemCredentialPreferences)
                $SystemPreferredMfaEnabled = $false
                $SystemPreferredMfaState   = 'Not configured'
                try {
                    $SysPref = Invoke-MgGraphRequest -Method GET `
                        -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy' `
                        -ErrorAction SilentlyContinue
                    if ($SysPref -and $SysPref.systemCredentialPreferences) {
                        $state = $SysPref.systemCredentialPreferences.state
                        $SystemPreferredMfaEnabled = ($state -eq 'enabled')
                        $SystemPreferredMfaState   = if ($SystemPreferredMfaEnabled) { 'Enabled [OK]' } else { "Disabled [$state] -- [WARN] Enable so users are always prompted for their strongest method" }
                    }
                } catch {
                    $SystemPreferredMfaState = 'Unable to retrieve (requires beta endpoint)'
                }

                if ($AuthPolicy -and $AuthPolicy.AuthenticationMethodConfigurations) {

                    # Security classification for each method -- drives the Security Rating column
                # and the HealthCheck colour-coding. Kept in one place so updating is easy.
                $MethodMeta = @{
                    'microsoftAuthenticator' = @{ Name = 'Microsoft Authenticator';      Rating = 'Strong';               Recommendation = 'Enable -- strong MFA, supports number matching to prevent push-fatigue attacks.' }
                    'fido2'                  = @{ Name = 'FIDO2 Security Keys';          Rating = 'Phishing-Resistant';   Recommendation = 'Enable -- phishing-resistant, highest assurance. Required for ACSC E8 ML3.' }
                    'x509Certificate'        = @{ Name = 'Certificate-Based Auth (CBA)'; Rating = 'Phishing-Resistant';   Recommendation = 'Enable if PKI infrastructure exists -- phishing-resistant, meets E8 ML3.' }
                    'temporaryAccessPass'    = @{ Name = 'Temporary Access Pass (TAP)';  Rating = 'Strong (Temporary)';   Recommendation = 'Enable -- time-limited bootstrap token. Restrict to admins via group target.' }
                    'softwareOath'           = @{ Name = 'Software OATH Tokens';         Rating = 'Moderate';             Recommendation = 'Acceptable -- TOTP is stronger than SMS but weaker than Authenticator push.' }
                    'hardwareOath'           = @{ Name = 'Hardware OATH Tokens';         Rating = 'Moderate';             Recommendation = 'Acceptable -- physical TOTP token. Consider FIDO2 keys as a superior alternative.' }
                    'email'                  = @{ Name = 'Email OTP';                    Rating = 'WEAK -- Disable';      Recommendation = 'DISABLE -- email is not a secure second factor. Email accounts are frequently compromised.' }
                    'sms'                    = @{ Name = 'SMS (Text Message)';           Rating = 'WEAK -- Disable';      Recommendation = 'DISABLE -- susceptible to SIM-swap and SS7 interception attacks.' }
                    'voice'                  = @{ Name = 'Voice Call';                   Rating = 'WEAK -- Disable';      Recommendation = 'DISABLE -- same attack surface as SMS; voice calls can be intercepted or social-engineered.' }
                    'qrCodePin'              = @{ Name = 'QR Code + PIN';                 Rating = 'Moderate';             Recommendation = 'Limited availability -- designed for frontline workers without personal devices. Restrict scope carefully.' }
                    'windowsHelloForBusiness'= @{ Name = 'Windows Hello for Business';   Rating = 'Phishing-Resistant';   Recommendation = 'Enable -- phishing-resistant biometric/PIN tied to device. Meets E8 ML3.' }
                }

                $PolicyObj = [System.Collections.ArrayList]::new()
                    foreach ($Method in ($AuthPolicy.AuthenticationMethodConfigurations | Sort-Object Id)) {
                        $State = $Method.State
                        $Meta  = if ($MethodMeta.ContainsKey($Method.Id)) { $MethodMeta[$Method.Id] } else { $null }
                        $MethodName    = if ($Meta) { $Meta.Name           } else { $Method.Id }
                        $SecurityRating = if ($Meta) { $Meta.Rating        } else { 'Unknown'  }
                        $Recommendation = if ($Meta) { $Meta.Recommendation } else { 'Review this method and assess necessity.' }

                        # Determine target scope
                        $IncludeTargets = if ($Method.AdditionalProperties.includeTargets) {
                            $TargetList = $Method.AdditionalProperties.includeTargets
                            if ($TargetList | Where-Object { $_.id -eq 'all_users' }) { 'All Users' }
                            else { "$(@($TargetList).Count) group(s)" }
                        } else { 'N/A' }

                        $policyInObj = [ordered] @{
                            'Authentication Method'  = $MethodName
                            'State'                  = if ($State -eq 'enabled') { 'Enabled' } else { 'Disabled' }
                            'Security Rating'        = $SecurityRating
                            'Scope / Targets'        = $IncludeTargets
                            'Recommendation'         = $Recommendation
                        }
                        $PolicyObj.Add([pscustomobject]$policyInObj) | Out-Null
                    }

                    $null = (& {
                        if ($HealthCheck.EntraID.MFA) {
                            # Weak methods enabled = Warning (orange)
                            $null = ($PolicyObj | Where-Object {
                                $_.State -eq 'Enabled' -and $_.'Security Rating' -like 'WEAK*'
                            } | Set-Style -Style Warning | Out-Null)
                            # Strong/phishing-resistant methods disabled = Warning
                            $null = ($PolicyObj | Where-Object {
                                $_.State -eq 'Disabled' -and $_.'Security Rating' -in @('Strong','Phishing-Resistant')
                            } | Set-Style -Style Warning | Out-Null)
                            # Phishing-resistant methods enabled = OK (green)
                            $null = ($PolicyObj | Where-Object {
                                $_.State -eq 'Enabled' -and $_.'Security Rating' -eq 'Phishing-Resistant'
                            } | Set-Style -Style OK | Out-Null)
                        }
                    })

                    # ColumnWidths: Method(22) + State(10) + Rating(18) + Scope(12) + Recommendation(38) = 100
                    $PolicyTableParams = @{ Name = "Authentication Methods Policy - $TenantId"; List = $false; ColumnWidths = 22, 10, 18, 12, 38 }
                    if ($Report.ShowTableCaptions) { $PolicyTableParams['Caption'] = "- $($PolicyTableParams.Name)" }
                    $PolicyObj | Table @PolicyTableParams

                    # Store for Excel export
                    $null = ($script:ExcelSheets['Auth Methods Policy'] = $PolicyObj)

                    #region ACSC E8 + CIS Authentication Methods Assessment
                    $null = (& { if ($script:IncludeACSCe8) {
                        BlankLine
                        Paragraph "ACSC Essential Eight Maturity Level Assessment -- Authentication Methods:"
                        BlankLine
                    }})
                    $null = (& { if ($script:IncludeCISBaseline) {
                        BlankLine
                        Paragraph "CIS Microsoft 365 Foundations Benchmark Assessment -- Authentication Methods:"
                        BlankLine
                    }})

                    # Derive compliance check variables from $PolicyObj
                    $StrongMethodCount     = if ($PolicyObj) { @($PolicyObj | Where-Object { $_.'Security Rating' -in @('Strong','Strong (Temporary)','Phishing-Resistant') -and $_.State -eq 'Enabled' }).Count } else { 0 }
                    $AuthenticatorEnabled  = ($PolicyObj | Where-Object { $_.'Authentication Method' -eq 'Microsoft Authenticator' -and $_.State -eq 'Enabled' }) -ne $null
                    $AuthenticatorState    = if ($AuthenticatorEnabled) { 'Enabled' } else { 'Disabled' }
                    $SmsEnabled            = ($PolicyObj | Where-Object { $_.'Authentication Method' -eq 'SMS (Text Message)'     -and $_.State -eq 'Enabled' }) -ne $null
                    $VoiceEnabled          = ($PolicyObj | Where-Object { $_.'Authentication Method' -eq 'Voice Call'             -and $_.State -eq 'Enabled' }) -ne $null
                    $EmailOtpEnabled       = ($PolicyObj | Where-Object { $_.'Authentication Method' -eq 'Email OTP'              -and $_.State -eq 'Enabled' }) -ne $null
                    $Fido2Enabled          = ($PolicyObj | Where-Object { $_.'Authentication Method' -eq 'FIDO2 Security Keys'    -and $_.State -eq 'Enabled' }) -ne $null
                    $CbaEnabled            = ($PolicyObj | Where-Object { $_.'Authentication Method' -eq 'Certificate-Based Auth (CBA)' -and $_.State -eq 'Enabled' }) -ne $null
                    $SmsState              = if ($SmsEnabled)      { 'Enabled' }  else { 'Disabled' }
                    $VoiceState            = if ($VoiceEnabled)    { 'Enabled' }  else { 'Disabled' }
                    $EmailOtpState         = if ($EmailOtpEnabled) { 'Enabled' }  else { 'Disabled' }
                    $Fido2State            = if ($Fido2Enabled)    { 'Enabled' }  else { 'Disabled' }
                    $CbaState              = if ($CbaEnabled)      { 'Enabled' }  else { 'Disabled' }

                    # Number matching
                    $NumberMatchEnabled = $false; $NumberMatchState = 'Not Configured'
                    try {
                        $AuthPol2 = Get-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration `
                            -AuthenticationMethodConfigurationId 'microsoftAuthenticator' -ErrorAction SilentlyContinue
                        if ($AuthPol2) {
                            $nmState = $AuthPol2.AdditionalProperties.featureSettings.numberMatchingRequiredState.state
                            $NumberMatchEnabled = ($nmState -eq 'enabled')
                            $NumberMatchState   = if ($NumberMatchEnabled) { 'Enabled' } else { if ($nmState) { $nmState } else { 'Not Configured' } }
                        }
                    } catch {}

                    # System-preferred MFA
                    $SystemPreferredMfaEnabled = $false; $SystemPreferredMfaState = 'Not Configured'
                    try {
                        $RegEnf = $AuthPolicy.AdditionalProperties.registrationEnforcement
                        if ($RegEnf) {
                            $sp = $RegEnf.authenticationMethodsRegistrationCampaign.state
                            $SystemPreferredMfaEnabled = ($sp -eq 'enabled')
                            $SystemPreferredMfaState   = if ($SystemPreferredMfaEnabled) { 'Enabled [OK]' } else { 'Disabled [WARN]' }
                        }
                    } catch {}

                    $_ComplianceVars = @{
                        'StrongMethodCount'         = $StrongMethodCount
                        'AuthenticatorEnabled'      = $AuthenticatorEnabled
                        'AuthenticatorState'        = $AuthenticatorState
                        'SmsEnabled'                = $SmsEnabled
                        'VoiceEnabled'              = $VoiceEnabled
                        'EmailOtpEnabled'           = $EmailOtpEnabled
                        'SmsState'                  = $SmsState
                        'VoiceState'                = $VoiceState
                        'EmailOtpState'             = $EmailOtpState
                        'NumberMatchEnabled'        = $NumberMatchEnabled
                        'NumberMatchState'          = $NumberMatchState
                        'Fido2Enabled'              = $Fido2Enabled
                        'CbaEnabled'                = $CbaEnabled
                        'Fido2State'                = $Fido2State
                        'CbaState'                  = $CbaState
                        'SystemPreferredMfaEnabled' = $SystemPreferredMfaEnabled
                        'SystemPreferredMfaState'   = $SystemPreferredMfaState
                    }

                    $null = (& { if ($script:IncludeACSCe8) {
                        $E8AuthChecks = Build-AbrComplianceChecks `
                            -Definitions (Get-AbrE8Checks -Section 'AuthMethods') `
                            -Framework E8 `
                            -CallerVariables $_ComplianceVars
                        New-AbrE8AssessmentTable -Checks $E8AuthChecks -Name 'Authentication Methods' -TenantId $TenantId
                        if ($E8AuthChecks) { $null = $script:E8AllChecks.AddRange([object[]](@($E8AuthChecks | Select-Object @{N='Section';E={'Authentication Methods'}}, ML, Control, Status, Detail ))) }
                    }})

                    $null = (& { if ($script:IncludeCISBaseline) {
                        $CISAuthChecks = Build-AbrComplianceChecks `
                            -Definitions (Get-AbrCISChecks -Section 'AuthMethods') `
                            -Framework CIS `
                            -CallerVariables $_ComplianceVars
                        New-AbrCISAssessmentTable -Checks $CISAuthChecks -Name 'Authentication Methods' -TenantId $TenantId
                        if ($CISAuthChecks) { $null = $script:CISAllChecks.AddRange([object[]](@($CISAuthChecks | Select-Object @{N='Section';E={'Authentication Methods'}}, CISControl, Level, Status, Detail ))) }
                    }})
                    #endregion
                }
            } catch {
                Write-AbrSectionError -Section 'Authentication Methods Policy' -Message "$($_.Exception.Message)" 
                Paragraph "Authentication Methods Policy could not be retrieved. Ensure Policy.Read.All permission is granted."
            }
            #endregion

            #region Microsoft Authenticator Detail
            if ($InfoLevel.AuthenticationMethods -ge 2) {
                try {
                    $AuthenticatorPolicy = Get-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration `
                        -AuthenticationMethodConfigurationId 'microsoftAuthenticator' `
                        -ErrorAction SilentlyContinue

                    if ($AuthenticatorPolicy) {
                        Section -Style Heading3 'Microsoft Authenticator Configuration' {
                            Paragraph "Detailed configuration of the Microsoft Authenticator method in tenant $TenantId."
                            BlankLine

                            $AuthApp = $AuthenticatorPolicy.AdditionalProperties
                            $AuthObj = [System.Collections.ArrayList]::new()
                            $authInObj = [ordered] @{
                                'State'                          = $AuthenticatorPolicy.State
                                'Number Matching'                = if ($AuthApp.featureSettings.numberMatchingRequiredState.state) { $AuthApp.featureSettings.numberMatchingRequiredState.state } else { 'Not Configured' }
                                'Show App Name (Additional Context)'    = if ($AuthApp.featureSettings.displayAppInformationRequiredState.state) { $AuthApp.featureSettings.displayAppInformationRequiredState.state } else { 'Not Configured' }
                                'Show Geographic Location'       = if ($AuthApp.featureSettings.displayLocationInformationRequiredState.state) { $AuthApp.featureSettings.displayLocationInformationRequiredState.state } else { 'Not Configured' }
                            }
                            $AuthObj.Add([pscustomobject]$authInObj) | Out-Null
                    $null = (& {
                        if ($HealthCheck.EntraID.MFA) {
                        $null = ($AuthObj | Where-Object { $_.'Number Matching' -in @('disabled', 'Not Configured') } | Set-Style -Style Warning | Out-Null)
                        }
                    })

                            $AuthTableParams = @{ Name = "Microsoft Authenticator Settings - $TenantId"; List = $true; ColumnWidths = 55, 45 }
                    if ($Report.ShowTableCaptions) { $AuthTableParams['Caption'] = "- $($AuthTableParams.Name)" }
                            $AuthObj | Table @AuthTableParams
                        }
                    }
                } catch {
                    Write-AbrSectionError -Section 'Authenticator Policy Detail' -Message "$($_.Exception.Message)" 
                }

                #region FIDO2 Detail
                try {
                    $Fido2Policy = Get-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration `
                        -AuthenticationMethodConfigurationId 'fido2' `
                        -ErrorAction SilentlyContinue

                    if ($Fido2Policy) {
                        Section -Style Heading3 'FIDO2 Security Key Configuration' {
                            Paragraph "Detailed configuration of FIDO2 security keys in tenant $TenantId."
                            BlankLine

                            $Fido2Props = $Fido2Policy.AdditionalProperties
                            $Fido2Obj   = [System.Collections.ArrayList]::new()
                            $fido2InObj = [ordered] @{
                                'State'                               = $Fido2Policy.State
                                'Self-Service Setup Allowed'          = if ($null -ne $Fido2Props.isSelfServiceRegistrationAllowed)    { $Fido2Props.isSelfServiceRegistrationAllowed }    else { '--' }
                                'Enforce Attestation'                 = if ($null -ne $Fido2Props.isAttestationEnforced)               { $Fido2Props.isAttestationEnforced }               else { '--' }
                                'Key Restriction Enforced'            = if ($null -ne $Fido2Props.keyRestrictions.isEnforced)          { $Fido2Props.keyRestrictions.isEnforced }          else { '--' }
                                'Restriction Enforcement Type'        = if ($Fido2Props.keyRestrictions.enforcementType)               { $Fido2Props.keyRestrictions.enforcementType }     else { 'N/A' }
                                'Allowed / Blocked AAGUIDs'          = if ($Fido2Props.keyRestrictions.aaGuids)                        { ($Fido2Props.keyRestrictions.aaGuids -join ', ') } else { 'None' }
                            }
                            $Fido2Obj.Add([pscustomobject](ConvertTo-HashToYN $fido2InObj)) | Out-Null

                            $Fido2TableParams = @{ Name = "FIDO2 Policy Settings - $TenantId"; List = $true; ColumnWidths = 55, 45 }
                    if ($Report.ShowTableCaptions) { $Fido2TableParams['Caption'] = "- $($Fido2TableParams.Name)" }
                            $Fido2Obj | Table @Fido2TableParams
                        }
                    }
                } catch {
                    Write-AbrSectionError -Section 'FIDO2 Policy Detail' -Message "$($_.Exception.Message)" 
                }
                #endregion

                #region Temporary Access Pass Detail
                try {
                    $TapPolicy = Get-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration `
                        -AuthenticationMethodConfigurationId 'temporaryAccessPass' `
                        -ErrorAction SilentlyContinue

                    if ($TapPolicy) {
                        Section -Style Heading3 'Temporary Access Pass (TAP) Configuration' {
                            Paragraph "Configuration of Temporary Access Pass in tenant $TenantId."
                            BlankLine

                            $TapProps = $TapPolicy.AdditionalProperties
                            $TapObj   = [System.Collections.ArrayList]::new()
                            $tapInObj = [ordered] @{
                                'State'                          = $TapPolicy.State
                                'Default Lifetime (minutes)'     = if ($TapProps.defaultLifetimeInMinutes) { $TapProps.defaultLifetimeInMinutes } else { '--' }
                                'Minimum Lifetime (minutes)'     = if ($TapProps.minimumLifetimeInMinutes) { $TapProps.minimumLifetimeInMinutes } else { '--' }
                                'Maximum Lifetime (minutes)'     = if ($TapProps.maximumLifetimeInMinutes) { $TapProps.maximumLifetimeInMinutes } else { '--' }
                                'Default One-Time Use'           = if ($null -ne $TapProps.isUsableOnce) { $TapProps.isUsableOnce } else { '--' }
                            }
                            $TapObj.Add([pscustomobject](ConvertTo-HashToYN $tapInObj)) | Out-Null

                            $TapTableParams = @{ Name = "TAP Policy Settings - $TenantId"; List = $true; ColumnWidths = 55, 45 }
                    if ($Report.ShowTableCaptions) { $TapTableParams['Caption'] = "- $($TapTableParams.Name)" }
                            $TapObj | Table @TapTableParams
                        }
                    }
                } catch {
                    Write-AbrSectionError -Section 'TAP Policy Detail' -Message "$($_.Exception.Message)" 
                }
                #endregion
            }
            #endregion

            #region Per-User Registered Authentication Methods
            if ($InfoLevel.AuthenticationMethods -ge 2) {
                try {
                    Write-Host " - Retrieving per-user registered authentication methods (may take time for large tenants)..."
                    $AllUsers = Get-MgUser -All -Property Id,DisplayName,UserPrincipalName,UserType,AccountEnabled -ErrorAction Stop |
                        Where-Object { $_.UserType -eq 'Member' -and $_.AccountEnabled -eq $true }

                    $UserMethodObj = [System.Collections.ArrayList]::new()

                    foreach ($User in ($AllUsers | Sort-Object DisplayName)) {
                        try {
                            $UserMethods = Get-MgUserAuthenticationMethod -UserId $User.Id -ErrorAction SilentlyContinue
                            $MethodNames = [System.Collections.ArrayList]::new()

                            foreach ($Method in $UserMethods) {
                                $OdataType = $Method.AdditionalProperties['@odata.type']
                                $FriendlyName = switch ($OdataType) {
                                    '#microsoft.graph.microsoftAuthenticatorAuthenticationMethod' { 'Microsoft Authenticator' }
                                    '#microsoft.graph.phoneAuthenticationMethod'                 { 'Phone (SMS/Voice)'      }
                                    '#microsoft.graph.fido2AuthenticationMethod'                 { 'FIDO2 Security Key'      }
                                    '#microsoft.graph.passwordAuthenticationMethod'              { 'Password'               }
                                    '#microsoft.graph.softwareOathAuthenticationMethod'          { 'Software OATH Token'    }
                                    '#microsoft.graph.temporaryAccessPassAuthenticationMethod'   { 'Temporary Access Pass'  }
                                    '#microsoft.graph.windowsHelloForBusinessAuthenticationMethod' { 'Windows Hello for Business' }
                                    '#microsoft.graph.emailAuthenticationMethod'                 { 'Email OTP'              }
                                    default                                                       { $OdataType               }
                                }
                                $null = $MethodNames.Add($FriendlyName)
                            }

                            # Remove 'Password' from method list for cleaner output -- it's always present
                            $MfaMethods = $MethodNames | Where-Object { $_ -ne 'Password' }

                            $userMethodInObj = [ordered] @{
                                'Display Name'          = $User.DisplayName
                                'UPN'                   = $User.UserPrincipalName
                                'Registered Methods'    = if ($MfaMethods) { ($MfaMethods | Select-Object -Unique) -join ', ' } else { 'None' }
                                'Method Count'          = @($MfaMethods | Select-Object -Unique).Count
                                'Has MFA Method'        = ($MfaMethods -and @($MfaMethods | Select-Object -Unique).Count -gt 0)
                            }
                            $UserMethodObj.Add([pscustomobject](ConvertTo-HashToYN $userMethodInObj)) | Out-Null

                        } catch {
                            Write-PScriboMessage -IsWarning -Message "Auth methods for '$($User.DisplayName)': $($_.Exception.Message)" 
                        }
                    }

                    if ($UserMethodObj.Count -gt 0) {
                        Section -Style Heading3 'Registered Authentication Methods per User' {
                            Paragraph "The following table lists the authentication methods registered by each enabled member user in tenant $TenantId."
                            BlankLine

                            $null = (& {
                                if ($HealthCheck.EntraID.MFA) {
                                    $null = ($UserMethodObj | Where-Object { $_.'Has MFA Method' -eq 'No' } | Set-Style -Style Critical | Out-Null)
                                    $null = ($UserMethodObj | Where-Object { $_.'Method Count' -eq '1' }    | Set-Style -Style Warning  | Out-Null)
                                }
                            })

                            $UserMethodTableParams = @{ Name = "Per-User Authentication Methods - $TenantId"; List = $false; ColumnWidths = 22, 32, 30, 10, 6 }
                    if ($Report.ShowTableCaptions) { $UserMethodTableParams['Caption'] = "- $($UserMethodTableParams.Name)" }
                            $UserMethodObj | Table @UserMethodTableParams

                            # Store for Excel export
                            $null = ($script:ExcelSheets['Per-User Auth Methods'] = $UserMethodObj)
                        }
                    }
                } catch {
                    Write-AbrSectionError -Section 'Per-User Authentication Methods' -Message "$($_.Exception.Message)" 
                }
            }
            #endregion


        } # end Section Authentication Methods
        #endregion
    }

    end {
        Show-AbrDebugExecutionTime -End -TitleMessage 'Authentication Methods'
    }
}