Using Template Libraries for Build and Deploy Stages in Azure DevOps Pipelines

Using Template Libraries for Build and Deploy Stages in Azure DevOps Pipelines

Picture this: one client I worked with had a 500-line azure-pipelines.yml—build here, deploy there, all tangled up. Another had five repos with copied-and-pasted stages—change one variable, fix it everywhere. Sound familiar? It’s a maintenance nightmare. Drift creeps in, errors pile up, and scaling becomes a slog.

Having Git repositories with dedicated and parameterised build and deploy templates in Azure Repos, fixes that.

Stick your templates—build, deploy, whatever—in one place, one Azure Repos git repo. Update once, and reuse them across features and other Azure Repos git repositories.

I’ve run hundreds of stages this way for big names. It’s not just tidy; it’s faster. You’re not rewriting the same deploy logic for dev, test, and prod. Let’s have a look how we can achieve this easily.

How to Make It Work

Here’s the nuts and bolts—three steps to get your build and deploy stages living in an external repo in Azure Repos, that gets pulled in for your multiple feature repo pipelines that need them.

Step 1: Setup the Repo

Create a new Git repo—call it templates-repo—on Azure Repos. This is your library. Inside, add two files:

  • build.yml for your build stage.
  • deploy.yml for your deploy stage.

Keep it simple to start—I’ll show you examples next.

Step 2: Write the Templates

These are parameterised YAML pipeline files—flexible, reusable. Here’s what they might look like:

build.yml:

parameters:
  - name: buildConfig
    default: Release
stages:
  - stage: Build
    jobs:
      - job: BuildJob
        steps:
          - script: echo Building ${{ parameters.buildConfig }}
            displayName: Build Step
          - script: |
              # Build here and copy output to Build.ArtifactStagingDirectory
              echo "Replace this with your build command (e.g., dotnet build)"
              cp -r ./output/* $(Build.ArtifactStagingDirectory)/
            displayName: Build and Copy Artifacts
          - task: PublishBuildArtifacts@1
            displayName: Publish artifacts
            inputs:
              PathtoPublish: $(Build.ArtifactStagingDirectory)
              ArtifactName: drop
              publishLocation: Container

This builds with a config (e.g., Release or Debug) you pass in. You can extend the steps to your liking.

Next, we have our deploy.yml template, again parameterised this time with an envName. You can add as many other parameters as required. And even have separate deploy.yml templates. Think docker-deploy.yml, dotnet-deploy.yml etc, you get the idea.

deploy.yml:

parameters:
  - name: envName
    default: dev
stages:
  - stage: Deploy_${{ parameters.envName }}
    jobs:
      - deployment: DeployJob
        environment: ${{ parameters.envName }}
        strategy:
          runOnce:
            deploy:
              steps:
                - script: echo Deploying to ${{ parameters.envName }}
                  displayName: Deploy Step

This deploys to an env (e.g., dev, prod) you specify. Parameters make this all portable and repeatable.

Step 3: Reference in Your Pipeline

In your main project repo, tweak azure-pipelines.yml to pull these in:

resources:
  repositories:
    - repository: templates
      type: git
      name: templates-repo  # Replace with your repo name
stages:
  - template: build.yml@templates
    parameters:
      buildConfig: Release
  - template: deploy.yml@templates
    parameters:
      envName: dev
  - template: deploy.yml@templates
    parameters:
      envName: prod

The example above highlights how the deploy.yml template can be reused for the dev and prod deploy stage. They both use the same build artifact and deploy this to the relevant environment.

It’s easy to see how the parameters for both the build.yml and deploy.yml stage can be tweaked and extended to suit your use case(s).

The resources.repositories block links your existing repo—templates git repo in Azure Repos is just an alias. Then @templates calls the files, passing parameters. Run it—builds in Release, deploys to prod. Swap envName to dev, and you’re golden. One repo, infinite uses. And it’s easy to extend your templates with sensible defaults.

Why This Wins

I’ve been automating pipelines for 15 years—external templates and thinking about parameterisation and re-usability and adopting your approach to cater for this mechanism is a game changer.

For the World Health Organisation, I created CruiseControl.NET XML templates that were highly parameterised.

And these days, with Azure DevOps, it’s easier than ever. But unless you’ve gone through the pain, it’s hard to appreciate the work required to have solid CI/CD pipeline templates in place that can be reused easily without breaking other pipelines or causing an administrative mess where autonomous teams just copy and paste without thinking.

Months of tinkering saved—dev teams could focus on code, not YAML. Pair it with Bicep for infra, and you’ve got a system—scalable, readable, done.

Stay tuned for more practical tips here and ready-baked templates you can use.