Using templates in AzureDevOps pipeline
One of the biggest challenges in maintaining Infrastructure as Code (IaC) pipelines is avoiding repetition. If you have five environments (Dev, QA, Staging, Prod-A, Prod-B), copying the 50-line Terraform Plan logic five times is a recipe for error.
The solution would be Azure DevOps YAML Stage Templates.
This post breaks down a powerful, reusable, and secure pattern for running Terraform operations that only proceeds to the deployment stage if actual infrastructure changes are detected.
The main pipeline file azure-pipelines.yml serves as the “Table of Contents.” It sets global variables and, crucially, defines the stages by referencing templates. It passes dynamic information (like the working directory) to the reusable templates using parameters.
Key Points:
DRY Principle: We call a template, we don’t duplicate the steps.
Variable vs. Parameter: We use
$(tfWorkingDir)(a variable) in the main file and pass its value to the template using thetfWorkingDirparameter.Stage Dependency: The
Deploystage explicitly setsdependsOnStage: Plan, which we use in the template to enforce order.
Variables ($() syntax)
Variables are typically processed during the runtime of the pipeline (after the compilation/template expansion phase).
They are used for values that might change during the job execution (like iteration counts, file paths, or values set by previous tasks).
They are referenced using the
$(variableName)syntax.$(tfWorkingDir)is defined in thevariables:block of the mainazure-pipelines.yml. It’s a pipeline-level setting that remains constant throughout the run, but YAML treats it as a runtime variable.
Parameters (${{ }} syntax)
Parameters are evaluated and substituted before the pipeline starts running, during a process called template expansion (or compilation). They are compile-time concepts.
They are essential for defining reusability and controlling the structure of the pipeline. They allow you to dynamically set job names, stage names, conditions, and other structural YAML elements.
We cannot directly use a runtime variable ($(tfWorkingDir)) to define a compile-time structural element (like the workingDirectory of a task input within the template) because the compiler needs to know the final structure of the pipeline before any variables are resolved at runtime.
The process works like this:
Main File (
azure-pipelines.yml): Passes the value of the variable$(tfWorkingDir)(which is 'app-service-and-sql-database') as an argument to the template’s parameter block:YAML
# Main File
- template: templates/terraform-plan-stage.yml
parameters:
tfWorkingDir: '$(tfWorkingDir)' # Passes the VALUE 'app-service-and-sql-database'
Template Expansion: Azure DevOps takes the value '
app-service-and-sql-database'and substitutes it everywhere the parameter${{ parameters.tfWorkingDir }}is used in the template.Final Pipeline: The resulting, expanded YAML file (which is what actually runs) now has the hardcoded path where the parameter was used. This ensures the structural parts of the pipeline (like the artifact path or job inputs) are correctly defined before execution.
The Plan Template:templates/terraform-plan-stage.yml
This template defines the entire Plan Stage logic, including initialization, planning, and, most critically, detecting changes.
A. Parameters Block
The template declares the inputs it expects from the orchestrator:
parameters:
- name: stageName
type: string
# ...
- name: tfWorkingDir
type: string
Conditional Output Variable
The magic of conditional deployment happens here. We use a PowerShell task to execute terraform show -json tfplan, analyze the output for any resource changes (create, update, delete), and then set an output variable:
# Inside the PowerShell task:
script: |
# ... logic to determine $anyTfChanges ...
Write-Host “##vso[task.setvariable variable=anyTfChanges;isOutput=true]$anyTfChanges”
name: ‘SetChangesVariable’
This template runs the terraform apply command, but only if the previous Plan stage was successful and detected changes.
The Deploy Template: templates/terraform-deploy-stage.yml
This template runs the terraform apply command, but only if the previous Plan stage was successful and detected changes.
Code is published on my GitHub repo and code is already explained in my previous post,if you’re interested,check bellow text.


