Extracting Outputs from Azure DevOps Terraform Tasks

I’ve recently started using the Microsoft DevLabs Terraform Extension for Azure DevOps to deploy terraform to Azure, switching from other third party terraform extensions or just using CLI commands.

This is a great extension, and I find it especially useful for the ability to use existing Azure RM service connections for authentication to Azure.

One thing that it took me a bit to figure out is how to output the results of a Terraform Apply task and pass it to another task. The official documentation is not great in outlining how to do this and it took me a while to figure out. Below I’ll outline how this can be done.

The jsonOutputVariablesPath

When you use the TerraformTaskV4@4 with command ‘output’, you get the gain the extra output value jsonOutputVariablesPath. This contains the outputs from your top level terraform files. This value is only available when you use the output command, and is not available when using apply. My usual strategy is to run a plan, apply and then output in that order.

trigger:
  none
    
pool:
  vmImage: ubuntu-latest

variables:
  workingDir: '$(System.DefaultWorkingDirectory)/terraform/'

stages:
- stage: 'firstStage'
  jobs:
  - job: 'tfDeploy'
    displayName: 'Deploy'
    steps:

    - task: TerraformTaskV4@4
      displayName: 'Terraform Init'
      inputs:
        provider: 'azurerm'
        command: 'init'
        workingDirectory: $(workingDir)
        backendServiceArm: 'my-sc'
        backendAzureRmResourceGroupName: 'my-rg'
        backendAzureRmStorageAccountName: 'my-state-storage-account'
        backendAzureRmContainerName: 'tfstate'
        backendAzureRmKey: 'testalex.tfstate'

    - task: TerraformTaskV4@4
      displayName: 'Terraform Plan'
      inputs:
        provider: 'azurerm'
        command: 'plan'
        workingDirectory: $(workingDir)
        environmentServiceNameAzureRM: 'my-sc'

    - task: TerraformTaskV4@4
      displayName: 'Terraform Apply'
      inputs:
        provider: 'azurerm'
        command: 'apply'
        workingDirectory: $(workingDir)
        environmentServiceNameAzureRM: 'my-sc'

    - task: TerraformTaskV4@4
      name: TerraformOutput
      inputs:
        provider: 'azurerm'
        command: 'output'
        workingDirectory: '$(workingDir)'
        environmentServiceNameAzureRM: 'my-sc'

    - script: |
        output_value=$(jq -r '.output_test.value' $(TerraformOutput.jsonOutputVariablesPath))
        echo "my output variable:"
        echo $(output_value)
        echo "##vso[task.setvariable variable=myOutputVariable;isOutput=true]$(output_value)"
      displayName: 'Echo Variable Name'
      name: GetTFOutputs

- stage: 'SecondStage'
  dependsOn: firstStage
  variables:
    myVar: $[stageDependencies.firstStage.tfDeploy.outputs['GetTFOutputs.myOutputVariable']]
  jobs:
  - job: 'secondjob'
    displayName: 'secondjob'
    steps:
    - script: |
        echo "my output variable:"
        echo $(myVar)
      displayName: 'Echo Variable Name'

See below for an overview of each step:

  • Terraform Init, Terraform Plan, Terraform Apply
    These tasks firstly initialise a terraform working directory, creates a terraform plan (based off a working directory of terraform files) and runs a Terraform Apply.
  • TerraformOutput
    Retrieves the output values from the Terraform state file. The output is stored in a JSON file specified by jsonOutputVariablesPath.
  • Echo Variable Name
    In this task firstly we will output the variable name so we can see it in our pipeline. This uses jq to parse the JSON output file and extract the value of output_test, where output_test is the name of the Terraform output variable we want to extract the value for.

output_value=$(jq -r '.output_test.value' $(TerraformOutput.jsonOutputVariablesPath))
echo $(output_value)
  • The next command we will run is used to set the output variable from Terraform as a devops task output variable. We will do this so we can use the output variable in subsequent stages.
echo "##vso[task.setvariable variable=myOutputVariable;isOutput=true]$output_value"

If we wanted to use this variable within the same stage, we could do so by using the $(myOutputVariable) operator.

  • SecondStage
    Important. To use dependencies, you need to set the dependsOn property on the future job using the name of the past job in which the output variable was set. Therefore set the dependsOn value of the second stage to the name of the first stage.
- stage: 'SecondStage'
  dependsOn: firstStage


This stage contains a single job secondjob that retrieves the value of myOutputVariablefrom the dependent first stage.
Firstly we will set a new variable based off the output of a previous stage. This is done via the command:

myVar: $[stageDependencies.firstStage.tfDeploy.outputs['GetTFOutputs.myOutputVariable']]

We can then use this variable in this stage by referencing it like any other pipeline variable, eg:

- script: |
    echo "my output variable:"
    echo $(myVar)
  displayName: 'Echo Variable Name'

This will give us output like the below:

Conclusion

The above demonstrates how to use the jsonOutputVariablesPath task output to:

  • Retrieve Terraform outputs and store them in a JSON file.
  • Parse the JSON file to extract specific output values.
  • Set these values as pipeline variables for use in subsequent stages.

I found this information tricky to find and not even Copilot could generate me working code; so hopefully this helps someone use this specific feature of the Azure DevOps Terraform task.

alexd Avatar

Published by

Categories:

Leave a comment