Skip to content

CloudRunbook | Practical Cloud Engineering

Identity-first Azure: the baseline every landing zone should start with

7 min read

A practical identity baseline for secure Azure architecture: admin separation, PIM, Conditional Access, workload identities, and secrets. Written as a runbook you can implement.

Applies to
Entra IDPIMRBAC
Effort
1–2 days
Risk
Medium
Rollback
Partial
Last reviewed
26 Jul 2026
TL;DR
  • Split admin identities from daily identities and enforce least privilege through PIM.
  • Group-based RBAC + Conditional Access + logging is the bare minimum; portal role assignments do not scale.
  • Day 0: create admin groups, PIM-eligible roles, CA baseline, break-glass. Day 30: convert noisy recommendations into policy, federate workload identities. Quarterly: review PIM activity, CA logs, exemptions.
  • Secrets belong in Key Vault with private endpoints; prefer managed identities and OIDC for workloads.
  • Treat the directory like code: Terraform for groups/assignments, Bicep/Policy for guardrails, KQL for validation.
Before you start
  • Access: Privileged Role Administrator in Entra ID for PIM configuration, and User Access Administrator (or Owner) at the management group scope for the Azure RBAC eligibility. These are two different control planes — you need rights on both.
  • Licensing: PIM and risk-based Conditional Access require Entra ID P2. The rest of this baseline works on P1.
  • Tooling: Terraform 1.7+ with azurerm 3.100+ and azuread 2.47+.
  • Do this first: create and test the break-glass accounts before you enforce any Conditional Access policy. Locking yourself out is the classic failure here.

Why identity-first is the fastest path to “secure by default”

In Azure, identity is your control plane. If an attacker (or a rushed engineer) can get privileged identity access, all the networking controls and policy guardrails become speed bumps.

An identity baseline is not about locking people out. It’s about creating a predictable model where:

  • platform admins can do platform work safely
  • workload teams can ship without needing broad rights
  • credentials stop living in pipelines and random scripts
  • you can explain “who can do what” in 60 seconds

What good looks like

  • Admin separation – every privileged user has a dedicated admin identity, MFA, and Conditional Access policies applied.
  • Group-based RBAC with PIM – roles are assigned to Entra groups, not individuals, and are eligible (not permanent).
  • Documented break-glass – two emergency accounts, monitored, stored offline, tested quarterly.
  • Workload identity pattern – managed identities for Azure hosts, OIDC federation for CI/CD, Key Vault for secrets behind private endpoints.
  • Policy + logging – Azure Policy enforcing RBAC hygiene, PIM activity logged to a central workspace, alerts hitting the SOC.

Baseline decision points

  1. Scope for admin roles – Tenant root vs management group, who owns what, and which roles exist (Platform, Security, Network, Workload).
  2. PIM configuration – Activation durations, approval requirements, justification text, and notifications.
  3. Conditional Access baseline – MFA requirements, trusted locations, device compliance expectations, and break-glass exclusions.
  4. Break-glass process – Who holds credentials, when they’re used, how they’re monitored/resets.
  5. Workload identity strategy – Managed identities vs service principals, when to use OIDC federation, and how to track secrets.
  6. Logging + alerting – Where PIM/Sign-in logs land, which alerts go to the SOC/platform, and how exemptions are tracked.
  7. Cost/trade-offs – Azure AD P1/P2 licensing coverage and training needs for engineers.

Signal vs noise

  • Enable now: Separate admin accounts, PIM eligibility, CA baseline, break-glass monitoring, managed identities for new workloads.
  • Enable at Day 30: Full OIDC federation for all CI/CD, automatic revocation of unused service principals, policy enforcement of MFA for workload admins.
  • Probably never: Permanent Owner at tenant root, secrets scattered in pipelines, or “MFA optional” exceptions without expiry.

Phased rollout

  • Day 0 baseline – Create admin groups, PIM assignments, CA policies, break-glass accounts, and Terraform modules to manage them.
  • Day 30 hardening – Enable auditing policies, convert recurring identity recommendations into Azure Policy, federate major pipelines.
  • Quarterly review – Use KQL to audit PIM activations, check for direct user role assignments, rotate break-glass secrets, and review exemptions.

Runbook: identity baseline for Azure landing zones

  1. Create admin separation

    Create separate admin accounts for anyone with privileged access.

    Baseline:

    • One “normal” user identity for daily work
    • One “admin” identity for privileged actions (no email use, no browsing)

    If you use admin workstations or hardened browser profiles, apply them to admin identities first.

  2. Define your admin roles (small set, least privilege)

    Start with the smallest useful set.

    Typical roles:

    • Platform Admin: owns management groups, policy, subscription vending
    • Security Admin: owns Defender/Sentinel integration and security config
    • Network Admin: owns connectivity patterns, DNS zones, firewall/vWAN config

    Be clear which control plane you’re on. Managing management groups, policy and subscriptions is Azure RBAC, not an Entra directory role — reach for Owner/Contributor at a management group scope, not Privileged Role Administrator. Directory roles are for managing the directory itself.

    If you automate PIM, make the group eligible rather than permanently assigned:

    pim-platform-admin.tf
    HCL
    # PIM will not accept a group that isn't role-assignable, and the flag cannot
    # be changed after creation. Get it right the first time.
    resource "azuread_group" "platform_admin" {
      display_name       = "alz-platform-admin"
      security_enabled   = true
      assignable_to_role = true
    }
    
    data "azurerm_management_group" "platform" {
      name = "mg-platform"
    }
    
    data "azurerm_role_definition" "contributor" {
      name = "Contributor"
    }
    
    # Eligible, not active. Members still activate through PIM, and the activation
    # requirements (MFA, justification, approval, max duration) live in the PIM
    # role settings rather than here.
    resource "azurerm_pim_eligible_role_assignment" "platform_admin" {
      scope              = data.azurerm_management_group.platform.id
      role_definition_id = data.azurerm_role_definition.contributor.id
      principal_id       = azuread_group.platform_admin.object_id
    
      schedule {
        start_date_time = timestamp()
        expiration {
          duration_days = 365
        }
      }
    
      justification = "Platform team standing eligibility, activated via PIM"
    }

    Avoid giving everyone Owner. Owner is convenient, but it’s also an incident generator — and it carries Microsoft.Authorization/*, so an Owner can quietly grant themselves anything else.

  3. Enable PIM for privileged role elevation

    Set a policy that privileged roles are:

    • Eligible (not permanent)
    • Time-limited activation
    • Require MFA
    • Require justification (and optionally approval for the highest roles)

    Practical defaults:

    • 1–4 hour activation for high privilege roles
    • approval required for the most sensitive roles (your call)
  4. Create and protect break-glass accounts

    Break-glass accounts exist for a scenario where Conditional Access or MFA is misconfigured and admins get locked out.

    Baseline:

    • At least 2 break-glass accounts
    • Strong passwords stored offline in a secure process
    • Exempt them from some controls carefully (or use emergency access accounts)
    • Monitor and alert on any sign-in

    Document who can use them and what “emergency” means.

  5. Conditional Access baseline for admins

    Keep this practical. You can add nuance later.

    Admin baseline:

    • Require MFA for admin identities
    • Block legacy auth (if applicable)
    • Restrict admin access to trusted locations/devices (if your org can)
    • Enforce sign-in risk policies if using Identity Protection

    The goal is to reduce credential replay and opportunistic admin compromise.

  6. Standardise RBAC assignments at management group scope

    Assign platform roles at the correct scope.

    Recommended:

    • Platform team roles assigned at Platform management group
    • Workload team roles assigned at subscription scope or workload MG scope

    If you assign high privilege at tenant root “just to make it work”, you will keep it forever.

  7. Workload identity: prefer managed identity and federated credentials

    For apps and automation:

    • Prefer managed identities for Azure-hosted workloads
    • Prefer federated credentials (OIDC) for GitHub Actions / CI pipelines where possible
    • Avoid long-lived service principal secrets

    This drastically reduces the “credential in a pipeline” risk.

  8. Secrets and keys: make them boring

    If you must store secrets:

    • Put them in Key Vault (or your chosen secret store)
    • Control access via RBAC
    • Use private connectivity if you’re operating in a private network model
    • Rotate secrets on a schedule

    The best secret is one you don’t need. The second best is one you can rotate without drama.

  9. Logging and alerting for identity events

    You should treat identity events as security signals.

    Minimum:

    • alert on privileged role activation
    • alert on break-glass sign-in
    • alert on risky sign-ins (if available)
    • audit role assignment changes

    If you don’t alert on privilege changes, you’re blind to the highest-risk activity.

Guardrails via Azure Policy

Enforce identity hygiene with policy initiatives. Use this Bicep skeleton to ensure critical roles are assigned to groups, not individuals, and that PIM eligibility is applied. Replace the policy definition IDs with your own.

BICEP
targetScope = 'managementGroup'

// Resolve real definition IDs first, e.g.:
//   az policy definition list --query "[?contains(displayName,'<search term>')].{name:displayName,id:id}" -o table
resource initiative 'Microsoft.Authorization/policySetDefinitions@2023-04-01' = {
  name: 'identity-guardrails'
  properties: {
    displayName: 'Identity Guardrails'
    description: 'Enforce group-based RBAC and audit PIM coverage.'
    policyType: 'Custom'
    policyDefinitions: [
      {
        policyDefinitionReferenceId: 'deny-user-role-assignment'
        policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/<deny-user-role-assignment-id>'
      }
      {
        policyDefinitionReferenceId: 'audit-pim'
        policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/<audit-pim-id>'
      }
    ]
  }
}

resource assignment 'Microsoft.Authorization/policyAssignments@2023-04-01' = {
  name: 'identity-guardrails'
  properties: {
    displayName: 'Identity Guardrails'
    policyDefinitionId: initiative.id
  }
}

Validation checks

Use KQL to spot risky patterns. This lists directory role assignments made in the last 7 days where the principal is a user rather than a group. It needs Entra ID audit logs streaming into the workspace via a diagnostic setting.

KQL
AuditLogs
| where TimeGenerated > ago(7d)
| where Category == "RoleManagement"
| where OperationName has "Add member to role"
| mv-expand target = TargetResources
| where tostring(target.type) == "User"
| project TimeGenerated,
          InitiatedBy = tostring(InitiatedBy.user.userPrincipalName),
          TargetUser  = tostring(target.userPrincipalName),
          Changed     = target.modifiedProperties
| order by TimeGenerated desc

For Azure RBAC — the control plane that actually governs subscriptions — query the resource layer instead. This finds role assignments made directly to users rather than groups:

KQL
AzureActivity
| where TimeGenerated > ago(7d)
| where OperationNameValue =~ 'MICROSOFT.AUTHORIZATION/ROLEASSIGNMENTS/WRITE'
| where ActivityStatusValue =~ 'Success'
| project TimeGenerated, Caller, SubscriptionId, ResourceId = _ResourceId
| order by TimeGenerated desc
  • Admins use separate admin identities for privileged work.

  • Privileged roles are eligible and activated just-in-time via PIM.

  • Break-glass accounts exist, are protected, and are monitored.

  • Conditional Access baseline is enforced for admin identities.

  • Workloads use managed identity or OIDC federation; secrets are not sitting in pipelines.

  • Role assignments are group-based and documented.

  • PIM activation logs are stored centrally and reviewed.

Common pitfalls

Pitfall: direct user assignments

Uncontrolled sprawl. Use groups only and enforce via policy.

Pitfall: PIM without alerts

If nobody monitors activations, PIM is just extra clicks. Wire alerts to SOC/platform.

Pitfall: eternal CA exemptions

Permanent exclusions for “that one vendor” never get reviewed. Force expiry and document justification.

Pitfall: forgotten run-as accounts

Service principals with passwords older than your tenancy keep popping up. Inventory and retire them.

Pitfall: break-glass drift

Credentials stored in someone’s drawer and never tested defeat the point. Test quarterly and log usage.

Rollback / back-out plan

  • PIM misconfiguration – revert the Terraform module, redeploy previous settings, and communicate downtime for activations.
  • Conditional Access outage – disable the offending policy via the emergency account, fix offline, reapply via source control.
  • Policy guardrail issue – disable the assignment (not the definition), patch parameters, pilot in a test MG, then reapply.
  • Workload identity rollback – keep legacy service principal credentials for a limited overlap while OIDC rollouts complete. Document the sunset date.

For every rollback, log who executed it, why, and when the change will be resubmitted. Identity hygiene is only real when it survives change windows.