Export-AuthenticationMethods
Exports user authentication methods and tenant policy settings to an Excel report.
Description
Connects to Microsoft Graph and retrieves all users' registered authentication methods, sign-in preferences, and tenant-level authentication method policy configurations. Outputs an Excel workbook with two sheets: a Summary (method usage counts and tenant policy state) and a Details sheet (one row per user with a Boolean column per method).
Parameters
-GraphCloud
The Microsoft Graph national cloud environment to connect to. Defaults to 'Global'. Accepted values: Global, USGov, USGovDoD, Germany, China.
| Property | Value |
|---|---|
| Type | String |
| Required | false |
| Default | Global |
-Tenant
Optional tenant ID or domain to target when authenticating to Microsoft Graph. If omitted, the default tenant for the authenticated account is used.
| Property | Value |
|---|---|
| Type | String |
| Required | false |
-Out
Optional output path for the Excel file (.xlsx).
Defaults to: ~\Downloads\
| Property | Value |
|---|---|
| Type | String |
| Required | false |
Examples
EXAMPLE 1
Connects to the global cloud and saves the report to the Downloads folder.
EXAMPLE 2
Targets a specific tenant and writes the report to a custom path.
Requires -Modules Microsoft.Graph.Authentication, ImportExcel
Script
Download Export-AuthenticationMethods.ps1
<#
.SYNOPSIS
Exports user authentication methods and tenant policy settings to an Excel report.
.DESCRIPTION
Connects to Microsoft Graph and retrieves all users' registered authentication methods,
sign-in preferences, and tenant-level authentication method policy configurations.
Outputs an Excel workbook with two sheets: a Summary (method usage counts and tenant
policy state) and a Details sheet (one row per user with a Boolean column per method).
.PARAMETER GraphCloud
The Microsoft Graph national cloud environment to connect to.
Defaults to 'Global'. Accepted values: Global, USGov, USGovDoD, Germany, China.
.PARAMETER Tenant
Optional tenant ID or domain to target when authenticating to Microsoft Graph.
If omitted, the default tenant for the authenticated account is used.
.PARAMETER Out
Optional output path for the Excel file (.xlsx).
Defaults to: ~\Downloads\<OrgName>-UserAuthMethods-<timestamp>.xlsx
.EXAMPLE
.\Export-AuthenticationMethods.ps1
Connects to the global cloud and saves the report to the Downloads folder.
.EXAMPLE
.\Export-AuthenticationMethods.ps1 -Tenant contoso.onmicrosoft.com -Out C:\Reports\auth.xlsx
Targets a specific tenant and writes the report to a custom path.
#>
#Requires -Modules Microsoft.Graph.Authentication, ImportExcel
[CmdletBinding()]
param(
[ValidateSet('Global', 'USGov', 'USGovDoD', 'Germany', 'China')]
[string]$GraphCloud = 'Global',
[string]$Tenant,
[string]$Out
)
function Log { param([string]$Msg, [ConsoleColor]$Color = 'White') Write-Host "[$(Get-Date -Format 'HH:mm')] $Msg" -ForegroundColor $Color }
$scopes = @(
'User.Read.All',
'Organization.Read.All',
'UserAuthenticationMethod.Read.All',
'Policy.Read.All'
)
if ($Tenant) { Connect-MgGraph -Scopes $scopes -Environment $GraphCloud -NoWelcome -ErrorAction Stop -TenantId $Tenant }
else { Connect-MgGraph -Scopes $scopes -Environment $GraphCloud -NoWelcome -ErrorAction Stop }
$Org = Get-MgOrganization
Log "Connected to Graph: $($Org.DisplayName)" Green
if (!$Out) {
$friendlyName = "UserAuthMethods"
$Out = Join-Path $env:USERPROFILE "Downloads\$($Org.DisplayName)-$friendlyName-$(Get-Date -Format 'yyyy-MM-dd_HH-mm').xlsx"
}
elseif ($Out -notlike '*.xlsx') { $Out += '.xlsx' }
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 }
}
}
Log "Fetching all users..."
$users = Get-MgUser -All -Property id, displayName, userPrincipalName, accountEnabled, createdDateTime
# Collect all method types present across the directory
$allMethodTypes = New-Object System.Collections.Generic.HashSet[string]
$userMethodMap = @{}
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
Log "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, get default authentication method or the system preferred method
try {
$pref = Get-MgBetaUserAuthenticationSignInPreference -UserId $u.Id -ErrorAction Stop
$row['IsSystemPreferred'] = [bool]$pref.IsSystemPreferredAuthenticationMethodEnabled
$row['PreferredMethod'] = $pref.UserPreferredMethodForSecondaryAuthentication
$sysPref = $null
if ($pref.PSObject.Properties.Name -contains 'SystemPreferredAuthenticationMethod') {
$sysPref = $pref.SystemPreferredAuthenticationMethod
}
elseif ($pref.AdditionalProperties -and $pref.AdditionalProperties.ContainsKey('systemPreferredAuthenticationMethod')) {
$sysPref = $pref.AdditionalProperties['systemPreferredAuthenticationMethod']
}
if ($sysPref) { $row['SystemPreferredMethod'] = $sysPref }
# 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)
Log "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 = @{
Path = $Out
AutoSize = $true
FreezeTopRow = $true
TableStyle = 'Medium2'
}
# User method summary
$summaryCounts | Export-Excel @excelParams -WorksheetName 'Summary' -TableName 'AuthMethodCounts'
# Tenant auth methods summary
$startRow = ($summaryCounts.Count + 3)
$summaryConfig | Export-Excel -WorksheetName 'Summary' -TableName 'AuthMethodConfig' -StartRow $startRow @excelParams
# Details
$rows | Export-Excel @excelParams -WorksheetName 'Details' -TableName 'UserAuthDetails'
Log "Exported report: $Out" Green
$answer = Read-Host "Open the report now? [Y/n]"
if ($answer -eq '' -or $answer -match '^y') {
Start-Process $Out
}