Displaying Terraform Plan as a comment in Azure DevOps Repo PRs with Azure DevOps Pipelines

In this blog post – we will displaying Terraform plans directly within Azure DevOps Repository Pull Requests as a comment using Azure DevOps Pipelines.

Prerequisites

Please read this blog post prior, it has some prerequisites that are required before this pipeline will work successfully. Ensure you have:

  • Enabled the service connection Contribute to pull requests to Allow within the Azure repos settings
  • Configured a branch policy so system variable System.PullRequest.PullRequestId works

Azure DevOps Pipeline

The Azure DevOps pipeline is going to be in a simple state, I want to show how you can display a terraform plan as a comment as part of your pull request process using the Azure DevOps Pipeline.

The pipeline will be:

  • Single stage called terraform
  • With the following tasks:
    • install: Terraform installer
    • init: Terraform initialisation
    • validate: Runs terraform validate
    • plan: Runs terraform plan
    • show: Runs terraform show , used to convert the terraform plan into a readable format. (Can be done using terraform plan, but it was erroring when attempting to use with task TerraformTaskV4@4 )
    • terraform plan to azure devops : Uploads terraform plan output to Azure DevOps as a pull request comment

Lets look at the task: terraform plan to azure devops

      - task: Bash@3
        displayName: 'Terraform Plan to Azure DevOps'
        inputs:
          targetType: 'inline'
          script: |
            TF_PLAN=$(cat $workingDirectory/tf_plan.txt)

            ADO_API=$(echo "$(System.CollectionUri)$(System.TeamProject)/_apis/git/repositories/$(Build.Repository.Name)/pullRequests/$(System.PullRequest.PullRequestId)/threads?api-version=7.1-preview.1")

            TF_PLAN_comment=$(jq --arg comment "$TF_PLAN" '.comments[0].content = $comment' <<< '{"comments": [{"parentCommentId": 0,"content": "","commentType": 1}],"status": 1}')

            curl --request POST "$ADO_API" \
            --header "Authorization: Bearer $SYSTEM_ACCESSTOKEN" \
            --header "Accept: application/json" \
            --header "Content-Type: application/json" \
            --data "$TF_PLAN_comment" \
            --verbose
        env:
          workingDirectory: '$(System.DefaultWorkingDirectory)/terraform/'
          SYSTEM_ACCESSTOKEN: $(System.AccessToken)

I have outlined this task in a previous blog post, similar setup but this time with the TF_PLAN variable, it will be including the terraform plan output

The full pipeline:

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

trigger: none

pr: none

variables:
  - name: backendServiceArm
    value: 'thomasthorntonautomatic'
  - name: backendAzureRmResourceGroupName
    value: 'thomasthorntoncloud'
  - name: backendAzureRmStorageAccountName
    value: 'thomasthorntontfstate'
  - name: backendAzureRmContainerName
    value: 'ado-terraform-pr-plan-output'
  - name: backendAzureRmKey
    value: 'terraform.tfstate'
  - name: deployment_subscription_id
    value: '04109105-f3ca-44ac-a3a7-66b4936112c3'

stages :
  - stage: terraform
    jobs:
    - job: validate
      continueOnError: false
      steps:
      - task: TerraformInstaller@0
        displayName: 'install'
        inputs:
          terraformVersion: '1.6.0'

      - task: TerraformTaskV4@4
        displayName: 'init'
        inputs:
          provider: 'azurerm'
          command: 'init'
          backendServiceArm: '${{ variables.backendServiceArm}}'
          backendAzureRmResourceGroupName: '${{ variables.backendAzureRmResourceGroupName}}'
          backendAzureRmStorageAccountName: '${{ variables.backendAzureRmStorageAccountName}}'
          backendAzureRmContainerName: '${{ variables.backendAzureRmContainerName }}'
          backendAzureRmKey: 'terraform1.tfstate'
          workingDirectory: '$(System.DefaultWorkingDirectory)/terraform/'

      - task: TerraformTaskV4@4
        displayName: 'validate'
        inputs:
          provider: 'azurerm'
          command: 'validate'

      - task: TerraformTaskV4@4
        displayName: 'plan'
        inputs:
          provider: 'azurerm'
          command: 'plan'
          commandOptions: '-input=false -var deployment_subscription_id=$(deployment_subscription_id) -out=plan.tfplan'
          environmentServiceNameAzureRM: '${{ variables.backendServiceArm}}'
          workingDirectory: '$(System.DefaultWorkingDirectory)/terraform/'

      - task: Bash@3
        displayName: 'show'
        inputs:
          targetType: 'inline'
          script: |
            cd $workingDirectory
            terraform show -no-color plan.tfplan > tf_plan.txt
        env:
          workingDirectory: '$(System.DefaultWorkingDirectory)/terraform/'

      - task: Bash@3
        displayName: 'Terraform Plan to Azure DevOps'
        inputs:
          targetType: 'inline'
          script: |
            TF_PLAN=$(cat $workingDirectory/tf_plan.txt)

            ADO_API=$(echo "$(System.CollectionUri)$(System.TeamProject)/_apis/git/repositories/$(Build.Repository.Name)/pullRequests/$(System.PullRequest.PullRequestId)/threads?api-version=7.1-preview.1")

            TF_PLAN_comment=$(jq --arg comment "$TF_PLAN" '.comments[0].content = $comment' <<< '{"comments": [{"parentCommentId": 0,"content": "","commentType": 1}],"status": 1}')

            curl --request POST "$ADO_API" \
            --header "Authorization: Bearer $SYSTEM_ACCESSTOKEN" \
            --header "Accept: application/json" \
            --header "Content-Type: application/json" \
            --data "$TF_PLAN_comment" \
            --verbose
        env:
          workingDirectory: '$(System.DefaultWorkingDirectory)/terraform/'
          SYSTEM_ACCESSTOKEN: $(System.AccessToken)

Below shows the example comment of the Terraform plan:

GitHub repository containing the example code references above

Leave a Reply