Azure Pipelines & Infrastructure as Code
Azure DevOps Pipelines runs multi-stage build and release pipelines for any language on Microsoft-hosted or self-hosted agents. Terraform and Bicep describe Azure infrastructure in code so environments are reproducible. Learn how a CI/CD pipeline and IaC toolchain fit together on Azure.
What is this?
Azure Pipelines is the CI/CD engine of Azure DevOps: a YAML or classic pipeline defines stages, jobs and steps that run on Microsoft-hosted agents or your own self-hosted agents, from build through test to deployment. On the infrastructure side, Terraform and Bicep treat Azure resources as code — Bicep is Azure's domain-specific language compiled to ARM templates, while Terraform is the cross-cloud standard. Both enable reviewable, repeatable environments.
Why do you need it?
- Pipelines turn 'it works on my machine' into a tested, reviewed, auditable release path.
- IaC makes environments reproducible — the same code creates dev, staging and production.
- Bicep and Terraform diff your intended state against reality, so drift is visible and fixable.
- Interviewers almost always ask how you ship code and infrastructure safely and repeatably.
- Both are force multipliers: a single pipeline change updates every release.
Mental model
A recipe card and a test kitchen
IaC and Azure Pipelines
The recipe card (Bicep or Terraform) lists every ingredient and step to produce the exact dish (environment) every time. The test kitchen (CI/CD pipeline) runs the recipe end to end, tasters (tests) sample every course, and only approved recipes go to the dining room (production). Because the recipe is versioned, you can replay a past dinner or branch a new menu without risk.
How it works
- A YAML pipeline defines trigger, stages (Build, Test, Deploy), jobs and steps; agents execute them.
- Build stages compile, run tests and publish artifacts (or container images to ACR).
- Release stages download artifacts and apply them with approvals and gates between environments.
- Bicep files declare Azure resources and parameters; az deployment group create compiles and applies them.
- Terraform uses a state file to plan and apply changes, so the cloud always matches your code.
Pipeline stages driving infrastructure and application deployment.
Key commands
az pipelines run --branch mainQueue a pipeline run from the main branch.
az pipelines runs list --status completedList completed pipeline runs.
terraform initInitialize Terraform providers and the backend.
terraform plan -out=tfplanCompute the diff between code and current infrastructure.
terraform apply tfplanApply the planned changes.
az deployment group create --resource-group rg-devops --template-file main.bicep --parameters env=stagingApply a Bicep template to a resource group.
Examples
Run a build and apply infrastructure with Terraform
$ az pipelines run --branch mainBuild #20260809.1Started by: devops-bot $ terraform initInitializing the backend...Terraform has been successfully initialized! $ terraform plan -out=tfplanTerraform will perform the following actions: + azurerm_resource_group.rg name = "rg-devops" Plan: 1 to add, 0 to change, 0 to destroy. $ terraform apply tfplanazurerm_resource_group.rg: Creating...azurerm_resource_group.rg: Creation completeApply complete! Resources: 1 added, 0 changed, 0 destroyed.az pipelines run queues a build from main. terraform init prepares the provider and backend, plan computes the diff between code and reality, and apply executes it — creating the resource group. The same three commands replay in every environment, which is the entire point of IaC.
In production
- Run a multi-stage pipeline that builds, tests, plans IaC, and applies with an approval gate before production.
- Store IaC in the same repo as the app so infrastructure changes review like code changes.
- Use Bicep for Azure-only stacks and Terraform when you manage multi-cloud or existing tooling.
- Keep state and plan outputs in a locked backend so two pipelines cannot apply conflicting changes.
- Use environments in Pipelines to gate deploys, hold approvals, and surface deployment history.
Best practices
- Run terraform plan in CI and apply only after human or automated gate approval.
- Use workspaces or per-environment directories so dev, staging and prod state never mix.
- Make pipelines hermetic: pin versions of tools, images and extensions.
- Fail the build on lint and format checks (terraform fmt, bicep build) before applying anything.
- Store all secrets in Key Vault / variable groups referenced by the pipeline, never inline.
Common mistakes
- Letting pipelines apply straight to production with no approval or drift check.
- Sharing one Terraform state across environments, so dev and prod fight over the same resources.
- Using latest tags for tools and images, breaking builds on unexpected upstream changes.
- Checking the state file or .tfstate into git — a leak of everything you own.
- Editing cloud resources by hand and letting IaC drift until the next apply fails catastrophically.
Common issues
Terraform apply fails on a resource that already existsThe resource was created outside Terraform, or state is stale and drift is unmanaged.
Run terraform state pull to inspect, import the resource with terraform import, or re-plan after reconciling the difference.
Pipeline job hangs waiting for a self-hosted agentNo matching self-hosted agent is online and labeled for the job's demands.
Start the agent or change the job to a Microsoft-hosted agent pool, and align the demands with what the agent advertises.
az deployment group create rejects the Bicep templateA syntax or type error, or a parameter referenced but never passed.
Run bicep build locally to surface the compile error, then pass all required parameters with --parameters.
Debugging tips
- Re-run the failing pipeline stage with the same inputs — flaky stages are usually missing dependency pins.
- For Terraform, compare the plan against the actual state to see whether drift or a typo caused the diff.
- Read the first failing step, not the last green one — pipeline failures cascade.
- Check the agent log for 'denied' messages that reveal permission gaps before the apply stage.
- Use az pipelines runs show to pull the run and stage status after a failed deployment.
Interview checkpoint
Hands-on lab
- Write a declarative description of an environment
- Compute a plan by diffing against the current state
- Apply the plan to update the environment
- Detect drift and fix it back to the declared state
Simulate a Plan-Apply-Validate IaC Cycle
20 min · intermediate
Quick quiz
Which command shows the diff between Terraform code and current infrastructure?
Bicep compiles to ARM templates that Azure applies.
Two engineers run terraform apply at the same time. What prevents a corrupt state file?
Cheat sheet
StagesBuild, test, deploy phasesJobsUnit of work on an agentStepsCommands or tasks in a jobArtifactsDeployable outputsApprovalsGates between environmentsBicepAzure DSL, compiles to ARMTerraformCloud-agnostic, state + planterraform initProviders + backend setupterraform planShow the diffterraform applyMake reality match code