Skip to content

Tier 0 Pipeline Design

Part of the Security Model. This page explains why a CI pipeline must never hold Tier 0 access, what can safely be automated, and who is eligible to let Tier 0 changes go live. A pipeline may still run a Tier 0 apply when a PIM-activated senior gives it access for minutes, not standing — see Tier 0 Just-in-Time CI Apply. 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 activation, 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 a CI-applied pipeline identity held Owner/UAA at the Tenant Root Group, instead of a human applying azure-privileged-access-grants under PIM.

flowchart TD
    subgraph "Counterfactual: CI holds Owner at root"
        MR["Merge request to a CI-applied project"] --> 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-privileged-access-grants"] --> PLAN["CI plans only<br/>(read-only identity)"]
        PLAN --> REVIEW["Senior-engineer review"]
        REVIEW --> ACT["PIM activation of the operator persona<br/>(MFA + justification, time-boxed)"]
        ACT --> HUMANAPPLY["Human, local az login,<br/>./apply.ps1 applies the saved plan"]
        HUMANAPPLY --> LIVE2["Owner at tenant root, live,<br/>tied to a named person"]
    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 activation step exists to catch it, because the controls that would normally gate it (PIM activation rules, a named approver) are 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-privileged-access-allowlist / azure-privileged-access-grants) 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 gate on every 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). The gate is either a person applying locally (Lane A apply) or a person giving CI's write identity a 30-minute, PIM-timed grant (just-in-time CI apply). Either way, no pipeline identity holds Tier 0 access between applies.
  • PIM activation on every use, with MFA, a justification and a time limit, exists so activation itself is a separate, purpose-specific act by a named person — addresses AC-6(5) directly. Today there is no approval step, because there is only one operator. Approval on activation is planned once there's a second approver (see Role Tier Mapping).
  • 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-landingzones, and read access at mg-platform, don't get the same treatment — the failure mode that justifies it (self-referential Control-plane compromise) doesn't apply there. Write access at mg-platform does, because it reaches Tier 0 state in rg-terraform-state.


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 the activation rules (MFA, justification, approval) on a Tier 0 persona — the pipeline that enforces the control cannot be trusted to remove it unsupervised
PIM eligibility for Tier 1/2 roles Any role assignment (Owner/UAA/Reader) at tenant root or mg-intermediate-root
Read-only or reporting pipelines Anything that lets an identity grant itself more privilege (Privileged Role Administrator, or an app with admin-consent rights)

For the specific directory roles, Graph permissions and RBAC assignments an applying CI identity must never hold, see Prohibited CI Permissions.

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 (human apply, or just-in-time CI apply) Tier 1/2 (CI, auto-apply)
Standard engineer Not PIM-eligible for anything at Tier 0. Can write Tier 0 changes and, under just-in-time CI apply, click apply once a senior has granted access — but can't grant it. Full participation — merge rights to the Tier 1/2 project are enough; no personal privileged role needed
Senior engineer PIM-eligible for the persona the change requires, per Entra directory roles and Azure RBAC by management group. Every activation requires MFA, a justification and a time limit; approval is added once there's a second approver. 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-privileged-access-grants' required MR approvers should name senior engineers specifically, or AC-5's separation is nominal only.