Azure Pipelines Resource Checks: Gate Production Access Outside YAML
Protect Azure Pipelines releases with resource-owned approvals, branch checks, artifact evaluation, timeouts, and exclusive locks.
In Azure DevOps Services, an Azure Pipelines stage can be prevented from consuming production resources until external controls are satisfied. Resource-owner approvals and checks are configured on protected resources rather than in pipeline YAML. That creates a useful separation: a repository change can describe a deployment, but a contributor who can edit that YAML cannot simply delete a resource owner’s environment approval from the same commit. A stage-scoped exclusive lock is a separate mechanism configured with the YAML lockBehavior property; this article distinguishes it from a resource-scoped Exclusive lock check. The cited behavior is documented for Azure DevOps Services; do not assume Azure DevOps Server has identical checks or lifecycle behavior without verifying its version-specific documentation.
This is not a substitute for identity governance or review. An administrator who can edit the protected resource can change its checks, and a permitted bypass remains a privileged operation. The design goal is to make the normal deployment path enforceable outside the code being deployed, while keeping exceptions visible and recoverable.
The check belongs to a resource, not to a YAML stage
Azure Pipelines resources that support checks include environments, service connections, repositories, variable groups, secure files, and agent pools. A resource owner can configure the check through Azure DevOps, and a stage must satisfy all checks on the resources it consumes before that stage begins. One deployment job may consume more than one protected resource, so passing an environment approval does not imply that a service connection or secure file has also been authorized.
Make the boundary concrete. A production environment should represent the system whose deployment history and approval policy you want to govern. A production service connection should have a separate, minimal identity and its own pipeline authorization. Avoid putting unrelated work into the gated stage: if a stage uses a production connection for a harmless reporting task, that task also sits behind the production resource boundary and may broaden what the gate authorizes.
stages:
- stage: Production
dependsOn: Build
condition: succeeded()
lockBehavior: sequential
jobs:
- deployment: DeployApplication
environment: production
strategy:
runOnce:
deploy:
steps:
- script: ./deploy.sh
displayName: Deploy the reviewed artifact
The YAML declares a deployment job that consumes an environment. It does not create the environment’s approval check, branch-control policy, or REST check; those are configured on the resource itself. Lock scope is a separate decision: a stage with lockBehavior gets a stage-scoped exclusive lock, while a resource-scoped lock (for example, to guard stages consuming a shared environment) requires enabling the resource’s Exclusive lock check. These scopes solve different coordination problems; validate the exact pipeline/resource topology instead of assuming that a stage lock protects a resource everywhere it is used.
Know when checks are evaluated
The stage condition and resource checks are separate gates. The stage condition is evaluated first; check evaluation begins after the stage condition is satisfied and the run reaches that stage. A stage that is skipped by its condition will not reach the resource checks. A stage with a passing condition then waits for every applicable check on every resource it uses. A terminal rejection or timeout prevents the stage from executing; the exact result shown in a run can differ by check type, so diagnose the check record rather than interpreting a skipped stage as a successful deployment.
Azure DevOps Services groups checks into five categories. Static checks run first (branch control, required template, and artifact evaluation), followed by pre-check approvals, dynamic checks (manual approval, Azure Function, REST API, business hours, and Azure Monitor alert checks), post-check approvals, and finally the exclusive lock. Within each category, checks run in the order they were created. Repeated dynamic checks can have retry intervals and a timeout; configure them with the remote service’s availability and the stage’s deployment window in mind.
The list of approvers is captured when checks begin. Changing the approver list after a run has started waiting does not update that run’s reviewer set. If an Azure DevOps group is an approver, one member of that group is enough to approve. For a deferred approval, the approver can record a future effective time instead of treating approval time and deployment time as the same event.
Use static checks for facts about the release
Branch control validates the branches associated with the resources linked to the pipeline against an allowed list. Microsoft requires fully qualified branch names such as refs/heads/main; main alone is not the documented format. The check can also require branch protection. Decide explicitly how to handle a protection status Azure DevOps cannot determine, and test both known and unknown states rather than assuming an unavailable status means protected.
A required-template check can require a pipeline to extend from a specified YAML template. This helps keep a common release structure, but it does not prove that every job is correct, that every referenced task is safe, or that the template repository itself is trustworthy. Protect the template repository and pin its reference according to the change-control policy for that organization.
The Evaluate artifact check applies only to container image artifacts in the current documented product behavior. It evaluates available image metadata against the configured policy before a stage uses the environment. It is not a universal file scanner and it does not scan arbitrary ZIPs, build directories, or source trees. If a non-container artifact must be checked, integrate the appropriate external service and make its result a separate, explicit gate.
Dynamic checks and maintenance windows
An Invoke REST API or Azure Function check can query an external decision service before deployment. Treat the request and response format as a protocol: the integration should identify the exact artifact or release, authenticate the caller, return a decision that fails closed on malformed or missing evidence, and expose useful logs without leaking tokens. A timeout is not proof that the artifact is unsafe, but it is also not permission to deploy; it is an unavailable control that needs an owner and a recovery path.
Business-hours checks can align deployment start with an allowed time window. If another check delays a stage until the window has ended, the business-hours approval can be withdrawn and reevaluated for the next window. This is different from a fixed timer inside YAML: it is a resource gate applied to stages that consume that resource. Set a timeout long enough for the intended schedule, and plan for a run that becomes eligible near the end of the window but cannot start in time.
Prevent concurrent releases deliberately
An exclusive lock serializes work in its scope. Azure Pipelines supports runLatest and sequential for lockBehavior. sequential lets runs acquire the lock in sequence. runLatest drops stale contenders; for a stage-scoped lock, Microsoft specifically documents this behavior as limited to runs of the same branch. If a resource lock exists but no behavior is specified, runLatest is the default. Choose based on release semantics: a canary controller may want stale same-branch runs discarded, while a migration stream may require every approved release to run in sequence. Confirm whether the lock is stage-scoped or attached to the protected resource before assuming which competing deployments it coordinates.
Do not use an exclusive lock as a substitute for deployment idempotency. It serializes run requests within its configured scope, either a stage or a protected resource; it does not make a partially completed deploy atomic, stop an operator from changing the system through another path, or provide a database transaction. Keep the deployment safe to retry and maintain rollback or forward-recovery procedures separately.
Configure, test, and troubleshoot the boundary
In the Azure DevOps project, open the protected resource and use its Approvals and checks tab to add the needed controls. Assign approvers who can evaluate the release evidence and give them instructions that name the artifact digest, target, change record, and rollback plan. Limit resource administration and pipeline authorization separately. If an emergency bypass is part of policy, restrict it to resource administrators. Azure DevOps shows who bypassed a check in the checks panel; record the associated change or incident reference in the organization’s release evidence as well.
Test the policy in a non-production environment with both passing and failing cases: an allowed and disallowed branch; protected and unknown protection state; a successful and rejected approval; missing external evidence; a timed-out REST check; a container artifact that passes and one that fails the policy; overlapping runs under each chosen lock behavior. Verify that changing YAML alone cannot remove the resource check. Also test the recovery path: who can update a mistaken check, how a timed-out stage is retried, and how the team knows which artifact was ultimately approved.
If a check appears not to run, first confirm that the stage condition passed and that the stage actually consumes the exact resource carrying the check. Then inspect the checks panel and audit history for that run, resource, and policy. Confirm the run began after the check was configured; checks added after an earlier run has passed its resource boundary cannot retroactively protect that stage. For a timeout, verify both the check’s retry interval and the allowed duration. Do not solve a misconfigured gate by granting the pipeline broader resource access or by moving deployment work into an unprotected stage.
Keep the gate meaningful over time
Approvals work only when they are evidence-based. A prompt that says merely “approve deployment” encourages a click-through ritual; identify the exact release candidate, source revision, test status, operational risk, and rollback strategy. Review who can administer each resource, who can authorize each pipeline, which users can bypass checks, and whether old service connections, secure files, or environments remain accessible.
Finally, audit the path from build to production. The artifact evaluated by a check must be the same artifact the deployment job consumes; rebuilding after approval breaks that relationship unless the new artifact is independently bound to the reviewed digest. Capture the stage, resource, check decision, artifact identity, and deployment result as one traceable record. A well-designed resource gate is not just a pause in a pipeline: it is an enforceable authorization boundary around a specific release.
Related:
- Azure DevOps Services Operating Map: Boards, Repos, Pipelines, Artifacts, and Test Plans
- Azure Pipelines Workload Identity Federation: Secure Service Connections and Migrate the Issuer
Sources: