skip to content

Deployment: Environments & Approvals

Deployment jobs targeting an environment, with checks that must pass before the job runs and a strategy that controls how the rollout proceeds. This is where Azure DevOps encodes change control, so compliance-minded interviewers go straight here.

on this pageshow

explore

questions

6

In an Azure Pipelines YAML pipeline, what does declaring a job as `deployment:` with an `environment:` give you that a plain `job:` does not?

level: middleimportance: must knowfreq 68%

answer

  1. a job with a declared target
  2. the platform records where it went
  3. steps sit under a strategy hook
  4. gates live on the object, not the YAML
  5. runOnce, rolling, canary

basics

~20 s

A deployment job targets a named environment, so Azure Pipelines records deployment history against it, enforces that environment's approvals and checks before the stage runs, and lets you choose a runOnce, rolling or canary strategy.

solid answer

~50 s

A plain `job:` is just steps on an agent. A `deployment:` job binds those steps to an Azure DevOps **environment**, which is a first-class object in the project, and three things come with that binding. First, **traceability**: the environment page lists every deployment to it, with the commits and work items each one carried. Second, **gating**: approvals and checks configured on that environment are evaluated before the stage containing the job is allowed to start. Third, **strategy**: the steps no longer sit under `steps:`, they sit under a lifecycle hook of a `runOnce`, `rolling` or `canary` strategy, so the platform drives how the rollout proceeds. The environment can also hold Kubernetes namespace or virtual-machine resources that the job targets. Referencing an environment name that does not exist creates it automatically, with no resources and no checks on it.

code

yaml · 13 lines
yaml
stages:
- stage: Deploy
  jobs:
  - deployment: DeployWeb
    environment: production
    pool:
      vmImage: ubuntu-latest
    strategy:
      runOnce:
        deploy:
          steps:
          - checkout: self
          - script: ./scripts/deploy.sh

go deeper

for a junior

Know that a deployment job names an environment and that its steps go under a strategy hook such as runOnce.deploy.steps, not under a plain steps: list.

for a middle

Be ready to list what the environment binding buys — deployment history with commits and work items, checks that gate the stage, resource targeting — and to explain the checkout and artifact-download defaults.

for a senior

Show you know the gate lives on the environment object rather than in the YAML, and name the auto-creation trap: a mistyped environment name yields an ungated deployment that still reports success.

for a principal

Own the question of which environments exist at all and who may create them. Argue for pre-created, permission-scoped environments so the deployment history is a trustworthy audit record rather than a pile of one-off names.

## The two job types Azure Pipelines YAML has two kinds of job. A regular `job:` is a unit of work that runs steps on an agent from a pool. A `deployment:` job is the same execution unit with a declared *target*: the `environment:` property. That one property is the hinge of Azure DevOps' change-control model. ## Steps move under a strategy The most visible difference is structural. A deployment job has no top-level `steps:`. Instead it has a `strategy:`, and the steps live inside one of the strategy's lifecycle hooks: ```yaml jobs: - deployment: DeployWeb environment: production pool: vmImage: ubuntu-latest strategy: runOnce: deploy: steps: - script: ./scripts/deploy.sh ``` `runOnce` is the simplest strategy: run the hooks once. `rolling` repeats them over batches of virtual-machine resources, and `canary` repeats them over percentage increments. Writing `steps:` directly under `deployment:` is a schema error, and it is the most common first mistake. ## The environment is a real object An environment is not a string label like a Git branch name. It is a project-level object with its own page under **Pipelines > Environments**. It holds: - **Deployment history** — every run that deployed to it, in order, with the commits and work items each deployment included. This is the audit trail an auditor or an incident responder asks for: *what is running in production right now, and which change put it there?* - **Resources** — optionally a Kubernetes namespace or a set of registered virtual machines, so the job can target concrete infrastructure and the page can show the live workloads or machines. - **Approvals and checks** — the gate list described below. - **Security** — which pipelines and which users may use it, including an "open access" toggle versus explicit pipeline permissions. If you reference an environment name that does not exist yet, Azure Pipelines creates it on the fly with no resources. That is convenient and also a trap: an auto-created environment has **no** checks on it, so a typo in the environment name silently produces an *ungated* deployment that goes straight through. Locking down who may create environments, and pre-creating the ones that matter, is the standard mitigation. ## Checks gate the stage When a stage contains a deployment job whose environment carries checks, the run pauses at the *start of that stage* until every check passes. No agent is consumed while it waits. The gate is attached to the environment, not written into the pipeline YAML, so a pipeline author cannot remove it by editing the file — which is exactly why compliance-minded teams like this model. ## Defaults that differ from a regular job Two defaults surprise people. A deployment job **does not check out the source repository** — add `- checkout: self` if a step needs repo files. And it **does automatically download** the pipeline artifacts published earlier in the same run, which you suppress with `- download: none`. The reasoning is that a deployment consumes an already-built artifact rather than rebuilding from source, which is the build-once-deploy-many discipline expressed as a default. ## Targeting resources When the environment holds resources, the job can name one: ```yaml environment: name: production resourceType: VirtualMachine tags: web ``` With VM resources, the machines themselves run the agent, so no `pool:` is given; the job fans out across every matching machine. With a Kubernetes resource, the resource carries the cluster service connection and namespace, so manifest tasks target it without a separate connection. ## When you do not need one Build, test and lint jobs should stay plain `job:` entries. A deployment job for work that does not change a running environment adds noise to the deployment history and buys nothing. Reach for `deployment:` exactly when the job is the thing that changes what is running somewhere.

  • What happens if the environment name in your YAML has a typo?
    Azure Pipelines creates a brand-new environment with that name, no resources and no checks, and the deployment proceeds ungated. It looks like a successful production deploy while bypassing every approval. Restrict who may create environments and pre-create the real ones so a typo fails loudly rather than sailing through.
  • Can two deployment jobs in the same stage target the same environment?
    Yes. The checks on that environment are evaluated once for the stage, not once per job, so approvers see a single approval request rather than two. That is worth knowing when you split a deployment into several jobs and wonder why you were only prompted once.
  • Why does a deployment job download artifacts automatically but not check out the repository?
    Because a deployment is meant to consume the artifact built earlier in the run rather than rebuild from source — build once, deploy many. The default encodes that discipline. Add `- checkout: self` if a step genuinely needs repository files such as deployment scripts, or `- download: none` to skip the automatic artifact download.

saying these in an interview costs you the question

  • Thinking an environment is just a label string
  • Putting steps: directly under deployment: in the YAML
  • Believing approvals are written into the pipeline YAML
  • Assuming a deployment job checks out the repo like a normal job
  • Using deployment jobs for build and test work

context

open as a page

In Azure DevOps, when during a YAML pipeline run are the approvals and checks on a protected resource evaluated, and what is the run doing while they are pending?

level: seniorimportance: must knowfreq 60%

basics

~20 s

Checks are evaluated at the start of any stage that consumes the protected resource. The stage queues no jobs and holds no agent while it waits; every check on every resource the stage uses must pass before any job starts.

open as a page

In an Azure Pipelines deployment job, which lifecycle hooks can a strategy define, and when does each one run?

level: middleimportance: should knowfreq 45%

basics

~10 s

Azure Pipelines deployment strategies expose preDeploy, deploy, routeTraffic and postRouteTraffic hooks, run in that order, plus on: success and on: failure hooks that run last depending on the outcome.

open as a page

What does configuring an Azure DevOps Azure Resource Manager service connection with workload identity federation change, and how does Microsoft Entra ID decide to trust it?

level: seniorimportance: should knowfreq 48%

basics

~20 s

No secret is stored in Azure DevOps. The pipeline presents a short-lived token that Entra ID exchanges for an access token, trusting it because a federated identity credential matches the token's issuer, subject and audience.

open as a page

Azure DevOps lets you attach checks to environments, service connections, agent pools, repositories, variable groups and secure files. How would you decide which of those resources should carry a given gate?

level: principalimportance: should knowfreq 33%

basics

~20 s

Attach the gate to the resource that is the thing you actually want to restrict access to. Gate the credential on the service connection, the destination on the environment, and shared build capacity on the agent pool.

open as a page

An Azure Pipelines `deployment:` job runs a script stored in the repository and fails with a file-not-found error, yet the identical step works in a regular `job:`. What is the cause?

level: juniorimportance: nice to knowfreq 36%

basics

~10 s

Deployment jobs do not check out the source repository automatically the way regular jobs do, so the working directory has no repo files. Add an explicit - checkout: self step before the script.

open as a page