Skip to main content
KBY Technologies Logo
Graduate Track

Retiring Legacy Per-User MFA for Entra ID Authentication Policy

A staged runbook for clearing legacy per-user MFA and hardening Entra ID authentication methods without locking out users or losing break-glass access.

Elliot Ward 15 July 202612 min read Intermediate 55 min labIdentity and Access Management

Mission brief

What you will achieve

Complete a production-safe implementation and leave with verification and rollback evidence.

Practical steps

10 guided stages

Follow these in order as your working checklist.

Time to set aside

About 55 minutes

Includes configuration, validation and rollback checks.

Tutorial navigation
Production note
Validate permissions, test changes in a non-production scope, record the previous state, and retain a rollback path before applying operational steps.

Before you begin

  • Entra ID role-based access administration
  • Microsoft Graph PowerShell SDK usage
  • Conditional Access policy fundamentals

#Operational requirement

You will inherit tenants where Multi-Factor Authentication was switched on years before the unified Authentication Methods Policy existed. In those tenants, MFA enforcement still lives partly in a legacy per-user attribute called StrongAuthenticationRequirements, set historically through the old MSOnline module or the classic MFA portal. That attribute coexists uneasily with the modern Authentication Methods Policy blade in Entra ID (Protection > Authentication methods > Policies). The two systems do not always agree with each other, and Microsoft has been pushing tenants toward full retirement of per-user MFA for several release cycles.

A newly hired administrator is commonly handed exactly this task: migrate a tenant fully onto the unified policy, retire legacy per-user enforcement, and tighten which authentication methods are permitted, typically to kill off SMS and voice call as primary methods in favour of Microsoft Authenticator and passkeys. Treat this as a governance change, not a quick toggle.

Get this wrong and the failure mode is not subtle. If you disable legacy per-user MFA before every affected user has a registered method that satisfies the unified policy, users get stuck in an authentication loop or are blocked outright at sign-in. If you disable SMS or voice before confirming who still depends on it — shift workers without smartphones, service accounts with interactive fallback, executives travelling on roaming SIMs — you generate a help desk incident spike during a window when nobody on the identity team is watching sign-in logs closely enough to catch it fast. This sits squarely inside Identity and Access Management, and it is different from conditional access work: you are changing what proves identity, not who gets challenged.

#Prerequisites and permissions

Before you request a change window, confirm you actually hold the access this task requires. You need the Authentication Policy Administrator role in Entra ID, or Global Administrator if your tenant has not yet delegated this role separately — check with the identity lead before assuming the latter. You also need a Graph PowerShell session consented with the Policy.ReadWrite.AuthenticationMethod scope, and, for the legacy audit step, a working MSOnline module connection, which means the tenant must not yet have blocked that legacy module outright.

Confirm the following exist before you start, not during the migration:

RequirementWhy it matters
A documented break-glass account pair, excluded from every conditional access and authentication method policyEmergency access must survive every stage of this migration untouched
A change ticket with rollback trigger thresholds agreed in advanceYou will not have time to negotiate thresholds mid-incident
Read access to sign-in logs, ideally streamed to a Log Analytics workspaceVerification in this task is evidence-driven, not portal-driven
A current export of users still on legacy per-user MFAThis becomes your rollback map and your batch tracking sheet

If any of these are missing, raise it before the change window opens. Running this migration without a break-glass exclusion or a rollback trigger agreed with your manager is how a routine hardening task becomes a postmortem.

#Pre-migration assessment

Establish the current state on both sides of the legacy and modern boundary before touching anything. The legacy per-user MFA attribute is still only readable through the MSOnline module in most tenants, because the modern Microsoft Graph SDK has no direct property mapping for StrongAuthenticationRequirements. Microsoft has announced retirement of the MSOnline and AzureAD PowerShell modules, so treat this audit step as time-limited technical debt — if your tenant has already had these modules disabled, use the sign-in log approach described under Verification instead.

1Connect-MsolService
2Get-MsolUser -All | Where-Object { $_.StrongAuthenticationRequirements.Count -gt 0 } |
3  Select-Object DisplayName, UserPrincipalName, @{n='MFAState';e={$_.StrongAuthenticationRequirements.State}}

Run this and export to CSV before you change anything. That CSV is your rollback map — it tells you exactly which accounts had legacy enforcement and at what state, Enabled versus Enforced, which matters because Enforced accounts have already completed registration and are lower risk to migrate than accounts sitting in Enabled with no registration behind them.

Next, pull the current unified policy configuration to understand what is already permitted tenant-wide:

1Connect-MgGraph -Scopes "Policy.ReadWrite.AuthenticationMethod"
2Get-MgPolicyAuthenticationMethodPolicy | Select-Object Id, Description
3Get-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration

Look for the current State of each method configuration ID — Sms, Voice, MicrosoftAuthenticator, FIDO2, TemporaryAccessPass, and X509Certificate at minimum. Most legacy tenants have MicrosoftAuthenticator enabled but Sms and Voice still enabled tenant-wide from the original rollout, with no target group restriction at all. That gap is the debt you are here to clean up.

#Staged migration procedure

Do this in stages across a change window, not as a single cutover. Attempting a big-bang migration on a tenant with several thousand users is how you end up explaining a Sev1 to the CIO on a Friday afternoon.

Step 1 — target Microsoft Authenticator registration at scope, not tenant-wide, first. Confirm the method configuration includes the correct include and exclude groups before widening it further:

1Update-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration `
2  -AuthenticationMethodConfigurationId MicrosoftAuthenticator `
3  -BodyParameter @{ state = "enabled"; includeTargets = @(@{ targetType = "group"; id = "<group-object-id>" }) }

Step 2 — run a registration campaign, do not silently require it. Enable the built-in nudge so users register Authenticator ahead of enforcement, with a defined snooze window so nobody is locked out mid-task:

1Update-MgPolicyAuthenticationMethodPolicy -BodyParameter @{
2  registrationEnforcement = @{
3    authenticationMethodsRegistrationCampaign = @{
4      state = "enabled"
5      snoozeDurationInDays = 7
6      excludeTargets = @(@{ id = "<breakglass-group-id>"; targetType = "group" })
7    }
8  }
9}

Exclude your break-glass accounts explicitly at this step, not later. Those accounts must never be subject to a registration nudge or a method restriction — that is exactly how you lose emergency access during an outage you are trying to fix.

Step 3 — confirm coverage before removing anything legacy. Do not proceed past this gate until registration coverage on the target population sits at an acceptable threshold, typically 95 percent or higher, verified through the Authentication Methods Activity report or the Graph reports endpoint. Anyone below the threshold gets a manual follow-up call, not a forced cutover.

Step 4 — clear legacy per-user enforcement in batches. Do not run this against the whole tenant in one pass. Chunk it, and reconnect the MSOnline session between chunks, because long-running loops against thousands of accounts will hit token expiry mid-script and leave you with a partially migrated population and no clear record of where it stopped.

1$users = Import-Csv .legacy-mfa-users.csv | Select-Object -First 200
2Connect-MsolService
3foreach ($u in $users) {
4  Set-MsolUser -UserPrincipalName $u.UserPrincipalName -StrongAuthenticationRequirements @()
5}

Log every batch — user list, timestamp, operator — into the change ticket. This is the audit trail your governance reviewer will ask for six months later, and it is far easier to keep as you go than to reconstruct afterwards.

Step 5 — restrict SMS and voice only after confirming zero dependency. Query sign-in logs for the authentication method actually used over the preceding 30 days before touching this configuration, then narrow the include scope rather than disabling outright on day one:

1Update-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration `
2  -AuthenticationMethodConfigurationId Sms `
3  -BodyParameter @{ state = "enabled"; includeTargets = @(@{ targetType = "group"; id = "<legacy-fallback-group-id>" }) }

Only move to full disablement of Sms and Voice once that fallback group is empty, meaning nobody still depends on it. On any tenant with real headcount this is a multi-week task, not an afternoon job — plan the change record accordingly and resist pressure to compress it.

#Monitoring during rollout

Do not treat this migration as fire-and-forget between stages. Put a standing watch on help desk ticket volume tagged with an MFA or sign-in category, and agree with the service desk lead beforehand what a normal daily baseline looks like so a genuine spike is obvious rather than lost in noise. Ask for hourly rollups during the first week of each stage, not daily summaries — daily summaries hide the exact hour a change went wrong.

If your tenant streams to Log Analytics, build a simple saved query that counts authentication failures by method over rolling 4-hour windows and pin it to a dashboard for the duration of the project. Reviewing this once at the end of each stage, before widening scope further, is the single habit that prevents a small localised problem becoming a tenant-wide one.

#Verification

Verification here is log-driven, not policy-screen-driven — a green tick in the portal tells you the policy is configured, it does not tell you it is safe.

Confirm the legacy attribute is genuinely clear across your migrated population:

1Get-MsolUser -All | Where-Object { $_.StrongAuthenticationRequirements.Count -gt 0 } | Measure-Object

That count should trend toward zero across your batches, excluding any accounts you have deliberately deferred with a documented reason in the ticket.

Cross-check actual sign-in behaviour with a Log Analytics KQL query against the SigninLogs table if your tenant streams to a workspace:

1SigninLogs
2| where TimeGenerated > ago(24h)
3| where AuthenticationRequirement == "multiFactorAuthentication"
4| summarize count() by AuthenticationMethodsUsed = tostring(AuthenticationDetails[0].authenticationMethod), ResultType
5| order by count_ desc

Watch for a rising count against a specific method following a policy change — that is your earliest signal something has broken for a segment of users, well before help desk ticket volume tells you the same thing an hour later. Keep a screenshot or exported result of this query at the end of each stage as evidence for the change record.

#Failure Modes

Most incidents in this migration trace back to one of a small set of causes. Recognise the symptom quickly and you save yourself hours of guesswork.

SymptomLikely causeFix
Users blocked at sign-in during the registration campaign windowSnooze limit exhausted with no method registeredExtend snoozeDurationInDays or add the affected user to excludeTargets temporarily
Users caught in a repeating MFA prompt after migrationLegacy StrongAuthenticationRequirements still set to Enforced while the unified policy also requires a method the user has not registeredClear the legacy attribute for that user and confirm a compliant method is registered before re-testing
Break-glass account fails sign-in during a later SMS restriction passBreak-glass account was not excluded from the registration campaign or method scopeAdd break-glass accounts to a dedicated exclusion group before any method configuration change, permanently
Bulk Set-MsolUser script errors out partway through a batchMSOnline session token expired mid-loop against a large user setChunk batches to 200 or fewer, reconnect Connect-MsolService between chunks, log each completed batch
Update-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration is rejectedSigned-in account lacks the Authentication Policy Administrator role or the Policy.ReadWrite.AuthenticationMethod scope was not consentedAssign the correct Entra role and re-consent the Graph PowerShell app with the required scope
Shift workers without smartphones locked out after SMS disablementFallback population identified from a stale sign-in log window rather than a current 30-day windowRe-widen the SMS include scope for that group and re-run the dependency check over a longer window before retrying

A quieter trap worth naming separately: assuming a low ticket count during Step 3 means coverage is fine. Silence can also mean users have simply stopped trying to sign in through the affected channel and are working around it, which hides the real dependency until the day you remove the fallback entirely.

#Change control

Every stage above should map to its own change record, not one giant record covering the whole migration. Reviewers approving a single narrow change — widen Authenticator registration to group X — can reason about the blast radius. Reviewers approving retire all legacy MFA cannot, and they will either rubber-stamp it without real scrutiny or block it outright out of caution. Neither outcome helps you.

Each change record should state the current stage, the population affected, the rollback trigger agreed for that stage specifically, and the evidence you will attach on completion, such as the exported Get-MsolUser count or the KQL query result. Keep the master project ticket as a parent record linking each staged change, so the audit trail reads as a coherent migration rather than a scattered set of unrelated tweaks.

#Rollback

Rollback here has two layers, and you need to know which one you are dealing with within minutes of the first help desk escalation.

If the problem is the registration campaign locking users out before they have registered a method, disable enforcement immediately rather than trying to extend snooze windows under pressure:

1Update-MgPolicyAuthenticationMethodPolicy -BodyParameter @{
2  registrationEnforcement = @{
3    authenticationMethodsRegistrationCampaign = @{ state = "disabled" }
4  }
5}

If the problem is a method restriction — SMS or Voice disabled for a group that turned out to still depend on it — re-widen the include scope for that specific group rather than reverting the whole policy back to tenant-wide open access, which would undo weeks of legitimate tightening for everyone else:

1Update-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration `
2  -AuthenticationMethodConfigurationId Sms `
3  -BodyParameter @{ state = "enabled"; includeTargets = @(@{ targetType = "group"; id = "<affected-group-id>" }) }

Do not reintroduce per-user StrongAuthenticationRequirements as a rollback mechanism. Reversing forward into legacy enforcement recreates the exact technical debt this migration exists to remove, and it will need doing again later by someone, quite possibly you. Define your rollback trigger in the change record before you start — for example, more than twenty MFA-related help desk tickets within a single hour halts the migration and reverts only the most recent stage, not the whole project. Require change approval before re-attempting the forward step once the cause is fixed and evidence is attached.

#Operational Summary

This is a staged, log-verified migration, not a policy toggle you flip once and walk away from. Audit the legacy attribute first while the MSOnline module still exists to read it, run the registration campaign with break-glass accounts explicitly excluded from day one, and gate every forward step on measured coverage rather than a deadline someone set in a planning meeting. Treat SMS and voice restriction as the last and most cautious stage, because it is the one most likely to strand a real population of users who have no obvious substitute method.

Keep monitoring live throughout, not just at the end of each stage, and keep your rollback scoped to the specific method or group that failed rather than the whole policy. Never roll back into the legacy per-user mechanism you are retiring — that is progress lost, not progress paused. Done properly, the tenant ends up with a single, auditable authentication policy, a documented batch trail, and a change record a reviewer can actually follow twelve months later. Done carelessly, it ends up as the incident the service desk still brings up in retrospectives.

Reader Support: KBY Technologies is an independent engineering editorial. We may earn a commission from affiliate links when you purchase infrastructure, software, or tools through our recommendations. This helps fund our research and labs.

Advanced suggested reading

Ready to go deeper?

These articles come from KBY’s senior engineering editorial. Expect denser architecture, fewer introductory explanations and deeper production trade-offs.

Leaving Graduate Track
Reader Interaction

Comments

Add a thoughtful note on Retiring Legacy Per-User MFA for Entra ID Authentication Policy. Comments are checked for spam and held for moderation before appearing.

Loading comments...

Loading anti-spam check...

Continue the track

Related Graduate tutorials

View learning path