Terraform Azure DevOps Build and Release pipelines

Georgi Kamacharov
6 min readJan 28, 2021

--

If you use Terraform to manage your infrastructure as code, Azure DevOps for your continuous integration and continuous deployment (CI/CD) needs and you would like to combine the two, then this is the post for you.

Introduction

For the sake of keeping this post concise and to the point, I’m not going to talk about best practices and definitions but rather how I have setup Terraform and Azure DevOps Build and Release pipelines for personal and professional projects. The hope is that this post helps someone trying to do the same as it took me a minute to figure it out.

Overview of all the technologies referenced in this post

  • Terraform — a tool used to manage your (usually cloud) infrastructure as code. It uses HashiCorp (creators of Terraform) proprietary programming language — HCL. The Terraform version is v0.14.2 and Terraform Azure provider version is 2.39.0
  • Terraform commands — basic Terraform commands such as init, plan, apply, etc.
  • Terraform workspaces — you can think of this as environment specific state. For example, there will be a state file for each environment — DEV, QA, STAGE and PROD
  • Azure DevOps build and release pipelines— collaborate tool for teams. In this specific post, we’ll be looking at its build and release pipelines
  • Azure StorageAzure storage account where I store the Terraform state for each specific workspace. More specifically, blob storage
  • Azure KeyVaultservice primary used for storing sensitive information such as secrets

Assumptions

  • You know Terraform file extensions, structures and what they’re used for (main.tf, variables.tf, etc.)
  • You know basic Terraform commands and what they do
  • You know what Terraform state is and how Terraform uses it
  • You know what credentials Terraform needs in order to authenticate in Azure (subscription id, tenant id, client id and client secret)

Terraform

Let’s go over how I have my Terraform setup. For this demo, let’s take a look at the following scenario — you want to create an Azure Resource Group via Terraform. Thus, your main.tf file looks something like this.

main.tf

Where the variables.tf file contains the following variables based on the current workspace (remember, I currently have 4 workspaces declared — dev, qa, stage and prod).

variables.tf

Note that the location and env variables are of type map (dictionary) where each key corresponds to each workspace I have defined.

Additionally, I have backends.tf file where I declare the definition of the azurerm backend and where the remote state files are stored.

backends.tf
  • resource_group_name — resource group of the Azure Storage account
  • storage_account_name — the name of the Azure Storage account where the state files are
  • container_name — the name of the Azure Storage account (blob) container where the state files are

The remote state files are in the format of tf/terraform.tfstateenv:{env} where {env} is each workspace and prefix tf/ is the folder name. Below are examples of dev and qa. Note: if you’re starting from scratch and create a new workspace, then these are automatically created for you and you don’t have to worry about them.

Azure Storage — blob container storage of remote state files

That is my whole Terraform setup. Now it is time to dive into the Azure DevOps Build and Release pipelines to see how we can CI/CD the process.

How to setup your Azure DevOps build pipeline

Now that you have your Terraform scripts, it’s time to take a look at the actual Azure DevOps Build pipeline.

It should be simple and to the point. The output of it needs to be the Terraform scripts published as an artifact so they can be used in the release pipeline where the bulk of the work is done. This is what my azure-pipelines.yml file looks like.

azure-pipelines.yml

As you can see, there are 3 steps in the process. The 1st one runs the Terraform command validate — this ensures that the scripts are valid and can be published. The 2nd command simply copies the Terraform scripts to the staging directory. Finally, the 3rd command publishes the scripts as an artifact. A typical build run would look like this.

Azure DevOps Terraform Build run

That’s all you need in terms of the Azure DevOps Build pipeline. Now let’s take a look at the Release pipeline.

How to setup your Azure DevOps release pipeline

Now that you have your Build pipeline, it’s time to take a look at the Release pipeline where the infrastructure will be created.

For this demo, we’ll be looking at dev Release. At a high level view, this is what that looks like in Azure DevOps.

Azure DevOps Terraform Release

There is 1 job and 8 tasks in it. Let’s dive in.

Azure DevOps Terraform Release tasks
Azure DevOps Terraform Release variables (env=dev)

Tasks

Azure DevOps Terraform Release task 1
  1. Connect to Azure KeyVault where secrets are stored — such as subscription id, tenant id, client id, client secret and more. These secrets can then be referenced anywhere in the pipeline using the format $(SECRET_NAME).
Azure DevOps Terraform Release task 2

2. Write the secrets taken from Azure KeyVault to a local to the pipeline terraform.tfvars file.

Azure DevOps Terraform Release task 3

3. Install Terraform v0.14.2

Azure DevOps Terraform Release task 4

4. Run the init Terraform command. In the passed in arguments, you will see the $(StorageAccountKey) reference. This is the Azure Storage account key stored in Azure KeyVault (which was connected to a couple of tasks before).

Azure DevOps Terraform Release task 5

5. Run the workspace select command based on the $(env) pipeline variable, in this case dev.

Azure DevOps Terraform Release task 6

6. This one was the tricky one. Even though I was running terraform workspace select dev the pipeline was still unable to pick up the correct remote state file. Thus, I added this step where it writes the $(env) variable’s value, in this case dev, to the local Terraform environment file.

Azure DevOps Terraform Release task 7

7. Run the plan Terraform command

Azure DevOps Terraform Release task 8

8. Run the validate and apply Terraform commands

Conclusion

It’s a lot of steps to follow but this is the only way I found to make using Terraform in Azure DevOps Build and Release pipelines work. These limitations are not only because of the tool but also the Terraform extension that you need to install and use in Azure DevOps. It’s currently limited to what it can do.

Full details

IaC (Terraform) source code where these examples were taken from

IaC (Terraform) source code jump to

--

--