When working with Terraform to deploy an infrastructure in an Azure DevOps pipeline, it is useful to use the output values in the following steps of your pipeline. Let’s see how we can achieve this easily.
Planning
For deploying Terraform templates to an infrastructure, I use the Terraform tasks library made by Microsoft.
Thanks to the output variables of the Terraform task, we are able to get a reference to a file containing the output values after a successful apply
.
Now, all the work is to read this file to convert it to variables for Azure DevOps. For this we are going to use a YAML pipeline. However it should be possible to do it with a classic pipeline.
Execution
First of all, we need to give a name to the apply
task of our pipeline, here we name it ‘terraformApply’.
- task: ms-devlabs.custom-terraform-tasks.custom-terraform-release-task.TerraformTaskV1@0
name: terraformApply
displayName: 'Terraform : validate and apply'
inputs:
command: apply
workingDirectory: '$(Pipeline.Workspace)/terraform'
commandOptions: 'tfplan'
environmentServiceNameAzureRM: ${{variables.EnvironmentServiceArm}}
backendServiceArm: ${{variables.BackendServiceArm}}
backendAzureRmResourceGroupName: ${{variables.BackendResourceGroup}}
backendAzureRmStorageAccountName: ${{variables.BackendStorageAccount}}
backendAzureRmContainerName: ${{variables.BackendContainer}}
backendAzureRmKey: ${{variables.BackendKey}}
Next step is parsing the file produced by the task above and convert it to variables. For this, we are going to use Powershell and the ability to define variables at runtime with the ##vso
commands.
The produced file looks like this :
{
"myapp_hostname": {
"sensitive": false,
"type": "string",
"value": "myapp.azurewebsites.net"
},
"myapp_name": {
"sensitive": false,
"type": "string",
"value": "myapp-dev"
}
}
We need to iterate on each property, take in account if the variable is sensitive (if so we have to specify that it is a secret) and use the ##vso
commands.
The corresponding task looks like this :
- powershell: |
$terraformOutput = Get-Content "$(terraformApply.jsonOutputVariablesPath)" | ConvertFrom-Json
$terraformOutput | Get-Member -MemberType NoteProperty | % { $o = $terraformOutput.($_.Name); Write-Host "##vso[task.setvariable variable=$($_.Name);isoutput=true;issecret=$($o.sensitive)]$($o.value)" }
name: terraformOutput
displayName: Read terraform outputs
We read the file (note that the jsonOutputVariablesPath
variable is an output of the apply task, thus we need to prefix with the task name terraformApply), parse the json and then on each property we output the ##vso
command with the parameter issecret
if needed.
We are now ready to use it in the next tasks. Such as the following App Service deployment task:
- task: AzureRmWebAppDeployment@4
displayName: 'Deploy MyApp'
inputs:
ConnectionType: 'AzureRM'
azureSubscription: ${{variables.EnvironmentServiceArm}}
appType: 'webApp'
WebAppName: '$(terraformOutput.myapp_name)'
packageForLinux: '$(Pipeline.Workspace)/MyApp.zip'
Please note, as the variables are defined as output, it’s needed to also prefix with terraformOutput
(which is the name of the powershell task we created).
Conclusion
Here we are ! We now are using the terraform outputs in the next tasks of our pipeline. Luckily, with the sensitive parameter we are also able to keep our secrets safe !
Dont you need to run the ‘plan’ step first to create the plan file, and then supply it to the apply command ?
Sure ! This is just an abstract of the whole pipeline.
In fact you should do this: install the terraform tool, run terraform init, run terraform plan, run terraform apply and then you’ll be able to get the output variables.
Il existe une extension Azure DevOps qui fait le job :
https://marketplace.visualstudio.com/items?itemName=raul-arrieta.terraform-outputs
C’est vrai Michel, mais pour plusieurs raisons j’ai décidé de faire par moi même:
– ca m’a intéressé de faire ca pour l’exercice
– c’est plutôt trivial à faire
– installer une extension peut se révéler compliqué dans certains environnement type on premise
– une extension d’un tiers dont on ne connait pas la politique de support peut poser problème également
– enfin, l’extension sur le store execute Terraform à nouveau pour produire le fichier, ce qui est peu utile vu qu’il a déjà été généré
There is also a github issue where this is discussed: https://github.com/microsoft/azure-pipelines-extensions/issues/660
Thanks for your blog. Very useful and helpful. Do you have any suggestion on how to get the terraform output and paste it as a PR comment? There is no simple solution out of the box, but do you have any solutions for that?
Thanks for your feedback, I appreciate it !
I’ve been thinking about the same feature for my team, I might try to implement it with a Azure Devops Extension as AFAIK there’s no easy way to comment a PR without being in an extension.
Check out my new article, should worth it 😉
https://www.natmarchand.fr/terraform-plan-as-pr-comment-in-azure-devops/