Azure DevOps Terraform Task

Introduction

As development teams implement more and more Infrastructure as Code (IaC) leveraging Azure DevOps(ADO) there is a need for to ADO tasks that integrate easily and help improve the software development process. This post will specifically focus on the Azure DevOps Terraform Task

The Problem

With growth in popularity on Azure DevOps Microsoft Hosted Agents we need the ability to consistently deploy software on machines that organizations don’t maintain and managed. Because of this there needs to be task(s) to install/validate that the require software for build/deployment is configured correctly on the hosted agent.

An additional ask would be feature/functionality that easily integrates into ADO and provides for a better overall developer experience.

The Azure DevOps Implementation

The Azure DevOps Terraform Task does both of these. First installing Terraform can be configured to a specific version passed in at build:

parameters:
- name: terraformVersion
  type: string
  
steps:
  - task: ms-devlabs.custom-terraform-tasks.custom-terraform-installer-task.TerraformInstaller@0
    displayName: install terraform
    inputs:
        terraformVersion: ${{ parameters.terraformVersion }}

This ensure that the correct version of Terraform is installed on the Microsoft hosted agent. After this we can use the additional commands via TerraformCLI@0.

For instance, to initialize Terraform against an azurerm backend.

parameters:
- name: serviceName
  type: string
- name: TerraformDirectory
  type: string
- name: AzureSubscriptionServiceConnectionName
  type: string
- name: TerraformStateStorageAccountResourceGroupName
  type: string
- name: TerraformStateStorageAccountName
  type: string
- name: TerraformStateStorageAccountContainerName
  type: string
  
steps:
  - task: TerraformCLI@0
    displayName: 'Terraform : init'
    inputs:
      command: init
      backendType: azurerm
      workingDirectory: ${{ parameters.TerraformDirectory }}
      backendServiceArm: ${{ parameters.AzureSubscriptionServiceConnectionName }}
      backendAzureRmResourceGroupName: ${{ parameters.TerraformStateStorageAccountResourceGroupName }}
      backendAzureRmStorageAccountName: ${{ parameters.TerraformStateStorageAccountName }}
      backendAzureRmContainerName: ${{ parameters.TerraformStateStorageAccountContainerName }}
      backendAzureRmKey: ${{ parameters.serviceName }}.tfstate

Then a plan:

parameters:
- name: TerraformDirectory
  type: string
- name: AzureSubscriptionServiceConnectionName
  type: string
- name: commandOptions
  default: '-out=$(System.DefaultWorkingDirectory)/terraform.tfplan -detailed-exitcode'
- name: additionalParameters
  type: object
  default: []

steps:
    - task: TerraformCLI@0
      displayName: 'Terraform : plan'
      inputs:
        command: plan
        workingDirectory: ${{ parameters.TerraformDirectory }}
        publishPlanResults: ${{ parameters.AzureSubscriptionServiceConnectionName }}
        environmentServiceName: ${{ parameters.AzureSubscriptionServiceConnectionName }}
        commandOptions: ${{ parameters.commandOptions }}

Take note on this one of the publishPlanResults and the commandOptions. We will show what these translate to later.

Finally, the apply:

parameters:
- name: TerraformDirectory
  type: string
- name: AzureSubscriptionServiceConnectionName
  type: string
- name: additionalParameters
  type: object
  default: []
  
steps:
- task: TerraformCLI@0
  displayName: 'Terraform : apply'
  condition: and(succeeded(), eq(variables['TERRAFORM_PLAN_HAS_CHANGES'],'true'))
  inputs:
    command: apply
    workingDirectory: ${{ parameters.TerraformDirectory }}
    commandOptions: '$(System.DefaultWorkingDirectory)/terraform.tfplan'
    environmentServiceName: ${{ parameters.AzureSubscriptionServiceConnectionName }}

Notice a condition on this apply checking against TERRAFORM_PLAN_HAS_CHANGES this is a variable created by the plan command and writes back if the Terraform will actually make any infrastructure changes. In the scenario where IaC is stored and deployed with application code (which I strongly recommend) this will skip the Terraform apply step if there aren’t any changes to apply.

Here is a screenshot confirming the apply was skipped in the case of no changes being detected:

The Azure DevOps Experience

In addition to providing the ability to skip over the apply step if no changes have occurred another key thing this task provides is the publishing of the plan directly to ADO.

Notice we have a new tab on the ADO pipeline called ‘Terraform Plan’

This tab lets me choose which plan to evaluate, in my case the plan was done per environment. Here is Dev where no changes were detected:

Azure DevOps Terraform Task

And here is an example of a UAT environment where changes were detected:

Azure DevOps Terraform Task

This information was previously available in the output of the tasks; however, by moving this to a new tab can quickly navigate and see any changes. Plus, with the push to have business users release code this is less daunting for someone without a technical background to look at.

Conclusion

Using the Azure Terraform CLI task can greatly improve the experience when working with Terraform within ADO.

For more great blogs check out here.

About the Author:

John is a Senior DevOps Engineer at OST based out of Grand Rapids Michigan. He has played a key role in creating a modern Web Architecture, designing and implementing a big Data Analytics platform, and leveraging DevOps software delivery pipelines where applicable.

He has been awarded Microsoft Most Valuable Professional (MVP) award for the Microsoft Azure category, and he is a Microsoft Certified Trainer. He is a Microsoft Certified: Azure Solutions Architect Expert and holds numerous other Azure Certifications, including Azure DevOps Engineer Expert, Azure Security Engineer Associate, Azure Administrator Associate, Azure Developer Associate, Azure Data Engineer Associate, and Azure Data Analyst Associate. He is also an AWS Certified: Solutions Architect Associate.

Loves taking on new and exciting challenges and helping teams focus on what they are good at.

Reference:

Folberth, J. (2021). Azure DevOps Terraform Task. Available at: https://blog.johnfolberth.com/azure-devops-terraform-task/ [Accessed: 6th January 2022].

Share this on...

Rate this Post:

Share:

Topics:

Azure DevOps

Tags: