Azure AD contains several different means of signing in; password, mobile phone, fido2 keys etc., but how are your users using these options? Here is a script for reporting statistics for exactly that.
I will skip information on how to get yourself an access token using app registrations this time – already have 100 posts showing how.
Easiest option to run the script once:
1. Go to https://developer.microsoft.com/en-us/graph/graph-explorer, sign in with your Azure AD global reader account
2. Make sure you consent to “User.Read.All” and “UserAuthenticationMethod.Read.All”

3. Copy the access token into the script below and run

$accessToken = "eyJ0eXAiOiJKV1QiLCJub25j---------O_dHsdrcf4WCHPYI4M-jg"
$VerbosePreference = "continue"
# No need to edit below this line
$restParams = @{Headers = @{Authorization = "Bearer $accessToken"}}
Write-Verbose "Getting all users"
$userlist = New-Object System.Collections.ArrayList
$uri = "https://graph.microsoft.com/v1.0/users?`$top=999"
do {
$r = Invoke-RestMethod $uri @restParams
if($r.value) {
$userlist.AddRange($r.value)
}
$uri = $r.'@odata.nextLink'
} while($uri)
# Function that processes a batch, returning report lines per auth method per user
function Process-GraphBatch
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,Position=0)]
[System.Collections.ArrayList] $Batch,
[Parameter(Mandatory=$true,Position=1)]
[System.Collections.ArrayList] $BatchUser
)
Process {
Write-Verbose "Sending batch of $($Batch.count) requests"
$batchBody = @{requests = $Batch} | ConvertTo-Json -Depth 7
Write-Debug $batchBody
$r = Invoke-RestMethod "https://graph.microsoft.com/beta/`$batch" -Method POST -ContentType "application/json" -Body $batchBody @restParams
Write-Debug ($r | ConvertTo-Json)
$r.responses | ForEach-Object {
if($_.status -ge 400) {
Write-Warning "Request failed with error $($_.body.error)"
} else {
$user = $BatchUser[$_.id - 1]
$_.body.value | ForEach-Object {
[PSCustomObject] @{
UserPrincipalName = $user.userprincipalname
ObjectID = $user.id
Method = $_.'@odata.type'
PhoneNumber = $_.phoneNumber
PhoneType = $_.office
smsSignInState = $_.smsSignInState
emailAddress = $_.emailAddress
displayName = $_.displayName
model = $_.model
attenstationLevel = $_.attestationLevel
}
}
}
}
}
}
Write-Verbose "Processing users into batches of 20 users (for speed)"
$report = $userlist | ForEach-Object `
-Begin {
# Initialize variables
$inc = 0
$batch = New-Object System.Collections.ArrayList
$batchUser = New-Object System.Collections.ArrayList
}`
-Process {
# Add user to batchUser and request to batch variable
Write-Verbose "$inc / $($userlist.Count)"; $inc = $inc + 1
$batchUser.Add($_) | Out-Null
$batch.Add(@{
id = "{0}" -f ($batch.Count + 1)
method = "GET"
url = "/users/$($_.id)/authentication/methods"
}) | Out-Null
# If the batch is 20 long, process it
if($inc % 20 -eq 0) {
Process-GraphBatch $batch $batchUser
$batch.Clear()
$batchUser.Clear()
}
}`
-End {
if($batch.Count -gt 0) {
Process-GraphBatch $batch $batchUser
}
}
$report | Out-GridView
# Report unique users using the different methods
$report | group ObjectId | Foreach{$_.Group[0]} | group Method | Select Name, Count | sort Count -Descending

Good luck 🙂
Antarctica (the territory South of 60 deg S)