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 🙂