Phase 2 of Azure mandatory multifactor authentication requires every user account that creates, updates or deletes resources through Azure Resource Manager to sign in with MFA, whether the call comes from Azure CLI, Azure PowerShell, Terraform, an SDK or the REST API. Interactive users fix it by signing in with MFA, ideally enforced at sign-in by Conditional Access. Unattended scripts and pipelines that use a user account must move to a workload identity: a managed identity, a service principal with a certificate, or workload identity federation (OIDC), none of which are subject to the enforcement.
Who this is for and what you will have at the end
This guide is for Azure administrators and platform engineers whose scripts, scheduled tasks or CI/CD pipelines started failing with RequestDisallowedByPolicy or "Interactive authentication is needed", or who want to find those scripts before they fail.
At the end you will have:
- A clear picture of what phase 2 covers and what it doesn't.
- A list of user accounts that run Azure CLI or Azure PowerShell without MFA, from the sign-in logs.
- Replacement sign-in code for Azure CLI, Azure PowerShell, Terraform and GitHub Actions using workload identities.
- An optional Azure Policy assignment that audits or blocks non-MFA changes on your own schedule.
What phase 2 enforces
Phase 1 covered the Azure portal, Microsoft Entra admin center and Intune admin center. Phase 2 started gradually on 1 October 2025 and covers the clients most automation uses:
| Client | App ID | Phase 2 scope |
|---|---|---|
| Azure CLI | 04b07795-8ddb-461a-bbee-02f9e1bf7b46 | Create, update, delete |
| Azure PowerShell | 1950a258-227b-4e31-a9cf-717495945fc2 | Create, update, delete |
| Azure mobile app | 0c1307d4-29d6-4389-a11c-5cbe7f65d7fa | Create, update, delete |
| Infrastructure as code tools | Use the Azure CLI or Azure PowerShell IDs | Create, update, delete |
| REST API (control plane) and Azure SDK | Not applicable | Create, update, delete |
Points that decide whether a script breaks:
- Enforcement is server-side. Microsoft enforces it on Azure Resource Manager, so any request that targets
https://management.azure.comis in scope, whatever client sends it. - Reads are not affected. A script that only lists or reads resources keeps working.
- Workload identities are exempt. Managed identities and service principals aren't affected by either phase.
- User accounts used as service accounts are not exempt. Accounts synchronized from Active Directory, break-glass accounts and accounts excluded from your Conditional Access policies all need MFA. Exclusions in your own policies don't apply to this system enforcement.
- Microsoft Graph is generally out of scope. Only requests to Azure Resource Manager are enforced.
- Public cloud only. Azure for US Government and other sovereign clouds aren't currently enforced.
- No opt-out. Self-service postponement allowed a start date up to 1 July 2026. After enforcement begins, a Global Administrator can ask Microsoft Help and Support to lift it temporarily.
To confirm the state of your tenant, sign in to the Azure portal as a Global Administrator, browse to https://aka.ms/postponePhase2MFA and check the banner on the Multifactor authentication (Phase 2) page.
Prerequisites
- Reports Reader or Security Reader to read sign-in logs, and a Log Analytics workspace receiving Microsoft Entra sign-in logs if you want to use KQL.
- Rights to create workload identities and assign Azure roles on the target scope, for example Owner, or Contributor plus User Access Administrator. Creating federated credentials on an app registration needs the app owner or Application Administrator, Cloud Application Administrator, Global Administrator or Hybrid Identity Administrator.
- Azure CLI 2.76 or later and Azure PowerShell 14.3 or later (Az.Accounts 5.2.0 or later). Older versions return less useful errors.
Step 1: Find the user accounts that run automation
Microsoft recommends querying sign-ins by the application IDs in the table above. If your sign-in logs flow to Log Analytics, this query lists user accounts that sign in to Azure CLI or Azure PowerShell, how often they do so with single-factor authentication, and whether they use the password-based ROPC flow, which can never satisfy MFA:
SigninLogs
| where TimeGenerated > ago(30d)
| where AppId in ("04b07795-8ddb-461a-bbee-02f9e1bf7b46", "1950a258-227b-4e31-a9cf-717495945fc2")
| summarize SignIns = count(),
SingleFactor = countif(AuthenticationRequirement == "singleFactorAuthentication"),
Protocols = make_set(AuthenticationProtocol),
IPs = make_set(IPAddress, 10),
LastSeen = max(TimeGenerated)
by UserPrincipalName, AppDisplayName
| order by SingleFactor descAccounts with a high SingleFactor count, ropc in Protocols, or a fixed set of server IP addresses are almost always scripts. Without Log Analytics, use the portal: Entra ID > Monitoring & health > Sign-in logs, filter Application on Azure CLI or Azure PowerShell, and check both the interactive and non-interactive user sign-in tabs.
For each account, record where the script runs (an Azure VM, an on-premises server, a pipeline agent, a developer laptop), which subscriptions it changes and which roles it holds. That decides the replacement identity.
Step 2: Choose the replacement identity
| Where the automation runs | Use | Why |
|---|---|---|
| Azure VM, App Service, Functions, Container Apps, AKS | Managed identity | No credential to store or rotate |
| GitHub Actions, Azure DevOps, Kubernetes, another cloud | Workload identity federation (OIDC) on an app registration or user-assigned managed identity | Exchanges the platform's token; no secret |
| On-premises server or anything else | Service principal with a certificate | Non-interactive; avoid client secrets where you can |
| A person at a terminal | Their own account with MFA | Interactive work is not automation |
Microsoft's PowerShell guidance describes managed identities as less flexible but more secure than service principals, and federated identities as the best fit for GitHub Actions, Kubernetes and workloads outside Azure.
Step 3: Update Azure CLI scripts
Grant the new identity the same role, at the narrowest scope that works:
APP_ID="11111111-1111-1111-1111-111111111111"
SCOPE="/subscriptions/<subscription-id>/resourceGroups/<resource-group>"
az role assignment create --assignee "$APP_ID" --role Contributor --scope "$SCOPE"Then replace the az login -u user@contoso.com -p ... line.
On an Azure resource with a managed identity:
# System-assigned managed identity
az login --identity
# User-assigned managed identity: use --client-id, --object-id or --resource-id
az login --identity --client-id <client_id>Anywhere else, with a service principal and a certificate. The PEM file must contain the certificate appended to the private key:
az login --service-principal --username APP_ID --certificate /path/to/cert.pem --tenant TENANT_IDIf you must use a client secret, Azure CLI supports --password; when the secret starts with a hyphen, attach it with --password=CLIENT_SECRET so the parser doesn't read it as an option.
Step 4: Update Azure PowerShell scripts
Connect-AzAccount -Credential $cred with a user name and password uses ROPC, which is incompatible with MFA. Replace it with one of these:
# System-assigned managed identity
Connect-AzAccount -Identity
# User-assigned managed identity
Connect-AzAccount -Identity -AccountId <user-assigned-identity-clientId-or-resourceId>
# Service principal with a certificate from the local certificate store
Connect-AzAccount -ServicePrincipal -ApplicationId $appId -Tenant $tenantId -CertificateThumbprint <thumbprint>For a service principal with a secret, build a credential from the application ID and secret and pass -ServicePrincipal -Credential $pscredential -Tenant $tenantId. Azure PowerShell stores that secret in AzureRmContext.json under the user profile, so protect the folder or prefer a certificate.
If one account has access to several tenants, always pass -Tenant (or -TenantId) so the sign-in targets the tenant you mean rather than the first one found.
Step 5: Update Terraform
The azurerm provider doesn't care which identity you use as long as Azure accepts the token. HashiCorp recommends Azure CLI authentication when you run Terraform locally and a service principal or managed identity when you run it non-interactively.
Locally: run az login as yourself with MFA. If your sign-in didn't include MFA, a plan, which mostly reads, can succeed while the apply fails, because only reads are exempt.
On an Azure VM or self-hosted agent with a managed identity:
export ARM_USE_MSI=true
export ARM_SUBSCRIPTION_ID=00000000-0000-0000-0000-000000000000
export ARM_TENANT_ID=00000000-0000-0000-0000-000000000000
# Only for a user-assigned identity:
export ARM_CLIENT_ID=00000000-0000-0000-0000-000000000000In a pipeline with OIDC (provider 3.7.0 or later):
provider "azurerm" {
features {}
use_oidc = true
}Supply ARM_CLIENT_ID, ARM_TENANT_ID and ARM_SUBSCRIPTION_ID as pipeline variables rather than in the file. In GitHub Actions the provider picks up the runner's token request variables automatically when the job has id-token: write. In Azure DevOps, set ARM_USE_OIDC and ARM_ADO_PIPELINE_SERVICE_CONNECTION_ID, and reference the service connection in at least one task. Since provider 3.44, Azure CLI authentication also works when the CLI itself is signed in as a service principal or managed identity. If you use the azurerm backend for state, configure its authentication too.
Step 6: Move GitHub Actions to OIDC
Create a federated credential that trusts your repository. The subject must match the workflow exactly; for a job that uses the production environment:
{
"name": "github-production",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:contoso/infrastructure:environment:production",
"description": "Production deployments",
"audiences": ["api://AzureADTokenExchange"]
}az ad app federated-credential create --id <app-id-or-object-id> --parameters credential.jsonOther subject formats are repo:<org>/<repo>:ref:refs/heads/<branch>, repo:<org>/<repo>:ref:refs/tags/<tag> and repo:<org>/<repo>:pull-request. Wildcards aren't supported, and an app or user-assigned managed identity can hold at most 20 federated credentials. Then sign in with azure/login and no secret:
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
permissions:
id-token: write
contents: read
steps:
- uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}Add enable-AzPSSession: true if later steps use Azure PowerShell.
Step 7: Make interactive users satisfy MFA at sign-in
For people, the cleanest fix is a Conditional Access policy so that the token already carries MFA. Microsoft's guidance: target the users, select Microsoft Admin Portals and Windows Azure Service Management API under Target resources, grant with Require authentication strength > Multifactor authentication, and start in Report-only. Tenants without Microsoft Entra ID P1 can use security defaults. Note that Azure CLI sign-in requests only User.Read, which matters for policies that target All resources with exclusions; see the baseline scopes enforcement change.
Optional: self-enforce with Azure Policy first
Two built-in definitions let you audit or block non-MFA changes before or alongside the platform enforcement:
| Definition | ID | Effects |
|---|---|---|
| Users must authenticate with multi-factor authentication to create or update resources | 4e6c27d5-a6ee-49cf-b2b4-d8fe90fa2b8b | Audit, then Deny |
| Users must authenticate with multi-factor authentication to delete resources | db4a9d17-db75-4f46-9fcb-9f9526604417 | AuditAction, then DenyAction |
Assign them under Policy > Assignments > Assign policy, use resource selectors on resourceLocation to start with low-risk regions, add a non-compliance message, and switch to deny later with an effect override. Audit events appear only in the Activity log; the compliance view always shows zero resources because the policy evaluates requests, not resources.
AzureActivity
| where CategoryValue == "Policy"
| extend p = parse_json(Properties)
| extend policies = parse_json(tostring(p.policies))
| mv-expand policies
| where tostring(policies.policyDefinitionId) == "/providers/Microsoft.Authorization/policyDefinitions/4e6c27d5-a6ee-49cf-b2b4-d8fe90fa2b8b"
| project TimeGenerated, ResourceId, OperationNameValueVerify
- Rerun the Step 1 query a week after each migration: the old user account should disappear from Azure CLI and Azure PowerShell sign-ins.
- In the sign-in logs, check the service principal and managed identity sign-in tabs for the new identity.
- Run one create, update and delete operation from each migrated script in a test resource group.
- Disable the old user account's role assignments, then the account itself once nothing fails.
Troubleshooting
(RequestDisallowedByPolicy) Resource was disallowed by policy ... Users must authenticate with multi-factor authentication to create or update resources. The token has no MFA claim. Azure CLI 2.76 and later prints the fix: az logout, then az login --tenant <tenant-id> --scope "https://management.core.windows.net//.default" --claims-challenge "<claims-challenge-token>". In unattended code, switch to a workload identity.
Resource was disallowed by policy. Users must use MFA for Create operation. in Azure PowerShell 14.3 and later. Run the suggested Connect-AzAccount -Tenant (Get-AzContext).Tenant.Id -ClaimsChallenge "<claims-challenge-token>" interactively, or require MFA at sign-in.
SharedTokenCacheCredential authentication unavailable on Az 14.2.0 or earlier. Upgrade to Az 14.3.0 and Az.Accounts 5.2.0 to see the real policy error.
UsernamePasswordCredential authentication failed: ... 400 (BadRequest) from Connect-AzAccount -Credential. This is ROPC, which doesn't support MFA. Replace it as shown in Step 4.
WARNING: Unable to acquire token for tenant ... User interaction is required. Azure PowerShell tried the first tenant it found and that tenant requires MFA. Pass -TenantId explicitly.
OIDC sign-in fails with no clear error. The federated credential subject doesn't match the workflow's branch, tag or environment. Check it character by character; a credential with a wrong subject is created without any error.
az ad sp create-for-rbac --name changed an existing app. Display names aren't unique, so the command can reuse a matching identity and replace its credentials. Use a unique name and target existing identities by app ID or object ID.
Checklist
- Phase 2 status confirmed on the Multifactor authentication (Phase 2) page.
- Azure CLI 2.76+ and Azure PowerShell 14.3+ deployed to admin workstations and build agents.
- User accounts signing in to Azure CLI and Azure PowerShell without MFA listed and owned.
- Each script moved to a managed identity, a certificate-based service principal or OIDC, with least-privilege role assignments.
- Terraform pipelines on
use_oidcoruse_msi; local runs on MFA-backedaz login. - Conditional Access requiring MFA for Windows Azure Service Management API, tested in report-only first.
- Break-glass accounts on passkeys or certificate-based authentication.
- Old service-account users stripped of roles and disabled.
For the wider landing-zone context these identities live in, see the enterprise Azure cloud migration playbook.
References
- Plan for mandatory Microsoft Entra multifactor authentication (MFA)
- Verify mandatory MFA setup for Microsoft Entra users
- Tutorial: Self-enforce MFA through Azure Policy
- Troubleshooting Azure CLI
- Troubleshooting the Az PowerShell module
- The impact of multifactor authentication on Azure PowerShell in automation scenarios
- Sign in to Azure PowerShell non-interactively for automation scenarios
- Sign into Azure using a managed identity and Azure CLI
- Sign in with Azure CLI using a service principal
- Authenticate to Azure from GitHub Actions by OpenID Connect
- Create a trust relationship between an app and an external identity provider
- SigninLogs table reference
- Targeting resources in Conditional Access policies
- Terraform azurerm provider: Azure CLI authentication guide
- Terraform azurerm provider: managed identity authentication guide
- Terraform azurerm provider: OIDC authentication guide