Microsoft has announced that by October 1, 2025 all Azure sign-ins (including CLI, PowerShell, REST, and IaC tools) will require MFA for user accounts. This means existing automation scripts that use user credentials will break, since MFA cannot be completed non-interactively. To address this, Azure recommends switching any “service accounts” to workload identities – namely service principals or managed identities – which do not require MFA and support non-interactive login.
- Impact on automation: Any script doing az login with a user/password will fail with an “Interactive authentication needed” error once MFA enforcement begins. For example, ROPC flows or unattended logins become incompatible with MFA.
- Solution: Use Service Principals (or Managed Identities) for automation instead, as these are intended for apps/scripts and aren’t affected by the MFA mandate.
By moving to service principals, our automation can continue running unattended without MFA prompts. Below we explain what service principals are (in plain terms), how they differ from managed identities, and what roles we can assign to them (both Azure RBAC and EA/billing roles). We also give sample Terraform and Azure CLI snippets for creating SPNs and assigning roles.
What Is an Azure Service Principal (Layman’s Terms)
An Azure service principal is essentially a “robot identity” in our Azure AD tenant. Instead of creating a real user for automation, we create an application registration which in turn creates a service principal object. This service principal has its own client ID (app ID) and credential (client secret or certificate) which our scripts can use to authenticate. In simple terms, it’s like giving our script its own login account.
According to Microsoft, a service principal is “An application whose tokens can be used to authenticate and grant access to specific Azure resources”. In practice, using a service principal lets us avoid “fake user” accounts (legacy service accounts) and instead use a dedicated non-human identity for automation. Its permissions are controlled by Azure RBAC roles, so we can tightly restrict exactly what resources it can access. Common use-cases include Terraform deployments, CI/CD pipelines (e.g. Azure DevOps service connections), or any third-party tool that needs Azure tokens.
In contrast, managed identities are a special type of service principal automatically created and managed by Azure for a specific resource (like a VM or App Service). The key differences are: managed identities are tied to one or more Azure resources and Azure maintains the credentials (we never see the secret). This makes managed identities very secure but only useful when our code runs on an Azure resource. A general service principal (unlike a managed identity) is not bound to any resource and is managed by us – giving more flexibility (multi-tenant use, cross-subscription) at the cost of having to securely manage its secret. In summary:
- Service Principal – a standalone Azure AD identity (with client ID/secret) for automation. Flexible: can access many resources or subscriptions, but we must create and store its credentials.
- Managed Identity – an automatically managed service principal tied to a specific Azure resource. Less flexible: only usable from that resource, but more secure since Azure handles the credentials.
Authenticating with a Service Principal
Once we have a service principal, our automation can log in with it instead of a user. For example, with the Azure CLI we would use:
az login --service-principal -u <APP_ID> -p <CLIENT_SECRET> --tenant <TENANT_ID>
This command signs you in as the service principal non-interactively. The output (as shown below) includes the appId
, password
(the secret), and tenant
we will need to store securely:
After logging in, we can run az commands just like a user. Our SPN’s actions are governed by its assigned roles (below), and it will not be prompted for MFA.
Azure RBAC Roles for Service Principals
A service principal can be assigned Azure RBAC roles at any scope (management group, subscription, resource group, or resource) to grant it permissions to Azure resources. The built-in RBAC roles work the same for SPNs as for users. Common roles include:
- Owner – Full access to manage all resources and assign roles (highest privilege).
- Contributor – Manage all resources (create/update/delete), but cannot assign RBAC roles.
- Reader – View-only access to all resources.
- User Access Administrator – Can manage user and service principal access (role assignments), but not resources.
We can also assign service principals any specialized built-in roles (e.g. Key Vault Contributor, Storage Account Contributor, Azure Functions Contributor, etc.) depending on the tasks they need to perform. The principle of least privilege applies: give the SP only the roles it needs. For example, an SPN that only deploys resources might be a Contributor on a resource group; an SPN that only needs to read metrics could be a Reader.
Terraform can create these assignments. For example, after creating an SPN, we can assign roles like this:
data "azuread_service_principal" "example" {
application_id = azuread_application.example.application_id
}
resource "azurerm_role_assignment" "assign_contributor" {
scope = azurerm_resource_group.example.id
role_definition_name = "Contributor"
principal_id = data.azuread_service_principal.example.object_id
}
This snippet looks up the SPN by its application_id
and assigns it the Contributor role on a resource group. The same pattern works for any built-in or custom role (using its name or ID). We can also iterate (for_each) over a list of role names if the SP needs multiple roles in the same scope.
Azure EA/Billing Roles for Service Principals
For organizations on an Enterprise Agreement (EA), Azure defines special billing and administrative roles that govern costs, subscriptions, and billing data across the enrollment hierarchy. These roles differ from RBAC roles: they control EA-level resources (like enrollments, departments, accounts) rather than Azure resource groups. Notably, EA roles (such as Enrollment Reader, Department Reader, Subscription Creator, EA Purchaser) can be assigned to service principals via APIs for automating billing tasks. These roles are not visible in the Azure portal and must be assigned programmatically.
Key EA roles we can grant to a service principal include:
- EnrollmentReader – View usage and charges across the entire enrollment (all accounts and subscriptions) and see remaining commitment balance.
- DepartmentReader – View usage and charges within a specific department scope.
- SubscriptionCreator – Create new subscriptions under an enrollment account. (This maps to the EA Administrator’s permission to add subscriptions)
- EA Purchaser – Purchase reservation orders and view all enrollment usage (inherits EnrollmentReader and DepartmentReader rights).
- PartnerAdmin Reader – (For EA partners) View data for all enrollments under the partner organization. Each EA role has a fixed role-definition ID that we use when assigning it. Unlike RBAC roles, an SP can only hold one EA role at a time.
Use cases: For example, an automation that needs to create Azure subscriptions on demand would use the SubscriptionCreator
EA role. A reporting service that pulls cost details could use EnrollmentReader
. These roles ensure the SP can manage billing entities without being a full EA admin.
Because EA roles aren’t yet supported in Terraform, we assign them with Azure REST APIs or Azure CLI. For example, using Azure CLI’s az rest
command we can PUT a billing role assignment:
# Assign SubscriptionCreator role to SP on an EA enrollment account
az rest --method put \
--uri "https://management.azure.com/providers/Microsoft.Billing/enrollmentAccounts/{enrollmentId}/billingRoleAssignments/{guid}?api-version=2020-05-01" \
--body '{
"properties": {
"principalId": "<SERVICE_PRINCIPAL_OBJECT_ID>",
"roleDefinitionId": "a0bcee42-bf30-4d1b-926a-48d21664ef71", # ID for SubscriptionCreator:contentReference[oaicite:42]{index=42}
"scope": "/providers/Microsoft.Billing/enrollmentAccounts/{enrollmentId}"
}
}'
We would replace {enrollmentId}
with our EA enrollment’s ID, use the SPN’s object ID, and the role-definition ID from the table above. Similarly, EnrollmentReader
is ID 24f8edb6-1668-4659-b5e2-40bb5f3a7d7e
, etc. (The Azure CLI also provides billing-specific commands under az billing
, but the REST approach is more direct for EA roles).
Creating a Service Principal with Terraform
As a concrete example, here’s how we might provision a service principal using Terraform. We typically use the azuread
provider to create an Azure AD application and SPN, and optionally a client secret:
terraform {
required_providers {
azuread = { source = "hashicorp/azuread" }
}
}
# 1. Create the Azure AD application (registration)
resource "azuread_application" "app" {
display_name = "example-service-principal"
}
# 2. Create the service principal linked to that application
resource "azuread_service_principal" "sp" {
application_id = azuread_application.app.application_id
}
# 3. Create a client secret for the SP (rotating monthly, for example)
resource "time_rotating" "month" {
rotation_days = 30
}
resource "azuread_service_principal_password" "password" {
service_principal_id = azuread_service_principal.sp.object_id
rotate_when_changed = { rotation = time_rotating.month.id }
}
# Output the credentials (client_id and client_secret)
output "client_id" {
value = azuread_application.app.application_id
}
output "client_secret" {
value = azuread_service_principal_password.password.value
sensitive = true
}
When we run this, Terraform will create an AAD App and a corresponding SPN, and then generate a client secret. The outputs client_id
and client_secret
can be used by our scripts to authenticate. (Always keep the secret safe, e.g. store it in Azure Key Vault rather than plaintext).
Once the SPN exists, we can use the Azure CLI or Terraform (as above) to assign roles to it. For example, to give this SPN Contributor rights on a subscription or resource group, use an azurerm_role_assignment
as shown earlier.
TL;DR
With Azure’s new MFA mandate, any automation using user accounts will break. The fix is to use service principals (or managed identities) for non-interactive authentication. A service principal is like a “service account” for our code: it has its own Azure AD identity and credentials. Managed identities are similar but tied to a specific Azure resource. We then control what the SPN can do by assigning Azure RBAC roles (e.g. Contributor, Owner) at the resource level, and if we’re on an Enterprise Agreement, by assigning EA billing roles (e.g. SubscriptionCreator, EnrollmentReader) at the enrollment level.
In practice, we would create the SPN (via Terraform, CLI, or portal), assign it the minimum necessary roles, and update our automation scripts to log in with the SPN’s credentials (e.g. az login --service-principal …
). This approach maintains secure automated workflows while complying with the new MFA requirements.
Sources:
Microsoft documentation-
Mandatory MFA
Impact of MFA on automation
Service principals
Built in RBAC roles
EA Roles assignment
Top comments (0)