Pipeline variables in Azure DevOps YAML pipelines get mapped to environment variables in the pipeline agent. With Terraform, if there’s an environment variable that starts with TF_VAR_ it will get mapped to an input variable. Combining the two can mean more succinct pipelines.

Azure DevOps

So, variables in Azure DevOps pipelines get mapped to environment variable. There’s one catch, it will always be uppercase.

variables:
  someVariable: 'someValue'

The variable someVariable becomes an environment variable named SOMEVARIABLE.

There is also another catch. Secrets won’t get mapped to environment variables for security reasons.

variables:
  someVariable: '$(SomeConnectionString)'

Assuming that SomeConnectionString is a pipeline variable stored as a secret, it will not get mapped to the pipeline variable. Therefore, there will be no environment variable to use.

Terraform

Let’s say you have the following input variables defined in Terraform.

variable "app_name" {
  type = string
}

variable "location" {
  type = string
}

variable "environment" {
  type = string
}

When we seek to perform a terraform plan and then terraform apply, we can specify the values of the variables using the -var flag.

terraform apply -var "app_name=someapp"

Also, we can specify its value by defining an environment variable that starts with TF_VAR_. This way, there’s no need to override with flags.

# Assuming an environment variable TF_VAR_app_name is already defined
terraform apply

Combining the two

So, if we define Azure DevOps pipeline variables with a prefix of TF_VAR_, they will get mapped into environment variables that Terraform will pick them up.

variables: 
  TF_VAR_APP_NAME: 'someapp'

Then, in your Terraform configuration, you have to define the variables with capital letters.

Why? Because Azure DevOps will always transform pipeline variables to uppercase environment variables.

variable "APP_NAME" {
  type = string
}

Except, this won’t work for Azure DevOps pipeline variables saved as secrets. For secrets, you will still have to pass them as variables. For example:

- task: TerraformTaskV1@0
  displayName: "Terraform Plan"
  inputs:
    provider: 'azurerm'
    command: 'plan'
    workingDirectory: '$(System.DefaultWorkingDirectory)/iac/'
    environmentServiceNameAzureRM: $(AzureServiceConnection)
    commandOptions: -input=false -var "VM_ADMIN_PASSWORD=$(TF_VAR_admin_password)"

TF_VAR_admin_password is a pipeline variable saved as a secret.