Replacing SMS and voice MFA, Part 2: Who is actually ready?

Part 2 - Who is actually ready? Reporting on authentication methods

This series:
Part 1 - SMS and voice MFA is ending: lock down your passkey profiles first
Part 2 - Who is actually ready? (you are here)
Part 3 - Enforcing the right passkey at sign-in
Part 4 - The people problem

In short: Before changing any sign-in settings, find out what each user has actually set up. This part shows the built-in Entra ID reports, why they are not enough on their own, and two free scripts that sort every user into a readiness group - from “ready” to “will be locked out in February 2027” - and turn those groups into action lists.

In Part 1, Contoso replaced the loose Default passkey profile with an Authenticator profile for all users and a YubiKey profile for Tier 0. In a production tenant, this report comes first: you need to know where every user stands before moving anyone off the Default profile. Otherwise the first anyone hears of a problem is a user who cannot sign in.

Start with the headline number

Entra ID > Authentication methods > Activity gives you the headline. In the Contoso tenant, 2 of 34 users were MFA capable and 0 were passwordless capable.

Authentication methods activity - 0 of 34 users passwordless capable

That is a useful number for a steering group. It does not tell you who to contact or what each of them needs.

Microsoft’s analyser checks policy, not people

Microsoft has published a PowerShell script for the retirement, Get-SmsVoicePolicyUsers.ps1 (github.com/microsoft/entra-sms-voice-usage-analyzer). It is worth running, but be clear about what it does. It reads the SMS and Voice method policy scope, the registration campaign state and the migration state, and exports the targeted groups and users to CSV.

It is a policy scope scanner, not a user inventory. It does not expand group membership, check registered methods or read sign-in activity. It tells you who SMS and voice are enabled for, not who depends on them.

User registration details

Entra ID > Authentication methods > User registration details is the per-user view. Each row shows whether the user is MFA capable, passwordless capable and SSPR capable, their default MFA method and every method they have registered. It can be filtered and downloaded as CSV.

User registration details - user names blurred

The Methods registered filter now separates Passkey (Microsoft Authenticator) from other passkeys, which is exactly the distinction the profiles in Part 1 depend on.

Methods registered filter

Why I built my own readiness scripts

Before Microsoft Graph, the quickest way to see each user’s methods was the MSOnline PowerShell module. With MSOnline retired, the community has filled the gap - AdminDroid’s free Export MFA Status Report script (published in the admindroid-community/powershell-scripts repository on GitHub) is a good example. In client reports I have generated, per-user reports like this have consistently surfaced users who have never registered Microsoft Authenticator - often users whose only method is a mobile phone number. Those are the users who will hit the blocking prompt in February 2027.

Generic MFA reports answer the question “does this user have MFA?”. For this migration the question is different: “will this user still be able to sign in once SMS and voice are gone and the restrictive passkey profiles from Part 1 apply?”. That needs the AAGUID of every registered passkey, checked against the profiles you plan to deploy - so I wrote two scripts for it, published in the cloud-secure-ai GitHub repository.

ScriptWhat it does
Get-PasskeyReadiness.ps1Read-only. Reads every user’s registered methods, including passkey models and AAGUIDs, and places each user in one readiness bucket. Exports a CSV and a console summary.
Sync-PasskeyReadinessGroups.ps1Creates one security group per bucket and keeps membership up to date. Supports -WhatIf.

The readiness buckets

BucketMeaning and action
SmsVoiceOnlyOnly phone-based MFA registered. Blocked from 1 February 2027. Highest priority.
PasskeyOutsideAllowHas a passkey whose AAGUID is not on your allow list - for example a synced passkey. It stops working when the Default profile is retired.
PrivilegedNoHardwareKeyMember of the Tier 0 group without an approved FIDO2 hardware security key. Fix before switching on the Part 3 policy.
NoAuthenticatorHas MFA, but no Microsoft Authenticator and no allowed passkey.
NotMfaCapableNo MFA method registered at all.
ReadyMicrosoft Authenticator or an allowed passkey registered.

Running the report

The script reads each user’s authentication methods directly, so the passkey AAGUIDs come back in the same call and it does not depend on Microsoft Entra ID P1 reporting. Phone numbers are masked to the last four digits, and nothing is written to the tenant. The allow lists match the Contoso profiles from Part 1:

$authenticator = 'de1e552d-db1d-4423-a619-566b625cdc84',   # Authenticator for Android
                 '90a3ccdf-635c-4729-a248-9b709135078f'    # Authenticator for iOS
$yubiKey5Nfc   = 'fa2b99dc-9e39-4257-8f92-4a30d23c4118',   # YubiKey 5 NFC 5.1
                 '2fc0579f-8113-47ea-b116-bb5a8db9202a',   # YubiKey 5 NFC 5.2/5.4
                 'a25342c0-3cdc-4414-8e46-f4807fca511c'    # YubiKey 5 NFC 5.7

.\Get-PasskeyReadiness.ps1 -AllowedAaguids ($authenticator + $yubiKey5Nfc) `
    -PrivilegedGroupId '<_PIM Tier 0 group object ID>' -PrivilegedAaguids $yubiKey5Nfc `
    -LicensedUsersOnly -EnabledUsersOnly

The output is a timestamped CSV and a summary by bucket. For scheduled runs, the script also supports certificate-based app authentication.

Requirements

  • Microsoft Graph PowerShell SDK
  • Get-PasskeyReadiness.ps1: User.Read.All, UserAuthenticationMethod.Read.All and GroupMember.Read.All
  • Sync-PasskeyReadinessGroups.ps1: Group.ReadWrite.All and GroupMember.ReadWrite.All

Turn the report into groups

A report is only useful if it drives action. Sync-PasskeyReadinessGroups.ps1 loads each bucket into its own security group. Run it with -WhatIf first to see what it would create:

.\Sync-PasskeyReadinessGroups.ps1 -CsvPath .\PasskeyReadiness_<timestamp>.csv -GroupPrefix 'Contoso-MFA-' -WhatIf
.\Sync-PasskeyReadinessGroups.ps1 -CsvPath .\PasskeyReadiness_<timestamp>.csv -GroupPrefix 'Contoso-MFA-' -RemoveStale
  • Contoso-MFA-SmsVoiceOnly - targeted first by the registration campaign and communications (Part 4)
  • Contoso-MFA-PasskeyOutsideAllow - contacted before the Default profile is retired, so they can register an allowed passkey
  • Contoso-MFA-PrivilegedNoHardwareKey - issued a YubiKey before the Part 3 policy is switched on
  • Contoso-MFA-NoAuthenticator - targeted by the Authenticator registration campaign

Re-run both scripts weekly. With -RemoveStale, users drop out of a group as soon as they fix their registration, so the groups always reflect who still needs help. When SmsVoiceOnly and PasskeyOutsideAllow reach zero, retiring the Default passkey profile is a non-event.

Next in the series

Part 3 returns to the question left open in Part 1: if profiles are a union, how do you make sure Tier 0 admins actually sign in with their YubiKey? The answer is a Conditional Access authentication strength.

References

  • Microsoft Learn - SMS and voice retirement in Microsoft Entra ID
  • GitHub - microsoft/entra-sms-voice-usage-analyzer
  • Microsoft Learn - userRegistrationDetails resource type (Microsoft Graph)
  • AdminDroid community - Export MFA Status Report using MS Graph: github.com/admindroid-community/powershell-scripts/blob/master/Export%20MFA%20Status%20Report%20using%20MS%20Graph/GetMFAStatusReport.ps1
  • GitHub - cloud-secure-ai/entra-passkey-readiness (Get-PasskeyReadiness.ps1 and Sync-PasskeyReadinessGroups.ps1) - github.com/cloud-secure-ai/entra-passkey-readiness

3 thoughts on “Replacing SMS and voice MFA, Part 2: Who is actually ready?”

  1. Pingback: Replacing SMS and voice MFA, Part 1: Lock down your passkey profiles first | Cloud-Secure.ai

  2. Pingback: Replacing SMS and voice MFA, Part 4: The people problem | Cloud-Secure.ai

  3. Pingback: Replacing SMS and voice MFA, Part 3: Enforcing the right passkey at sign-in | Cloud-Secure.ai

Leave a Reply