Skip to content

Tier 0 Pipeline Design

Part of the Security Model. This page explains why Tier 0 changes are never applied by a CI pipeline, what can safely be automated, and who is eligible to apply Tier 0 changes. It applies the Access Tiering Model. For the full Lane A and Lane B operating model, including the comparison matrix, runbooks and preflight checks, see Lane A and Lane B Delivery Methodology.

Related implementations:


What if this were applied differently — a pipeline holding Owner/UAA at root

It's worth walking through the counterfactual explicitly, because the complexity this tenant has built (two repos, a human CLI gate, PIM approval, senior-engineer-only eligibility) is a direct, traceable response to specific standards violations — not complexity for its own sake.

To be precise about which standard is doing the work here: CAF doesn't flatly prohibit a pipeline holding privileged access (see the model). What follows describes a pipeline that fails CAF's own stated conditions for when that's acceptable — no equivalent governance, no built-in escalation limit — not merely a hypothetical that falls short of this tenant's stricter-than-required choice.

Suppose azure-platform-identity's CI-applied service principal held Owner/UAA at the Tenant Root Group instead of azure-priv-tier-0's human-applied identity.

flowchart TD
    subgraph "Counterfactual: CI holds Owner at root"
        MR["Merge request to azure-platform-identity"] --> CIM["CI auto-applies on merge<br/>(no second gate)"]
        CIM --> LIVE1["Owner at tenant root, live<br/>— no human judged this specific action"]
    end

    subgraph "What this tenant actually does"
        MR2["Merge request to azure-priv-tier-0"] --> PLAN["CI plans only"]
        PLAN --> REVIEW["Senior-engineer review"]
        REVIEW --> HUMANAPPLY["Human, local az login,<br/>PIM-activated Global Administrator"]
        HUMANAPPLY --> ACT["PIM activation request"]
        ACT --> APPROVE["Named approver reviews + justification"]
        APPROVE --> LIVE2["Owner at tenant root, live,<br/>time-bound, alerted"]
    end

Here's specifically what breaks, standard by standard:

  • CAF's own conditions for a privileged pipeline. No PIM-equivalent governance exists for this SP (it's a standing grant, not time-bound or approved per-use), and nothing structurally stops it from applying a role assignment wider than intended — both of CAF's stated conditions fail, not just this tenant's stricter internal rule.
  • EAM's plane-isolation principle. EAM's whole model rests on lower-trust systems never being able to manage higher-trust ones. A CI runner triggered by anyone with merge rights — or by anyone who compromises that runner or its OIDC token — is a lower-trust surface than a human on a managed device with an activated privileged role. Giving it Control-plane authority collapses the isolation the model depends on.
  • AC-6(5) Privileged Accounts. This control requires privileged accounts be restricted to explicitly authorized individuals for explicitly authorized purposes. A CI identity used for routine merges is neither — it's a shared, purpose-generic identity, and "someone merged a PR" is not an authorized privileged purpose.
  • AC-5 Separation of Duties. The reviewer approving a Terraform diff is judging "is this code correct," not "should Global-Admin-equivalent access go live right now." Merge-to-apply collapses those into one action with one judgment call, removing the second, purpose-specific check separation of duties requires.
  • AC-2(6) Dynamic Privilege Management. A CI identity holding standing Owner at root — even if only to execute a Terraform apply — is a standing privileged grant, the opposite of the just-in-time model NIST and EAM both call for.

The concrete failure path: a merge to main — malicious, or just a mistake — auto-applies immediately. No approval step exists to catch it, because the control that would normally require one (pim_require_approval, a named approver) is defined by the same automated pipeline that just applied the change. One bad merge is full tenant compromise, with no human ever having made a judgment call about that specific action.

That failure path is exactly why the added complexity is necessary, not incidental:

  • The two-repo split (solution-priv-tier-0 / azure-priv-tier-0) exists so a compromised pipeline identity can't widen its own allow-list — this is CAF's own named condition (prevent self-escalation) satisfied directly, and it also addresses AC-6(5) (bounding what the "purpose" can ever be) and the EAM isolation principle (the allow-list is unreachable from the lower-trust repo).
  • The human-CLI-only apply exists so no merge, by itself, can ever make Tier 0 access live — this is where the design goes beyond CAF's minimum (equivalent governance) rather than attempting to simulate it for a service principal; it also addresses AC-5 (separates "code review" from "privilege activation") and AC-2(6) (nothing is standing; every use requires reactivation).
  • Mandatory PIM approval on every grant, hardcoded so it can't be toggled off by editing a variable, exists so activation itself has a second, purpose-specific check — addresses AC-6(5) directly.
  • Restricting Tier 0 PIM eligibility to senior engineers exists because AC-5's separation only works if the approver pool is distinct from, and at least as trusted as, the requester pool.

Every one of those is a mitigation for a named failure mode above, not a default level of caution applied everywhere. That's also why mg-platform and mg-landingzones don't get the same treatment — the failure mode that justifies it (self-referential Control-plane compromise) doesn't apply there.


What's safe to automate vs. what needs a human gate

Safe to fully automate (CI, merge = apply) Requires a manual, out-of-band gate
Creating groups and membership for non-privileged access Any change to a Tier 0 role's PIM eligibility list
Standing Reader / cost-management-style roles at Tier 1/2 scopes Any change to pim_require_approval on a Tier 0 grant — the pipeline that enforces the control cannot be trusted to remove it unsupervised
PIM eligibility for Tier 1/2 roles that already require approval Any role assignment (Owner/UAA/Reader) at tenant root or mg-grinntec
Read-only or reporting pipelines Anything that lets an identity grant itself more privilege (Privileged Role Administrator, or an app with admin-consent rights)

Automation may define policy (AC-6 least-privilege scoping). Only a supervised, logged, time-bound human action may exercise Tier 0 privilege (AC-2(6), AC-5).


Who is even eligible to hold Tier 0 PIM — engineer seniority

The Role Tier Mapping answers which PIM role a human needs to activate. It doesn't answer who should be allowed to be eligible for it in the first place — that's what AC-5 (Separation of Duties) actually requires: the approver pool must be distinct from, and no less trusted than, the requester pool.

Engineer level Tier 0 (CLI, human apply) Tier 1/2 (CI, auto-apply)
Standard engineer Not PIM-eligible for anything at Tier 0. All of it routes to a senior engineer. Full participation — merge rights to azure-platform-identity are enough; no personal privileged role needed
Senior engineer PIM-eligible for the roles the change requires, per Entra directory roles and Azure RBAC by management group. Every activation still requires approval + justification + time-bound + alerting. Same as standard — seniority isn't a factor at Tier 1/2

Why the line sits there

Tier 0 tasks aren't harder to write Terraform for — adding one UPN to a PIM eligibility list is trivial at any level. What seniority gates is judgment about blast radius: recognising that a seemingly small change is really about who can eventually reach GA/PRA-adjacent territory. azure-priv-tier-0's required-approvers should name senior engineers specifically, or AC-5's separation is nominal only.