Why an agent image is Tier 0
Picture a Tier 0 deployment pipeline. A job starts, the platform hands it a service connection token, and the job runs az, terraform or kubectl against the identity control plane. Every one of those steps executes inside a container image or on an agent image. Whoever controls that image controls what the job does with the token.
Microsoft calls this the clean source principle: every security dependency must be as trustworthy as the object it secures. An image that runs Tier 0 jobs is such a dependency. That settles the most common question in one line: agent and tooling images are built inside Tier 0 from pinned, verified inputs, and never promoted from Tier 2. No promotion gate can make a Tier 2 build system trustworthy after the fact.
Application images are different. They run on Tier 0 infrastructure but do not hold pipeline tokens, so Tier 2 can build them as long as Tier 0 decides what gets in. The rest of this article follows those two routes and the controls on each.
Four eyes and a PAW do different jobs
Teams with a strict two-person rule sometimes ask whether they still need PAWs. They do, because the two controls answer different questions. Four eyes asks: does a second person agree with this change? The PAW asks: is this session really that person, on a clean device?
Four eyes only guards the pipeline path. A Tier 0 administrator’s activated roles also work directly in the portal, the CLI and Graph, where no pipeline approval applies. And if an approver’s session is stolen from a daily-driver laptop, the attacker becomes the second pair of eyes.
| Situation | Four eyes in Azure DevOps | PAW | Controls |
|---|---|---|---|
| One engineer merges a malicious change alone | Stops it | Does not help | C2 C3 |
| An approver’s browser session is stolen on a daily-driver laptop | The attacker approves as that person | Tier 0 accounts cannot sign in outside a PAW | C1 |
| An admin uses an activated role directly in the portal to change the registry | No pipeline involved | Protects the session, not the intent; add PIM approval and alerts | C12 C13 |
| Malware on the engineer’s device edits the PR after approval | Only if votes reset on new pushes and the last pusher cannot approve | Clean device | C1 C2 |
| A pipeline job abuses an agent VM’s managed identity | Runs after approval | Not a human session | C3 C4 C7 |
| An Azure DevOps admin turns the checks off | Admins edit the checks | Only if admins are Tier 0 accounts on PAWs | C12 C13 |
There is a corollary for the PAW itself: do not build images or install packages on it. Package installs run third-party code at install time. The Shai-Hulud npm worm ran from postinstall and preinstall scripts and harvested cloud credentials, including the Azure CLI profile. On a PAW, that code would run next to activated Tier 0 tokens. The PAW authors code and approves runs; builds move to fresh, network-isolated Tier 0 agents, and everyday development stays on the normal workstation with a non-privileged account.
Decide the route first
Before choosing tools, classify the image. The test is simple: is it referenced by a container step on a Tier 0 job, is it the image of a Tier 0 agent pool, or do Tier 0 pipeline steps call tools inside it? Then it runs Tier 0 jobs. If it only runs on the Tier 0 cluster as an application, it is a workload image, unless that application can obtain Tier 0 credentials.
Decision flow
Does this image run Tier 0 pipeline jobs?
Treat it as Tier 0 control plane
Build it inside Tier 0 from pinned, verified inputs and never promote it from Tier 2.
Recommended
Managed DevOps Pools
Stateless pools give every job a fresh VM in your Tier 0 virtual network. No agent image to build, sign or patch, no registration secret, no Docker-in-Docker.
When in doubt, treat it as Tier 0. Rebuilding inside Tier 0 is always safe; promoting is safe only for workloads.
The target state, step by step
Here is the whole path from a pull request on the PAW to a signed image running on Tier 0 AKS. Press Play flow to walk the nine steps, select any box to see what it does, or select a dark tag (C1 to C14) to see the control behind it and how to verify it.
Nine steps from a PR on the PAW to a signed image running on Tier 0 AKS.
Select any box or a dark control tag to see what it does, which controls apply and how to verify them.
Three ideas carry most of the weight. First, jobs run only on stateless Managed DevOps Pools: each job gets a fresh VM that is discarded afterwards, and no managed identity is attached to any pool, so a job cannot pick up credentials from the VM. Second, internet egress and write credentials never share a job: acquire can reach pinned vendor endpoints but only writes the catalog, build has neither internet nor write access, and publish holds the push token but no internet. Third, the only identity that can push is reached through workload identity federation, so there is no secret to steal and only runs that pass the checks can use it.
Promoting application images from Tier 2
Only application images take this route, and the direction matters: Tier 0 pulls, Tier 2 never pushes.
- Tier 2 builds the application image, signs it with the Tier 2 Notation key, attaches the SBOM and provenance, and pushes it to a staging registry with public access off.
- The staging registry gets a second private endpoint in the Tier 0 registry endpoint subnet. Tier 2 and Tier 0 are never peered. The Tier 0 promote identity has pull-only rights on staging.
- The promote job verifies the Tier 2 signature and the provenance (repository, branch, pipeline) against a Notation trust policy.
- It scans the image against an internally mirrored vulnerability database.
- It copies the image by digest into the Tier 0 registry, including referrers such as the signature and SBOM.
- It signs the same digest with the Tier 0 key held in Key Vault.
- Tier 0 AKS admits only digests signed by the Tier 0 key.
Why not az acr import? Import needs the Container Registry Data Importer and Data Reader role on both registries, and every network-restricted registry involved must allow trusted services to bypass its network rules. That reopens exactly the paths the design closes. With export disabled on the Tier 0 registry, import out of it is blocked, which is intended.
One DNS detail is easy to miss. If privatelink.azurecr.io is a shared, central zone, the staging registry’s second endpoint registers a record that collides with its Tier 2 record. Give Tier 0 its own zone and resolver path.
A registry no human can push to
Only two managed identities hold data-plane roles: AcrPush for the orchestration identity through workload identity federation, and AcrPull for the AKS kubelet identity. No human has a role assignment and the admin user is off. On the network side, public access is off and the registry is reachable only through its private endpoint.
| Setting | Value | Why |
|---|---|---|
| SKU | Premium | Required for private endpoints, export policy and network rules. |
| Public network access | Disabled | Clients outside the virtual network cannot reach the registry endpoints. |
| Admin user | Disabled | No shared username and password; every push and pull is an Entra ID identity. |
| Export policy | Disabled | Blocks import and transfer pipelines out of the registry. Requires public access to be disabled. |
| Trusted-services bypass | None | Azure services use the private endpoint like everyone else. |
| Network rule set | Default deny, no IP rules | Defence in depth if someone re-enables public access. |
| Anonymous pull | Disabled | No unauthenticated reads. |
| DNS | Private DNS zone group | Writes both records the registry needs: the login server and the regional data endpoint. |
| Endpoint subnet NSG | 443 from runtime and build subnets only | Needs private endpoint network policies enabled on the subnet. |
| Data-plane roles | AcrPush (orchestration, via WIF), AcrPull (AKS) | ABAC-enabled registries ignore AcrPull and AcrPush; do not switch modes without replacing both. |
| Integrity | Deploy by digest, admit by signature | Tag locks only stop accidents. Docker Content Trust is being removed from ACR; use Notation. |
| Delete lock | CanNotDelete | Blocks control-plane deletion; image operations are unaffected. |
Governance closes the last gap. Anyone who can grant roles at the registry’s scope, such as Owner or User Access Administrator, can grant themselves AcrPush. Those roles are therefore Tier 0 too: held through PIM, used from PAWs, and every role assignment change on the registry raises an alert. Built-in Azure Policy definitions can deny the common regressions at the Tier 0 management group, such as re-enabling export, the local admin account or anonymous pull.
Microsoft’s documentation differs on whether Defender for Cloud needs the trusted-services bypass to scan a private registry. Keep the bypass off and confirm that scan results still appear before relaxing anything.
If agents stay in containers
Managed DevOps Pools remove the agent image, the registration secret and Docker-in-Docker in one step, which is why they are the recommended route. Some teams still run Azure DevOps agents as pods on AKS. Microsoft’s own Docker sample is a reasonable starting point for a lab, but it downloads the agent at start, keeps its registration token on disk and, on AKS, mounts the node’s Docker socket. All four of the fixes below are needed before it runs Tier 0 jobs.
Select any box or a dark control tag to see what it does, which controls apply and how to verify them.
A1Bake a pinned agent into the imageContainerized agents · documented capability + design recommendation
Microsoft’s sample start.sh downloads the latest agent package every time the container starts, so the signed image is not the code that runs. Download and SHA256-verify a pinned agent version at image build. Automatic agent updates are on by default, so turn them off for the pool and rebuild the image when a task needs a newer agent.
How to verify
The image contains the agent binaries. start.sh makes no download call. Pool settings show automatic updates off.
A2Keep the registration credential out of the job containerContainerized agents · documented capability + design recommendation
The sample writes the token to a file and keeps it for cleanup at shutdown, so any job on that agent can read it and register a rogue agent. Register in an init container and deregister from a separate controller. With workload identity, list the agent container in azure.workload.identity/skip-containers so only the init container gets the token.
How to verify
kubectl exec into the agent container finds no token file or token environment variable.
A3No Docker socket, no privileged podsContainerized agents · documented capability
Microsoft’s AKS example mounts the node’s Docker socket, and Microsoft warns that this lets code in the container run as root on the host. Docker-based tasks also do not work on AKS 1.19 or later. Build images on the Managed DevOps Pool instead.
How to verify
Pod Security Admission runs in restricted mode on the agent namespace. No pod has hostPath volumes.
A4One job per pod, short-lived credentialsContainerized agents · documented capability + design recommendation
The sample defaults to a PAT. A service principal’s client secret is used only during registration (agent 3.227.1 and later). Run with --once so each pod serves one job, and deregister as soon as the pod exits.
How to verify
A KEDA ScaledJob creates one pod per queued job. Agent registrations disappear within minutes of pod exit.
For daemonless builds, run BuildKit on the Managed DevOps Pool VM rather than inside agent pods. Kaniko’s original repository was archived in June 2025, so do not plan new work around it.
The control set
Fourteen controls make up the target state, plus the four agent fixes above if containerized agents stay. Each is tagged C for a documented Microsoft capability or L for a design recommendation inferred from documented behaviour, and each carries a check you can run to prove it is in place.
C1Tier 0 accounts sign in only from PAWsPeople and devices · design recommendation
Conditional Access limits Tier 0 accounts to PAW sessions. The PAW authors code and approves runs; it never builds images or installs packages.
How to verify
A Conditional Access policy targets the Tier 0 accounts and requires the PAW device filter or a compliant PAW. Sign-in logs show no Tier 0 sign-ins from other devices.
C2Branch policy on main enforces a second reviewerPipeline · documented capability
Require a minimum number of reviewers, turn off “Allow requestors to approve their own changes”, turn on “Prohibit the most recent pusher from approving their own changes”, and reset all approval votes when new changes are pushed.
How to verify
The main branch policies show those four settings. A test PR cannot be completed by its author.
C3Checks on Tier 0 pools and the service connectionPipeline · documented capability
Branch control allows only refs/heads/main, a required template check forces the approved pipeline shape, and an approval check stops approvers from approving their own runs. Pipeline permissions are granted per pipeline, not to all pipelines.
How to verify
Approvals and checks on each Tier 0 agent pool, the WIF service connection and the Tier 0 environment list all three checks. A run from a feature branch is blocked.
C4Managed DevOps Pools: stateless, VNet-injected, no pool identityBuild · documented capability + design recommendation
“Fresh agent every time” gives each job a new VM that is discarded afterwards. Each pool has its own delegated subnet. No managed identity is attached to any pool, so a job cannot pick up credentials through IMDS.
How to verify
Pool agent state is Stateless. Pool identity is None. Each pool’s subnet is delegated to Microsoft.DevOpsInfrastructure/pools and used by no other pool.
C5Internet egress and write credentials never share a jobBuild · design recommendation
Acquire has internet egress and may write only the catalog. Build has no internet and no write credentials. Publish has no internet and holds the WIF token for the Tier 0 registry. Each job runs on its own fresh VM.
How to verify
The required template defines the three jobs and their pools. Only the publish and promote jobs reference the WIF service connection.
C6Every input pinned in reviewed source and verifiedBuild · design recommendation
Base images by digest, downloads by SHA256 (plus GPG or vendor signature where published), the agent version and its SHA256. Inputs are verified before they enter the catalog, and builds use catalog inputs only.
How to verify
The repo holds a lock file of digests and hashes. A changed hash fails the acquire job. Build logs show only catalog registry pulls.
C7Push only through workload identity federationIdentity · documented capability + design recommendation
The orchestration identity holds AcrPush on the Tier 0 registry only and is reachable only through a WIF service connection, with no secret. Only the publish and promote jobs use that connection.
How to verify
The identity has exactly one federated credential (the service connection) and one role assignment (AcrPush on the registry). It is not attached to any VM.
C8Tier 0 registry locked to its private endpointRegistry · documented capability + design recommendation
Premium SKU, public network access off, admin user off, export policy disabled, private endpoint only, DNS through a zone group, an NSG allowing 443 only from the runtime and build subnets. Trusted-services bypass off.
How to verify
az acr show reports publicNetworkAccess Disabled, adminUserEnabled false, exportPolicy disabled and networkRuleBypassOptions None. Both A records exist in privatelink.azurecr.io.
C9Sign with a Tier 0 key; deploy by digestIntegrity · documented capability + design recommendation
The publish job signs the pushed digest with a Tier 0 key held in Key Vault (Notation Key Vault plugin) and attaches the SBOM. Deployments reference image@sha256 digests, never tags.
How to verify
notation ls lists a Tier 0 signature for each deployed digest. Kubernetes manifests contain digests.
C10Admission allows only Tier 0-signed digestsIntegrity · documented capability + design recommendation
Self-managed Ratify with Gatekeeper on Tier 0 AKS verifies the Tier 0 signature before admission. The AKS Image Integrity add-on is still in preview.
How to verify
Deploying an unsigned or Tier 2-only-signed image to the Tier 0 cluster is denied.
C11Promotion gate for Tier 2 application imagesIntegrity · documented capability + design recommendation
Verify the Tier 2 signature and provenance, scan, copy by digest including referrers, re-sign with the Tier 0 key. Do not use az acr import: it needs the Importer role on both registries and the trusted-services bypass on every network-restricted registry involved.
How to verify
Promote job logs show verify, scan, copy-by-digest and sign steps. No Tier 0 identity holds Container Registry Data Importer and Data Reader.
C12Azure DevOps admins are Tier 0 accountsPeople and devices · design recommendation
Organization, project, agent pool and service connection administrators can edit every check above, so they are dedicated Tier 0 accounts restricted to PAWs.
How to verify
Project Collection Administrators and the relevant admin groups contain only dedicated Tier 0 accounts. Membership changes raise alerts.
C13Registry and pipeline audit logs reach SentinelOperate · documented capability
ContainerRegistryRepositoryEvents and ContainerRegistryLoginEvents go to Log Analytics, alongside the Azure DevOps audit stream, firewall logs and pool logs.
How to verify
Both tables return rows in the workspace. Analytics rules for unexpected pushes, deletes and role assignment changes are enabled.
C14Per-subnet egress allowlistsOperate · documented capability + design recommendation
Managed DevOps Pools documents its required egress, including *.queue.core.windows.net and Ubuntu package endpoints. Allow only the documented Managed DevOps Pools and Azure DevOps endpoints, plus pinned vendor FQDNs for acquire. Never allow github.com or *.blob.core.windows.net broadly.
How to verify
Firewall rule collections exist per subnet. Denied-traffic logs from the build subnet stay at zero during normal runs.
Sources and further reading
Claims above are based on Microsoft’s published documentation, checked on October 13, 2026. Review the linked guidance for current product behaviour before you rely on it.
Trust model and PAW
Azure DevOps pipeline controls
Agents
- Managed DevOps Pools overview
- Managed DevOps Pools: configure scaling (stateless agents)
- Managed DevOps Pools: configure networking
- Managed DevOps Pools: configure images
- Run a self-hosted agent in Docker
- Azure Pipelines agents: agent version and upgrades
- Register an agent using a service principal
- Microsoft Entra Workload ID with AKS
Supply chain, signing and admission
Registry
- Connect privately to a container registry with Private Link
- Disable export of artifacts from a container registry
- Import container images to a container registry
- ACR repository permissions with Azure ABAC
- Azure Policy built-in definitions for Container Registry
- Defender for Containers: network access and permissions
- Monitor Azure Container Registry
