The security team’s Slack message arrives on a Tuesday afternoon: “We’re seeing resource deletions in prod. Investigating.” Your heart rate goes up. You pull the Azure Activity Log. The deletions are attributed to a service principal—one your team created eight months ago for a deployment pipeline. The pipeline finished its job. Nobody revoked the credential. The client secret was valid for two years.
It wasn’t a sophisticated attack. Someone found the secret in a Git history, and the principal had Contributor on the entire subscription. Everything it could touch, they touched.
This is the failure mode that network firewalls and VPN gateways don’t prevent. Authentication succeeded. Authorization succeeded. The blast radius was just enormous because nobody thought about what “temporary access for a deployment pipeline” means when the pipeline is gone but the credential isn’t.
Your identity architecture moves away from permanent, static permissions toward a Zero Standing Access (ZSA) model. Use Privileged Identity Management (PIM) for humans and Workload Identity Federation (OIDC) for machines. Every high-privilege action becomes time-bound, auditable, and cryptographic—with no static passwords or keys in your environment.
1. Entra ID vs. Azure RBAC
To build a secure model, you need to keep two authorization systems distinct: the identity provider (Entra ID) and the resource authorization engine (Azure RBAC).
Entra ID vs. Azure RBAC Boundaries
- Entra ID Roles: Manage the directory (Users, Groups, Domains). Examples:
Global Administrator,User Administrator. - Azure RBAC Roles: Manage resources inside subscriptions. Examples:
Owner,Contributor,Storage Blob Data Reader.
Security Rule: Never use Entra ID admins for daily Azure resource management. Create a dedicated Platform Engineers group in Entra ID and assign it Azure RBAC roles at the Management Group scope.
2. Privileged Identity Management (PIM)
Permanent “Owner” assignments are the #1 risk identified in 2026 security audits. PIM solves this by making users Eligible for a role rather than Active—they have to explicitly request access, justify it, and it expires automatically.
PIM JIT Activation Lifecycle
Terraform Implementation:
Use the azurerm_pim_eligible_role_assignment resource to automate your platform access.
resource "azurerm_pim_eligible_role_assignment" "platform_admins" {
scope = "/providers/Microsoft.Management/managementGroups/contoso-platform"
role_definition_id = data.azurerm_role_definition.owner.id
principal_id = azuread_group.platform_admins.object_id
schedule {
start_date = "2026-01-01T00:00:00Z"
expiration {
type = "AfterDuration"
duration = "P365D" # Re-evaluate eligibility annually
}
}
}
3. Secret-less CI/CD with OIDC
Storing “Client Secrets” in GitHub or Azure DevOps repository settings is a legacy risk. If a secret leaks, an attacker has permanent access until someone notices and rotates it. Workload Identity Federation (OIDC) eliminates the secret entirely—your pipeline proves its identity with a short-lived cryptographic token, not a password.
GitHub OIDC Token Exchange Flow
In 2026, use User-Assigned Managed Identities for your self-hosted runners and establish federated credentials between the runner and your GitHub Environment. Only a workflow running in your production environment branch on GitHub can acquire the production deployment token—not a developer’s local machine, not a fork, not a branch named main-copy.
4. Constraining Administration with RBAC Conditions
The “Owner” role is too powerful. It lets a user grant other people “Owner” access, which means one compromised admin can create another compromised admin. Your governance doesn’t survive that.
In 2026, use RBAC Conditions to restrict administrative reach.
Example: Grant “Owner” but block the ability to assign the “Owner” or “User Access Administrator” roles to anyone else. This stops privilege escalation even when an admin account is compromised.
resource platformOwner 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(principalId, 'Owner', scope)
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')
principalId: principalId
# Limit what this Owner can assign to others
conditionVersion: '2.0'
condition: '((!(ActionMatches{\'Microsoft.Authorization/roleAssignments/write\'})) OR (@Request[Microsoft.Authorization/roleAssignments:RoleDefinitionId] ForAnyOfAnyValues:GuidNotEquals {8e3af657-a8ff-443c-a75c-2fe8c4bcb635, 18d7d88d-d4f5-4b35-97b4-c3f4b4b9b9b6}))'
}
}
Key Takeaways
- Zero Standing Access: No human should have permanent administrative rights in production. Use PIM.
- Secret-less Pipelines: Use OIDC for CI/CD. Stop managing
AZURE_CLIENT_SECRETin GitHub. - Scoped Identities: Use User-Assigned Managed Identities for platform automation to decouple identity from resource lifecycle.
- Conditions are Mandatory: Use RBAC conditions to prevent “Admin sprawl” and ensure your governance remains intact.
Next Steps:
- Read Governance at Scale: Writing and Deploying Azure Policies with Terraform and Bicep to use these identities to deploy and enforce Azure Policies.
- Read CI/CD Pipeline for Your Landing Zone: Deploying Azure Verified Modules with GitHub Actions to build the GitHub Actions workflow that uses OIDC to deploy your landing zone.
