Azure mandatory MFA now blocks CLI, PowerShell and Terraform changes: moving service accounts to workload identities
Azure is rolling out a block on create, update and delete calls from user accounts that skipped MFA, and that includes the user accounts your scripts and pipelines sign in with. Here is how to find them, what to move them to, and the order we'd do it in.
part 4 of 4 · Cloud calls you'll have to makethe ideaScripts that sign in as people now fail on writes; give them their own identity.
after reading, you can- Spot the reason a deploy that ran fine last month now stops at its first write
- Pull a list of every script still signing in as a person from the sign-in logs
- Move each one to a managed identity or a federated workload identity
Azure is turning on a rule, tenant by tenant, that wants MFA before a user account can create, change or delete anything through the CLI, PowerShell, Terraform, the SDKs or the REST API. If any script or pipeline still signs in as a person with a password, it will fail the next time it tries to deploy. Move it to a managed identity or a federated workload identity. This guide shows how to find those accounts, which option fits each one, and what tends to go wrong during the move.
Azure now asks people for a second proof before they change anything. Your scripts can't give one
Microsoft runs a front desk for every change made to your Azure estate, and since late 2025 it has been checking that each person making a change has passed a second sign-in step, such as a phone prompt or a security key. That's fine for people. The trouble is the scripts and deployment pipelines that were set up years ago to sign in with an ordinary employee-style account and a stored password. They can't tap a phone. When they try to build or change something, Azure says no, and the deployment stops halfway. The fix is to give each script its own machine identity, which Azure was always built to accept without a phone prompt.
Picture a plant room door that now wants a fingerprint as well as a key card. Staff walk straight in. The cleaning robot has been using a retired employee's card, and it's stuck, because it has no finger. Give it its own robot pass. The door has always accepted those.
- Teams deploying to Azure with scripts
- Terraform and Bicep users
- Azure DevOps and GitHub Actions owners
- Anyone with a shared admin login
- Identity and security teams
- IT leads at smaller companies
Why it matters to you
Microsoft rolled the rule out in two phases. Phase 1 covered the Azure portal and the admin centres. Phase 2 covers everything else that manages Azure: the Azure CLI, Azure PowerShell, the Azure mobile app, infrastructure-as-code tools, the SDKs and the REST API. Microsoft rolls it out tenant by tenant, and the latest date anyone could postpone to has already passed. There's no opt-out, and test tenants aren't exempt.
The business risk is mostly an outage you didn't plan. A nightly job that scales a cluster, a release pipeline, a cost script that deletes old snapshots: each one keeps working until the day it tries to write, and then it stops. Reading still works, so dashboards and inventory scripts look healthy while the deploys behind them fail. Often the first sign is a failed release.
There's a security side too. A user account with a stored password and no MFA is exactly the kind of credential attackers go looking for in old repos and build logs. Microsoft says its research shows MFA blocks more than 99.2% of account compromise attacks, and it says student, break-glass, admin and excluded accounts all carry the same risk if compromised, so it makes no exceptions for them. Moving them off passwords closes a gap you probably already had.
-
01 Pipeline signs in as a user
Sign-in happens the old way: az login or Connect-AzAccount, a username, and a password pulled from the pipeline's variables.
-
02 Token arrives without MFA
Entra ID hands back a token, but it says only one factor was used, because a script can't answer a phone prompt.
-
03 Reads go through
Listing resource groups or reading tags still works, so earlier steps in the job pass.
-
04 First write hits ARM
Sooner or later the job has to change something, say create a VM or delete a disk. That request goes to management.azure.com, and Azure checks for MFA there.
-
05 ARM refuses the change
Azure returns RequestDisallowedByPolicy, saying the user must authenticate with MFA, and the deploy stops halfway.
For engineers: Azure checks this on the ARM side. Upgrading the CLI gives you a clearer message and the same block. Microsoft enforces Phase 2 by deploying the same built-in Azure Policy definitions you can assign yourself, so you can run the block in your own test window first. Microsoft Graph calls are generally out of scope, because only requests to management.azure.com are checked.
What changed, and what didn't
Phase 2 at a glance
Any user account now has to pass MFA before it can create, update or delete resources through these tools. Reading is still fine, which is why the failures look random at first.
Admins, students, guests, break-glass accounts and users with Conditional Access exclusions all count. Exclusions in your own policies don't apply to this rule.
Azure CLI 2.76 and Az PowerShell 14.3 (Az.Accounts 5.2.0) print the policy reason and a ready-made sign-in command with a claims challenge.
MSAL has deprecated its public-client username-and-password APIs, and Azure Identity has deprecated UsernamePasswordCredential, because that flow can't do MFA.
Global Administrators could push Phase 2 back, but no later than 1 July 2026. After enforcement starts, the only route is a support request to lift it for a while.
Two built-in Azure Policy definitions, still in preview, block writes from users who skipped MFA. Assign them in Audit mode first and you'll see what would fail before it does.
The change that bites hardest is the password flow. Its proper name is the Resource Owner Password Credentials grant, ROPCdefinitionResource Owner Password Credentials: a sign-in where a script sends a username and password straight to Entra ID. It can never pass MFA, so its writes to Azure now fail, and the sign-in fails too where Entra demands MFA. for short, and it's what runs whenever a script hands Entra ID a username and password directly. ROPC can't do MFA at all. Under Phase 2 alone, the password sign-in still works and the first write fails at ARM. If the account is also under an Entra MFA requirement, such as Conditional Access, security defaults or per-user MFA, the sign-in itself fails. In PowerShell that shows up as "UsernamePasswordCredential authentication failed" with a 400 BadRequest.
Watch for the quiet version of the same thing in application code. DefaultAzureCredential and EnvironmentCredential will try a username and password if AZURE_USERNAME and AZURE_PASSWORD are set in the environment. Check your build agents for those variables. They're easy to forget after an old setup.
| Client or identity | App ID (for log queries) | Phase 2 applies? | What you'll notice |
|---|---|---|---|
| Azure CLI, user account | 04b07795-8ddb-461a-bbee-02f9e1bf7b46 | Yes, for create, update, delete | Usually RequestDisallowedByPolicy; older CLIs may say enroll in MFA |
| Azure PowerShell, user account | 1950a258-227b-4e31-a9cf-717495945fc2 | Yes, for create, update, delete | Clear policy error on Az 14.3+, vaguer before |
| Terraform, Bicep, Ansible as a user | Uses the CLI or PowerShell IDs | Yes | Plan works, apply fails |
| SDK or REST call as a user | Not applicable | Yes, on management.azure.com | Policy error on writes |
| Service principal | Your app's ID | No | No change |
| Managed identity | Azure-managed | No | No change |
| Microsoft Graph scripts | Varies | Generally no | No change from this rule |
| Entra Connect or Cloud Sync account | Not applicable | No | Sync keeps running |
| Azure Government, sovereign clouds | Varies | Not enforced today | No change for now |
How we got here
- Oct 2024Phase 1 starts
The Azure portal and the Entra and Intune admin centres start asking for MFA. Scripts aren't touched yet.
- Feb 2025Microsoft 365 admin center
Enforcement starts rolling out there too.
- Mar 2025Portal done everywhere
Microsoft says the portal rule had reached every Azure tenant. Scripts and the CLI were still untouched.
- 5 Sep 2025Phase 2 announced
The Azure blog post set the start for October. It asked everyone to move to Azure CLI 2.76 and Az PowerShell 14.3 first, so the errors would at least make sense.
- 30 Sep 2025Phase 1 delay limit
Last date a tenant could push Phase 1 back to.
- 1 Oct 2025Phase 2 starts
Writes from the CLI, PowerShell, IaC tools, SDKs and REST start needing MFA. Microsoft turns it on tenant by tenant.
- 1 Jul 2026Phase 2 delay limit
Nobody could pick a later start than this. If your tenant postponed, that date has come and gone.
- 1 Jul 2027Azure DevOps issuer retires
Public-cloud federated connections that use a single-tenant app or a managed identity and are still on the old Azure DevOps issuer must move to the Entra issuer.
Your options, side by side
Each account that runs automation needs one decision. Most of them land on a managed identity or a federated identity, and a few stay as people who sign in properly. Pick by where the job runs, not by what's quickest to type.
| Option | Fits when | Secret to manage? | Effort | Watch out for |
|---|---|---|---|---|
| Managed identity | The job runs on Azure: a VM, App Service, a function app, a container or AKS | None | Low | Only works on Azure compute |
| Workload identity federation | GitHub Actions, Azure DevOps, Kubernetes, AWS, Google Cloud or another OIDC issuer | None | Low to medium | Subject must match exactly; 20 credentials per identity |
| Service principal with a certificate | Runs somewhere that can't federate, such as an on-premises scheduler | Certificate | Medium | Expiry dates and where the key lives |
| Service principal with a secret | Last resort for tools that support nothing else | Client secret | Low | Leaks, expiry, rotation by hand |
| Keep the user, add real MFA | A person runs the script by hand at a desk | None beyond MFA | Low | Only for interactive use |
| Ask support to lift enforcement | Something critical broke and needs days, not weeks | Unchanged | Low | Temporary, needs a Global Administrator |
- Its password lives in a variable group or a script, so anyone who can read those can use it
- Blocked on every write unless it passes MFA
- Uses a paid user licence
- Often shared across several jobs
- Owner left years ago, nobody wants to touch it
- No password; short-lived tokens only
- Not in scope for mandatory MFA
- Scoped to one job, so a leak only reaches that job's resource group
- Shows up under service principal sign-ins
- Workload ID Premium lets you block it by location or risk. Managed identities aren't covered by that.
One case comes up a lot. Some teams use a single user account that manages on-premises Active Directory and Azure from the same script, and they say a service principal can't reach AD. On Microsoft's Q&A forum, a Microsoft employee answered that the rule only bites when that account signs in to one of the listed Azure tools. It's usually simplest to split the script, so the AD half keeps its own account and the Azure half moves to a managed identity on the same server.
- Every user account that touched the Azure CLI or PowerShell in the past 30 days is on a list, with an owner who'll pick up the phone when it breaks.
- Next to each account you've written down what actually uses it. A person, a job on Azure, a pipeline or a script on a server elsewhere each get a different fix.
- Break-glass accounts have a passkey or certificate sign-in, and someone has actually tried it. Find out now, not during an outage.
- Your CLI is at 2.76 or later and Az PowerShell at 14.3 or later on build agents.
- No build agent has AZURE_USERNAME or AZURE_PASSWORD set. Those two make the SDK try a password sign-in.
- You've assigned the built-in MFA policies in Audit mode and read the Activity Log for a week.
- Someone can roll back a pipeline change in minutes if a new identity is missing a role.
Find the accounts before Azure finds them for you
Start with the sign-in logs. If you send them to Log Analytics, the query below lists user accounts that signed in to the Azure CLI or PowerShell without MFA. The AuthenticationProtocol column is the useful one: a value of ropc means a script is handing over a password directly, and that sign-in can never satisfy MFA.
// example: user sign-ins without MFA union SigninLogs, AADNonInteractiveUserSignInLogs | where TimeGenerated > ago(30d) | where AppId in ( "04b07795-8ddb-461a-bbee-02f9e1bf7b46", "1950a258-227b-4e31-a9cf-717495945fc2") | where AuthenticationRequirement == "singleFactorAuthentication" | summarize SignIns = count(), LastSeen = max(TimeGenerated) by UserPrincipalName, AppDisplayName, AuthenticationProtocol | order by SignIns desc // example output svc-deploy Azure CLI ropc 142 ops-backup Azure PS none 30
Rehearse the block before it happens
- 01Run the log query and build the list
Export the result and add three columns: owner, where the job runs, and the target option from the table above. Anything without an owner goes to the top of the list, because nobody will notice when it breaks.
- 02Assign the built-in policies in Audit mode
In Azure Policy, assign "Users must authenticate with multi-factor authentication to create or update resources" and the matching delete policy. Leave the effect on Audit or AuditAction. Use resource selectors to start with a few low-risk regions.
- 03Read the Activity Log, not the compliance page
Don't wait for the compliance page to light up. Microsoft's tutorial says it shows 0 resources for this policy, because it judges requests. The hits are in the Activity Log, one event per write by a user who skipped MFA.
- 04Add a report-only Conditional Access policy
If you have Entra ID P1 or P2, create a policy for all users that targets Microsoft Admin Portals and the Windows Azure Service Management API, requires MFA, and stays in report-only. The Conditional Access insights workbook then shows who would be prompted.
- 05Check your tenant's Phase 2 page
Have a Global Administrator check the Phase 2 page in the Azure portal. A banner there says whether enforcement has started, so you know which problem you have.
Moving each account to a workload identity
Before: every job borrows one person's login
One user account with a stored password signs in for several jobs. Every write now needs MFA it can't give, so deploys fail at the first change.
After: one identity per job, no passwords
GitHub and Azure DevOps swap their own tokens through federation, the VM uses its managed identity, and each identity only has the role its job needs.
The order we'd do it in
- 01Split shared accounts first
If one user account serves five jobs, give each job its own identity. It takes longer up front, but when something breaks later you'll know which job did it, and you can scope each role tightly.
- 02Jobs on Azure get a managed identity
Turn on a managed identity for the VM or function app, grant it the role it needs on the right scope, and swap the sign-in line for az login --identity, or Connect-AzAccount -Identity in PowerShell. For a user-assigned identity, add --client-id in the CLI or -AccountId in PowerShell.
- 03GitHub Actions gets federation
Create an app registration or a user-assigned managed identity, add a federated credential for the repo's branch, tag, environment or pull requests, and use azure/login@v2 with client-id, tenant-id and subscription-id. The job needs permissions: id-token: write.
- 04Azure DevOps gets a federated service connection
New Azure Resource Manager connections can use workload identity federation, which Azure DevOps sets up for you. If you have old connections on the Azure DevOps issuer, convert them to the Entra issuer now, before that issuer retires.
- 05Terraform reads the federated token
The azurerm provider supports OIDC from version 3.7.0. Set use_oidc = true, or ARM_USE_OIDC=true, plus the client, tenant and subscription IDs. In Azure DevOps, also set ARM_ADO_PIPELINE_SERVICE_CONNECTION_ID. Check the azurerm backend separately, because it has its own auth settings.
- 06Everything else gets a certificate
For an on-premises scheduler that can't federate, use a service principal with a certificate kept in a vault or the machine's certificate store. Treat a client secret as a stopgap with an expiry date and a named owner.
- 07Retire the old account
Once every job runs on its new identity, disable the user account and wait a couple of weeks for anything you missed. Then delete it and give the licence back. Microsoft suggests moving any Conditional Access you had on it to a workload identities licence.
# example: azurerm with a federated token provider "azurerm" { features {} use_oidc = true client_id = var.client_id tenant_id = var.tenant_id subscription_id = var.subscription_id } # Azure DevOps pipelines also set: ARM_USE_OIDC=true ARM_ADO_PIPELINE_SERVICE_CONNECTION_ID=<id>
What to do now, by role
Who does what this week
- Ask IT whether any deploy or scheduled Azure job failed with an MFA error since enforcement started.
- Ask whether any Azure job still signs in as a person, and who owns each one.
- Approve the time to move them before the next release, not after it fails.
- If you use an outside provider, ask them how their tools sign in to your tenant.
- Run the sign-in log query and list every single-factor CLI and PowerShell user.
- Upgrade build agents to Azure CLI 2.76+ and Az PowerShell 14.3+ so errors are readable.
- Move pipelines to federated service connections and Azure-hosted jobs to managed identities.
- Delete AZURE_USERNAME and AZURE_PASSWORD from agents and variable groups. Code will quietly try them otherwise.
- Grep the code for AcquireTokenByUsernamePassword and UsernamePasswordCredential. Those calls can't pass MFA, so their writes to Azure now fail.
- Replace them with managed identity, a federated credential or a certificate credential.
- Don't switch a user-facing app to app-only access; use interactive sign-in for people.
- Give break-glass accounts a passkey or certificate-based sign-in and test it.
- Assign the built-in MFA policies in Audit mode and review the Activity Log weekly.
- Put an owner and a review date on every new workload identity.
- Look at Conditional Access for workload identities. It can block a pipeline identity that signs in from an IP range you don't use.
Harden it so the next rule change is a non-event
- 01One job, one identity, one scope
Grant each identity a role on the resource group it actually deploys to, not Contributor on the subscription. When a pipeline is compromised, the blast radius is that one resource group.
- 02Tie federated credentials to an environment
For GitHub, point the production credential at a deployment environment, not every branch, and put required reviewers on that environment. Then a workflow run from some other branch can't get a production token.
- 03Self-enforce with Azure Policy
Once Audit mode is clean, override the effect to Deny or DenyAction region by region. Then a new user-based script fails in your own test window instead of on a Friday night.
- 04Watch service principal sign-ins
Service principals show up under their own sign-in log. Alert on a workload identity signing in from an IP range your CI provider doesn't use. With Workload Identities Premium you can block that outright.
- 05Keep tools current on agents
Pin build agents to a recent CLI and PowerShell version and update them on a schedule. Older clients hide the real reason behind vague token errors, which turns a five-minute fix into an afternoon.
If it goes wrong
When a deploy fails on MFA, or an old account looks misused
Most failures here are outages, not attacks. But a password-based account with no MFA is also a credential worth checking, so the playbook covers both.
- 01Containfirst hour
- Stop retries on the failing pipeline so it doesn't leave half-built resources behind.
- Write down which step failed and what it had already created. You'll need that list for the clean-up.
- If the account shows sign-ins from unknown IPs, reset its password and revoke its sessions.
- 02Restore servicesame day
- If a person runs the script by hand, have them sign in again with MFA. The CLI prints the exact command, claims challenge included.
- For an automated job, switch it to a managed or federated identity with the same role.
- If neither is possible quickly, a Global Administrator can ask Microsoft support to lift enforcement for a while.
- 03Investigatefirst day
- Pull the account's sign-in logs for the last 30 days, interactive and non-interactive both. Unknown IPs or odd hours are what you're looking for.
- Check the Activity Log for writes made by the account that nobody can explain.
- Search repos and pipeline logs for the old password. If it turns up, treat it as exposed and check who could read it.
- 04Clean upthis week
- Remove half-created resources or re-run the deploy cleanly on the new identity.
- Rotate any secret that sat next to the old password.
- Disable the old user account, then delete it after a quiet period.
- 05Report and learnthis month
- If you found misuse, run it as a security incident. Your contracts and regulators decide who has to hear about it.
- Add the job to the identity inventory with an owner and a review date. Then it can't quietly go stale again.
- Turn the Azure Policy effect to Deny so the next one fails in testing.
What this looks like in practice
Three typical estates
- the setup
- A three-person IT team runs a PowerShell script that signs in with Connect-AzAccount -Credential using a shared admin account their tenant requires MFA for, then rebuilds a test VM every Monday.
- what happens
- The sign-in fails with UsernamePasswordCredential authentication failed, because the password flow can't do MFA. Nobody notices until someone needs the test VM.
- what to do
- Have the person who runs it sign in interactively with MFA, or run it on an Azure VM with a managed identity scoped to that one resource group. Retire the shared admin login.
- the setup
- Terraform runs in Azure DevOps with ARM_CLIENT_ID pointing at a service principal, but one older pipeline signs in as a user through az login with a stored password.
- what happens
- terraform plan passes because it only reads. terraform apply fails on the first resource change with RequestDisallowedByPolicy, leaving a partly applied state.
- what to do
- Move that pipeline to a federated service connection, set ARM_USE_OIDC and the service connection ID, then re-run apply. Check the backend's auth settings too.
- the setup
- Hundreds of GitHub and Azure DevOps pipelines, a mix of user accounts, secret-based service principals and early federated connections on the Azure DevOps issuer.
- what happens
- User-based pipelines fail once the tenant is enforced. Teams move fast and hit the 20-credential limit on a shared identity and silent subject mismatches.
- what to do
- Inventory from the sign-in logs, give each repo or environment its own identity, convert old connections to the Entra issuer, and switch the Azure Policy effect to Deny once Audit is clean.
How Cohash helps
We start from your sign-in logs and go through the list with you, account by account. Each one ends up as a person with MFA, a managed identity, a federated identity or a certificate. We move the pipelines in an order that keeps releases going, and every new identity gets an owner and a review date. Our Microsoft Entra ID page covers the wider identity work, the Terraform page covers pipelines, and you can bring in engineers for the migration itself. If you want a second opinion first, ask for an assessment.
What to take from this
- 01
Azure is rolling out a block on writes from user accounts that skipped MFA, whatever tool they use, and there's no opt-out.
- 02
Managed identities and service principals aren't affected, so that's where every script should end up.
- 03
Find the accounts in the sign-in logs first; ropc in the protocol column means a sign-in that can never pass MFA.
- 04
Rehearse with the built-in Azure Policy in Audit mode, then switch it to Deny yourself.
- 05
Give each job its own identity. Watch the subject match and the 20-credential limit, because both fail quietly.
01Does mandatory MFA apply to service principals?
No. Microsoft says managed identities and service principals aren't affected by either phase. Only user accounts are. That's why the advice for every script and pipeline is the same: give it a workload identity and stop it signing in as a person.
02Why did az login with a password stop working?
Signing in with a username and password uses the ROPC flow, and ROPC can't do MFA. Under Phase 2 the sign-in may still work, and then every write fails. If your tenant also requires MFA for that account, through Conditional Access or security defaults, the sign-in fails outright. Either way, switch the script to a managed identity, a federated credential or a service principal with a certificate.
03Can we still postpone Phase 2?
Not any more. A Global Administrator could pick a later start, but nothing after 1 July 2026. If enforcement has begun and something important broke, a Global Administrator can raise a support request to lift it for a short time while you fix the account.
04Are Microsoft Graph scripts affected?
Generally no. Microsoft says only requests to management.azure.com are checked for Phase 2. A script that only calls Graph generally keeps working as far as this rule goes, though your own Conditional Access policies may still ask for MFA.
05Will it break Entra Connect sync?
No. Microsoft says the sync account behind Entra Connect and Cloud Sync isn't touched by mandatory MFA, so directory sync keeps running. Leave it alone for this change.
06Do test tenants and break-glass accounts need MFA too?
Yes to both. Microsoft makes no exception for test tenants, and break-glass accounts have to pass MFA as well. Give break-glass accounts a passkey security key or certificate-based sign-in, and test that it works before you need it.
07Is Azure Government affected?
Not today. Microsoft enforces mandatory MFA only in the public Azure cloud, and says it doesn't currently enforce it in Azure for US Government or the other sovereign clouds. Plan as if that could change.
08We use another MFA provider. Does that count?
It can. External MFA integrated with Entra ID satisfies the rule, and a federated provider such as AD FS works if it sends the MFA claim to Entra ID. The old Conditional Access custom controls preview doesn't count, so move off it.
Three questions, about a minute
Pick an answer and we'll tell you if it's right, and why it is or isn't. The score stays in this browser.
-
Q1A pipeline's terraform plan passes but apply fails with MFA is required. What's going on?
show the answer
B. It signs in as a user, and Azure only checks MFA when something gets written Phase 2 lets reads through and checks MFA when a user account creates, updates or deletes something. Plan only reads, apply writes, so the user sign-in is the problem.
-
Q2A nightly job on an Azure VM still signs in with a saved password. What would you switch it to?
show the answer
A. A managed identity on the VM, signed in with az login --identity The job already runs on Azure, so a managed identity removes the password entirely, and workload identities aren't in scope for mandatory MFA. Exclusions don't apply to this rule.
-
Q3You add a federated credential for a GitHub repo, and sign-in fails for a few minutes with AADSTS70021. What's the likely cause?
show the answer
C. Microsoft's sign-in servers haven't all picked up the new credential yet A new federated credential takes a little while to reach every node, and requests in that window fail with AADSTS70021. If it never clears, check the subject match next.