Validating Terraform Code During A Pull Request In Azure DevOps

I will show how to create a branch policy that will run a CI pipeline to validate Terraform code along with a Terraform plan, during a Pull Request in Azure DevOps and will include the YAML CI Pipeline.

Branch policies help teams protect their important branches of development. Policies enforce your team’s code quality and change management standards.

docs.microsoft.com

Depending on how you create and test your Terraform code; you will probably be doing this type of test locally but during a pull-request it gives a piece of mind to the reviewer(s) that the Terraform pull-request has successfully been validated along with a plan that can be reviewed.

Note:- In a previous blog post I have detailed how to Deploy Terraform using Azure DevOps

Time to DevOps

In this blog post, I will be using branch:- Develop

Now to create a CI Pipeline that will be used to validate and plan Terraform code during a Pull Request

Select Pipelines -> New Pipeline in Azure DevOps

  • Where is your code? Azure Repos Git
  • Select your repository
  • Select .yml basic and review the below .yml pipeline

Throughout the Pipeline, you will notice my reference to a Storage Account, Resource Group and container for the Terraform state file along with an Azure SPN – read my blog here on how to create these

  backendServiceArm: 'tamopstf'
  backendAzureRmResourceGroupName: 'tamopstf'
  backendAzureRmStorageAccountName: 'tamopstf'
  backendAzureRmContainerName: 'tfstatedevops'
  backendAzureRmKey: 'terraform.tfstate'

In my Pipeline, I have two Stages

Validate:- To Validate my Terraform code, if validation fails the pipeline fails (consists of Terraform init & validate)

Plan:- if Validation is successful, it moves to next stage of pipeline which is planning the Terraform code to output a Terraform Plan that can be reviewed as part of the pull request. (consists of Terraform plan)

The below YAML Pipeline will validate and plan your Terraform code

name: $(BuildDefinitionName)_$(date:yyyyMMdd)$(rev:.r)

trigger: none

pr: none
      
stages :
  - stage: validate
    jobs:
    - job: validate
      continueOnError: false
      steps:
      - task: TerraformInstaller@0
        displayName: 'install'
        inputs:
          terraformVersion: '0.12.3'
      - task: TerraformTaskV1@0
        displayName: 'init'
        inputs:
          provider: 'azurerm'
          command: 'init'
          backendServiceArm: 'tamopstf'
          backendAzureRmResourceGroupName: 'tamopstf'
          backendAzureRmStorageAccountName: 'tamopstf'
          backendAzureRmContainerName: 'tfstatedevops'
          backendAzureRmKey: 'terraform.tfstate'
      - task: TerraformTaskV1@0
        displayName: 'validate'
        inputs:
          provider: 'azurerm'
          command: 'validate'
          
  - stage: plan
    dependsOn: [validate]
    condition: succeeded('validate')
    jobs:
      - job: "Validate_TF_Develop_Branch"
        steps:
              - checkout: self
              - task: TerraformInstaller@0
                displayName: 'install'
                inputs:
                  terraformVersion: '0.12.3'
              - task: TerraformTaskV1@0
                displayName: 'init'
                inputs:
                  provider: 'azurerm'
                  command: 'init'
                  backendServiceArm: 'tamopstf'
                  backendAzureRmResourceGroupName: 'tamopstf'
                  backendAzureRmStorageAccountName: 'tamopstf'
                  backendAzureRmContainerName: 'tfstatedevops'
                  backendAzureRmKey: 'terraform.tfstate'
              - task: TerraformTaskV1@0
                displayName: 'plan'
                inputs:
                  provider: 'azurerm'
                  command: 'plan'
                  environmentServiceNameAzureRM: 'tamopstf'

Now save the pipeline; you can rename and add to a specific folder as below prior to saving

Now you have a Pipeline ready to be part of your branch policy; once the pipeline has been configured in a branch policy, it can run automatically as part of the pull request process.

Apply Branch Policy

In Azure DevOps select Repos -> Branches and you will see a screen similar to below with your branches available.

In my example, I mentioned that I will be applying the branch policy to Develop.

Select (to right of branch) -> Branch Policies

We will be creating a Build Validation; this is used to “Validate code by pre-merging and building pull request changes.”

Adding a build policy by selecting + on Build Validation

Below is the build policy I added

  • Build pipeline:- Assign the pipeline that was created earlier in this blog post
  • Trigger:- Automatic
  • Policy requirement:- Required
  • Build expiration:- Immediately when Develop is updated
  • Display Name:- Accurate display name of the build validation

Test the Branch Policy

A branch policy has now been created along with a build pipeline to validate and plan your Terraform code.

Create a pull request to the Develop Branch

Reviewing the pull request you will see in the Overview section the CI Pipeline that was created

This Pipeline will run automatically and the Pull request cannot be approved until the pipeline has been successful.

Awesome! We have now configured a branch policy that will run a CI pipeline to validate and plan your Terraform code during a Pull Request.

This pipeline can be reviewed and the Terraform plan can be reviewed as part of the pull request.

The Pipeline as mentioned has two stages; select either to review further

Once this has been reviewed, the pull request can be approved into develop

4 comments

  1. Hi Thomas

    is there a way to exclude below properties. Since i dont want to store the state file.
    backendServiceArm: ‘tamopstf’
    backendAzureRmResourceGroupName: ‘tamopstf’
    backendAzureRmStorageAccountName: ‘tamopstf’
    backendAzureRmContainerName: ‘tfstatedevops’
    backendAzureRmKey: ‘terraform.tfstate’

    1. Add a DeleteFiles task?

      – task: DeleteFiles@1
      displayName: ‘Remove unneeded files’
      inputs:
      contents: |
      terraform.tfstate

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s