Export-AuthenticationMethodReport
Exports user authentication methods to Excel.
Example Output

Syntax
Description
Retrieves all users from Microsoft Entra ID along with their configured authentication methods, default method preferences, and tenant-level authentication method policies. Exports to Excel with Summary and Details worksheets.
Examples
Example 1
.\Export-AuthenticationMethodReport.ps1
# Exports authentication methods to an auto-named Excel file.
Example 2
.\Export-AuthenticationMethodReport.ps1 -Tenant 'contoso.onmicrosoft.com' -Out 'C:\Reports\AuthMethods.xlsx'
# Exports authentication methods for the specified tenant.
Parameters
-Tenant
Optional tenant ID or domain for specific tenant connection.
- Type:
String - Required: No
-Cloud
Cloud environment to connect to. Defaults to 'Global'. Valid values: Global, USGov, USGovDoD, Germany, China
- Type:
String - Required: No
- Valid values:
Global,GCC,GCCH,DoD,Germany,China
-Out
Output file path for the Excel export. If not specified, generates a filename based on tenant name and current timestamp.
- Type:
String - Required: No
Notes
Requires Microsoft.Graph and ImportExcel modules.
Full Script
Copy this complete standalone script (includes all helper functions):
#Requires -Version 7.0
#Requires -Modules ImportExcel, Microsoft.Graph.Authentication
<#
.SYNOPSIS
Exports user authentication methods to Excel.
.DESCRIPTION
Retrieves all users from Microsoft Entra ID along with their configured
authentication methods, default method preferences, and tenant-level
authentication method policies. Exports to Excel with Summary and Details
worksheets.
.PARAMETER Tenant
Optional tenant ID or domain for specific tenant connection.
.PARAMETER Cloud
Cloud environment to connect to. Defaults to 'Global'.
Valid values: Global, USGov, USGovDoD, Germany, China
.PARAMETER Out
Output file path for the Excel export. If not specified, generates
a filename based on tenant name and current timestamp.
.EXAMPLE
.\Export-AuthenticationMethodReport.ps1
# Exports authentication methods to an auto-named Excel file.
.EXAMPLE
.\Export-AuthenticationMethodReport.ps1 -Tenant 'contoso.onmicrosoft.com' -Out 'C:\Reports\AuthMethods.xlsx'
# Exports authentication methods for the specified tenant.
.NOTES
Requires Microsoft.Graph and ImportExcel modules.
#>
[CmdletBinding()]
param(
[string]$Tenant,
[ValidateSet('Global', 'GCC', 'GCCH', 'DoD', 'Germany', 'China')
#region Helper Functions
function Write-TimeLog {
[CmdletBinding()]
param(
[Parameter(Mandatory, Position = 0)]
[string]$Message,
[Parameter()]
[ConsoleColor]$Color = 'White'
)
Write-Host "[$(Get-Date -Format 'HH:mm')] $Message" -ForegroundColor $Color
}
function Connect-MgGraphIfNeeded {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string[]]$Scopes,
[Parameter()]
[string]$Tenant,
[Parameter()]
[ValidateSet('Global', 'GCC', 'GCCH', 'DoD', 'Germany', 'China')]
[string]$Cloud = 'Global'
)
$envMap = @{
'Global' = 'Global'
'GCC' = 'Global'
'GCCH' = 'USGov'
'DoD' = 'USGovDoD'
'Germany' = 'Germany'
'China' = 'China'
}
try {
$context = Get-MgContext
if ($context) {
$missingScopes = $Scopes | Where-Object { $_ -notin $context.Scopes }
$tenantMismatch = $false
if ($Tenant) {
$currentTenantId = $context.TenantId
if ($currentTenantId -ne $Tenant) {
$org = Get-MgOrganization -ErrorAction SilentlyContinue
$tenantDomain = $org.VerifiedDomains | Where-Object { $_.IsDefault } | Select-Object -ExpandProperty Name
if ($Tenant -ne $tenantDomain) {
$tenantMismatch = $true
Write-TimeLog "Current connection is to '$($org.DisplayName)' but '$Tenant' was requested" -Color Yellow
}
}
}
if (-not $missingScopes -and -not $tenantMismatch) {
$org = Get-MgOrganization
Write-TimeLog "Using existing Graph connection to $($org.DisplayName)" -Color Cyan
return $org
}
if ($tenantMismatch) {
Write-TimeLog "Reconnecting to switch to tenant: $Tenant" -Color Yellow
}
elseif ($missingScopes) {
Write-TimeLog "Reconnecting to add scopes: $($missingScopes -join ', ')" -Color Yellow
}
}
$connectParams = @{
Scopes = $Scopes
Environment = $envMap[$Cloud]
NoWelcome = $true
ErrorAction = 'Stop'
}
if ($Tenant) {
$connectParams['TenantId'] = $Tenant
}
Connect-MgGraph @connectParams
$org = Get-MgOrganization
Write-TimeLog "Connected to Graph: $($org.DisplayName)" -Color Green
$org
}
catch {
Write-TimeLog "Failed to connect to Microsoft Graph: $($_.Exception.Message)" -Color Red
throw
}
}
function Get-DefaultExcelParams {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Path
)
@{
Path = $Path
TableStyle = 'Medium2'
AutoSize = $true
FreezeTopRow = $true
}
}
function New-ReportPath {
[CmdletBinding()]
param(
[Parameter()]
[string]$Out,
[Parameter(Mandatory)]
[string]$FriendlyName,
[Parameter(Mandatory)]
[string]$OrgDisplayName,
[Parameter()]
[string]$Extension = 'xlsx'
)
if (!$Out) {
"$OrgDisplayName-$FriendlyName-$(Get-Date -Format 'yyyy-MM-dd_HH-mm').$Extension"
}
elseif ($Out -notlike "*.$Extension") {
"$Out.$Extension"
}
else {
$Out
}
}
#endregion Helper Functions
]
[string]$Cloud = 'Global',
[string]$Out
)
$scopes = @(
'User.Read.All',
'Organization.Read.All',
'UserAuthenticationMethod.Read.All',
'Policy.Read.All'
)
$org = Connect-MgGraphIfNeeded -Scopes $scopes -Tenant $Tenant -Cloud $Cloud
$Out = New-ReportPath -Out $Out -FriendlyName 'UserAuthMethods' -OrgDisplayName $org.DisplayName
Write-TimeLog "Fetching all users..."
$users = Get-MgUser -All -Property Id, DisplayName, UserPrincipalName, AccountEnabled, CreatedDateTime
# Helper function to convert OData type to friendly method name
function Convert-MethodType {
param([string]$OdataType)
if (-not $OdataType) { return 'Unknown' }
$t = $OdataType -replace '#microsoft.graph.', ''
switch -Regex ($t) {
'^emailAuthenticationMethod$' { 'Email' ; break }
'^fido2AuthenticationMethod$' { 'FIDO2' ; break }
'^microsoftAuthenticatorAuthenticationMethod$' { 'MSAuthenticator' ; break }
'^passwordAuthenticationMethod$' { 'Password' ; break }
'^phoneAuthenticationMethod$' { 'Phone' ; break }
'^softwareOathAuthenticationMethod$' { 'SoftwareOATH' ; break }
'^temporaryAccessPassAuthenticationMethod$' { 'TemporaryAccessPass' ; break }
'^windowsHelloForBusinessAuthenticationMethod$' { 'WindowsHelloForBusiness' ; break }
'^x509CertificateAuthenticationMethod$' { 'Certificate' ; break }
default { $t }
}
}
# Collect all method types present across the directory
$allMethodTypes = New-Object System.Collections.Generic.HashSet[string]
$userMethodMap = @{}
Write-TimeLog "Fetching authentication methods for $($users.Count) users..."
foreach ($u in $users) {
try {
$methods = Get-MgUserAuthenticationMethod -UserId $u.Id -ErrorAction Stop
$userMethodMap[$u.Id] = $methods
foreach ($m in $methods) {
$null = $allMethodTypes.Add( (Convert-MethodType -OdataType $m.AdditionalProperties['@odata.type']) )
}
}
catch {
Write-Warning "Failed to retrieve authentication methods for $($u.UserPrincipalName): $_"
$userMethodMap[$u.Id] = @()
}
}
# Create a stable, readable order for the dynamic columns
$preferredOrder = @(
'Password', 'MSAuthenticator', 'Phone', 'Email', 'FIDO2', 'WindowsHelloForBusiness', 'SoftwareOATH', 'TemporaryAccessPass', 'Certificate', 'Unknown'
)
$dynamicColumns = @()
foreach ($name in $preferredOrder) {
if ($allMethodTypes.Contains($name)) { $dynamicColumns += $name }
}
# Append any remaining uncommon types that were discovered
$remaining = $allMethodTypes | Where-Object { $_ -notin $dynamicColumns }
$dynamicColumns += ($remaining | Sort-Object)
# Build rows with one Boolean column per discovered method + default & system preferred status
Write-TimeLog "Fetching sign-in preferences..."
$rows = foreach ($u in $users) {
$row = [ordered]@{
DisplayName = $u.DisplayName
UserPrincipalName = $u.UserPrincipalName
AccountEnabled = $u.AccountEnabled
CreatedDate = $u.CreatedDateTime
MethodCount = 0
DefaultAuthMethod = $null
IsSystemPreferred = $null
}
foreach ($col in $dynamicColumns) { $row[$col] = $false }
$count = 0
foreach ($m in $userMethodMap[$u.Id]) {
$typeName = Convert-MethodType -OdataType $m.AdditionalProperties['@odata.type']
if ($row.Contains($typeName)) { $row[$typeName] = $true }
$count++
}
# Beta endpoint: get default authentication method or the system preferred method
try {
$pref = Invoke-MgGraphRequest -Method GET -Uri "/beta/users/$($u.Id)/authentication/signInPreferences" -ErrorAction Stop
$row['IsSystemPreferred'] = [bool]$pref.isSystemPreferredAuthenticationMethodEnabled
$row['PreferredMethod'] = $pref.userPreferredMethodForSecondaryAuthentication
if ($pref.systemPreferredAuthenticationMethod) {
$row['SystemPreferredMethod'] = $pref.systemPreferredAuthenticationMethod
}
# System preferred wins if enabled; otherwise, user's preferred selection
if ($row['IsSystemPreferred']) { $row['DefaultAuthMethod'] = $row['SystemPreferredMethod'] }
else { $row['DefaultAuthMethod'] = $row['PreferredMethod'] }
}
catch {
# Leave preference fields null if not available
}
$row['MethodCount'] = $count
[PSCustomObject]$row
}
# Summary data
$summaryCounts = foreach ($name in $dynamicColumns) {
[PSCustomObject]@{
Method = $name
UsersConfigured = ($rows | Where-Object { $_.$name -eq $true }).Count
}
}
# Tenant authentication method configurations (enabled/disabled)
Write-TimeLog "Fetching tenant authentication settings..."
$summaryConfig = @()
try {
$policy = Get-MgPolicyAuthenticationMethodPolicy -ErrorAction Stop
$configs = $policy.AuthenticationMethodConfigurations
foreach ($c in $configs) {
# Prefer AdditionalProperties['@odata.type'] when present; else fall back to Id mapping
$odata = $null
if ($c.AdditionalProperties -and $c.AdditionalProperties.ContainsKey('@odata.type')) {
$odata = [string]$c.AdditionalProperties['@odata.type']
$odata = $odata -replace '#microsoft.graph.', ''
}
$name = switch ($odata) {
'emailAuthenticationMethodConfiguration' { 'Email' }
'fido2AuthenticationMethodConfiguration' { 'FIDO2' }
'microsoftAuthenticatorAuthenticationMethodConfiguration' { 'MSAuthenticator' }
'passwordAuthenticationMethodConfiguration' { 'Password' }
'phoneAuthenticationMethodConfiguration' { 'Phone' }
'softwareOathAuthenticationMethodConfiguration' { 'SoftwareOATH' }
'temporaryAccessPassAuthenticationMethodConfiguration' { 'TemporaryAccessPass' }
'windowsHelloForBusinessAuthenticationMethodConfiguration' { 'WindowsHelloForBusiness' }
'x509CertificateAuthenticationMethodConfiguration' { 'Certificate' }
'smsAuthenticationMethodConfiguration' { 'SMS' }
'voiceAuthenticationMethodConfiguration' { 'Voice' }
Default {
switch ($c.Id) {
'Fido2' { 'FIDO2' }
'MicrosoftAuthenticator' { 'MSAuthenticator' }
'Password' { 'Password' }
'TemporaryAccessPass' { 'TemporaryAccessPass' }
'SoftwareOath' { 'SoftwareOATH' }
'Email' { 'Email' }
'Voice' { 'Voice' }
'Sms' { 'SMS' }
'Phone' { 'Phone' }
'WindowsHelloForBusiness' { 'WindowsHelloForBusiness' }
'X509Certificate' { 'Certificate' }
Default { if ($c.DisplayName) { $c.DisplayName } else { $c.Id } }
}
}
}
$enabled = $null
if ($c.PSObject.Properties.Name -contains 'State') { $enabled = ($c.State -eq 'enabled') }
elseif ($c.PSObject.Properties.Name -contains 'IsEnabled') { $enabled = [bool]$c.IsEnabled }
$summaryConfig += [PSCustomObject]@{
Method = $name
Enabled = $enabled
}
}
}
catch {
Write-Warning "Failed to retrieve authentication method policy: $_"
}
# Export to Excel
$excelParams = Get-DefaultExcelParams -Path $Out
$summaryCounts | Export-Excel @excelParams -WorksheetName 'Summary' -TableName 'AuthMethodCounts'
# Tenant auth methods summary - add below the counts table
$startRow = ($summaryCounts.Count + 3)
$summaryConfig | Export-Excel -Path $Out -WorksheetName 'Summary' -TableName 'AuthMethodConfig' -StartRow $startRow -AutoSize
$rows | Export-Excel @excelParams -WorksheetName 'Details' -TableName 'UserAuthDetails'
Write-TimeLog "Exported to: $Out" -Color Green