Terraform Template for Azure DevOps
A copy of these templates is stored here
---
parameters:
- name: TERRAFORM_PATH
type: string
default: ""
displayName: "What is the path to your terraform code?"
- name: TERRAFORM_VERSION
type: string
default: ""
displayName: "What version of terraform should be installed?"
- name: TERRAFORM_DESTROY
default: false
type: boolean
displayName: "Do you wish to run terraform destroy?"
- name: TERRAFORM_PLAN_ONLY
default: true
type: boolean
displayName: "Do you wish to run terraform destroy?"
- name: TERRAFORM_STORAGE_RG_NAME
default: ""
type: string
displayName: "What is the resource group name in which the storage account exists in?"
- name: TERRAFORM_STORAGE_ACCOUNT_NAME
default: ""
type: string
displayName: "What is the name of the storage account in which the state file is being stored?"
- name: TERRAFORM_BLOB_CONTAINER_NAME
default: ""
type: string
displayName: "What is the name of the blob container in which the state file is being stored?"
- name: TERRAFORM_STORAGE_KEY
default: ""
type: string
displayName: "What is the key used to access your storage account? Please note, this value is a secret"
- name: TERRAFORM_STATE_NAME
default: ""
type: string
displayName: "What name should the state file have?"
- name: TERRAFORM_WORKSPACE_NAME
default: ""
type: string
displayName: "Which workspace should be used or created?"
- name: TERRAFORM_COMPLIANCE_PATH
type: string
default: ""
displayName: "Where is your terraform-compliance policy files located?"
- name: AZURE_TARGET_CLIENT_ID
default: ""
type: string
displayName: "What is the client id of the service principle you wish to use with Terraform?"
- name: AZURE_TARGET_CLIENT_SECRET
default: ""
type: string
displayName: "What is the client of the service principle you wish to use with Terraform? Note, this value is a secret"
- name: AZURE_TARGET_SUBSCRIPTION_ID
default: ""
type: string
displayName: "What is the subscription ID of the target subscription you are trying to deploy to?"
- name: AZURE_TARGET_TENANT_ID
default: ""
type: string
displayName: "What is the tenant ID in which the target subscription resides?"
- name: SHORTHAND_PROJECT_NAME
default: ""
type: string
displayName: "What is the shorthand name for your project?"
- name: SHORTHAND_ENVIRONMENT_NAME
default: ""
type: string
displayName: "What is the shorthand (3 character) name for environment you are deploying to?"
- name: SHORTHAND_LOCATION_NAME
default: ""
type: string
displayName: "What is the shorthand location name? E.g. uks for UK South etc"
- name: CHECKOV_SKIP_TESTS
default: ""
type: string
displayName: "What Checkov steps should be skipped, null by default, should be value like CKV_AZURE_50, CKV_AZURE_20 etc."
steps:
- task: ms-devlabs.custom-terraform-tasks.custom-terraform-installer-task.TerraformInstaller@0
displayName: "Install Terraform ${{ parameters.TERRAFORM_VERSION }}"
inputs:
terraformVersion: ${{ parameters.TERRAFORM_VERSION }}
enabled: true
- ${{ if and(eq(parameters.TERRAFORM_DESTROY, false), eq(parameters.TERRAFORM_PLAN_ONLY, true)) }}:
- pwsh: |
New-Item -Path . -Name .terraform -ItemType "Directory" -Force ; `
terraform init `
-backend-config="storage_account_name=${{ parameters.TERRAFORM_STORAGE_ACCOUNT_NAME }}" `
-backend-config="container_name=${{ parameters.TERRAFORM_BLOB_CONTAINER_NAME }}" `
-backend-config="access_key=${{ parameters.TERRAFORM_STORAGE_KEY }}" `
-backend-config="key=${{ parameters.TERRAFORM_STATE_NAME }}" ; `
Write-Output "${{ parameters.TERRAFORM_WORKSPACE_NAME }}" > .terraform/environment ; `
terraform workspace new "${{ parameters.TERRAFORM_WORKSPACE_NAME }}" ; `
terraform workspace select "${{ parameters.TERRAFORM_WORKSPACE_NAME }}" ; `
terraform validate ; `
terraform plan -out pipeline.plan
displayName: Terraform Init, Validate & Plan
workingDirectory: ${{ parameters.TERRAFORM_PATH }}
enabled: true
env:
TF_VAR_short: ${{ parameters.SHORTHAND_PROJECT_NAME }}
TF_VAR_env: ${{ parameters.SHORTHAND_ENVIRONMENT_NAME }}
TF_VAR_loc: ${{ parameters.SHORTHAND_LOCATION_NAME }}
TF_VAR_TERRAFORM_STORAGE_RG_NAME: ${{ parameters.TERRAFORM_STORAGE_RG_NAME }}
TF_VAR_TERRAFORM_STORAGE_ACCOUNT_NAME: ${{ parameters.TERRAFORM_STORAGE_ACCOUNT_NAME }}
TF_VAR_TERRAFORM_BLOB_CONTAINER_NAME: ${{ parameters.TERRAFORM_BLOB_CONTAINER_NAME }}
TF_VAR_TERRAFORM_STORAGE_KEY: ${{ parameters.TERRAFORM_STORAGE_KEY }}
ARM_CLIENT_ID: ${{ parameters.AZURE_TARGET_CLIENT_ID }}
ARM_CLIENT_SECRET: ${{ parameters.AZURE_TARGET_CLIENT_SECRET }}
ARM_SUBSCRIPTION_ID: ${{ parameters.AZURE_TARGET_SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ parameters.AZURE_TARGET_TENANT_ID }}
- pwsh: |
pip3 install terraform-compliance ; `
terraform-compliance -p pipeline.plan -f ${{ parameters.TERRAFORM_COMPLIANCE_PATH }}
displayName: 'Terraform-Compliance Check'
workingDirectory: "${{ parameters.TERRAFORM_PATH }}"
continueOnError: false
enabled: true
- pwsh: |
if ($IsLinux)
{
brew install tfsec
}
elseif ($IsMacOS)
{
brew install tfsec
}
elseif ($IsWindows)
{
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
choco install tfsec -y
}
tfsec . --force-all-dirs
displayName: 'TFSEC Check'
workingDirectory: "${{ parameters.TERRAFORM_PATH }}"
continueOnError: false
enabled: true
- pwsh: |
pip3 install checkov ; `
terraform show -json pipeline.plan > pipeline.plan.json ; `
checkov -f pipeline.plan.json
displayName: 'CheckOV Check'
workingDirectory: "${{ parameters.TERRAFORM_PATH }}"
continueOnError: false
condition: and(succeeded(), eq('${{ parameters.CHECKOV_SKIP_TESTS }}', ' '))
enabled: true
- pwsh: |
pip3 install checkov ; `
terraform show -json pipeline.plan > pipeline.plan.json ; `
checkov -f pipeline.plan.json --skip-check ${{ parameters.CHECKOV_SKIP_TESTS }}
displayName: 'CheckOV Check with Skipped Tests'
workingDirectory: "${{ parameters.TERRAFORM_PATH }}"
continueOnError: false
condition: and(succeeded(), not(eq('${{ parameters.CHECKOV_SKIP_TESTS }}', ' ')))
enabled: true
- ${{ if and(eq(parameters.TERRAFORM_DESTROY, false), eq(parameters.TERRAFORM_PLAN_ONLY, false)) }}:
- pwsh: |
New-Item -Path . -Name .terraform -ItemType "Directory" -Force ; `
terraform init `
-backend-config="storage_account_name=${{ parameters.TERRAFORM_STORAGE_ACCOUNT_NAME }}" `
-backend-config="container_name=${{ parameters.TERRAFORM_BLOB_CONTAINER_NAME }}" `
-backend-config="access_key=${{ parameters.TERRAFORM_STORAGE_KEY }}" `
-backend-config="key=${{ parameters.TERRAFORM_STATE_NAME }}" ; `
Write-Output "${{ parameters.TERRAFORM_WORKSPACE_NAME }}" > .terraform/environment ; `
terraform workspace new "${{ parameters.TERRAFORM_WORKSPACE_NAME }}" ; `
terraform workspace select "${{ parameters.TERRAFORM_WORKSPACE_NAME }}" ; `
terraform validate ; `
terraform plan -out pipeline.plan
displayName: Terraform Init, Validate & Plan
workingDirectory: ${{ parameters.TERRAFORM_PATH }}
enabled: true
env:
TF_VAR_short: ${{ parameters.SHORTHAND_PROJECT_NAME }}
TF_VAR_env: ${{ parameters.SHORTHAND_ENVIRONMENT_NAME }}
TF_VAR_loc: ${{ parameters.SHORTHAND_LOCATION_NAME }}
TF_VAR_TERRAFORM_STORAGE_RG_NAME: ${{ parameters.TERRAFORM_STORAGE_RG_NAME }}
TF_VAR_TERRAFORM_STORAGE_ACCOUNT_NAME: ${{ parameters.TERRAFORM_STORAGE_ACCOUNT_NAME }}
TF_VAR_TERRAFORM_BLOB_CONTAINER_NAME: ${{ parameters.TERRAFORM_BLOB_CONTAINER_NAME }}
TF_VAR_TERRAFORM_STORAGE_KEY: ${{ parameters.TERRAFORM_STORAGE_KEY }}
ARM_CLIENT_ID: ${{ parameters.AZURE_TARGET_CLIENT_ID }}
ARM_CLIENT_SECRET: ${{ parameters.AZURE_TARGET_CLIENT_SECRET }}
ARM_SUBSCRIPTION_ID: ${{ parameters.AZURE_TARGET_SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ parameters.AZURE_TARGET_TENANT_ID }}
- pwsh: |
pip3 install terraform-compliance ; `
terraform-compliance -p pipeline.plan -f ${{ parameters.TERRAFORM_COMPLIANCE_PATH }}
displayName: 'Terraform-Compliance Check'
workingDirectory: "${{ parameters.TERRAFORM_PATH }}"
continueOnError: true
enabled: true
- pwsh: |
if ($IsLinux)
{
brew install tfsec
}
elseif ($IsMacOS)
{
brew install tfsec
}
elseif ($IsWindows)
{
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
choco install tfsec -y
}
tfsec . --force-all-dirs
displayName: 'TFSEC Check'
workingDirectory: "${{ parameters.TERRAFORM_PATH }}"
continueOnError: false
enabled: true
- pwsh: |
pip3 install checkov ; `
terraform show -json pipeline.plan > pipeline.plan.json ; `
checkov -f pipeline.plan.json
displayName: 'CheckOV Check'
workingDirectory: "${{ parameters.TERRAFORM_PATH }}"
continueOnError: false
condition: and(succeeded(), eq('${{ parameters.CHECKOV_SKIP_TESTS }}', ' '))
enabled: true
- pwsh: |
pip3 install checkov ; `
terraform show -json pipeline.plan > pipeline.plan.json ; `
checkov -f pipeline.plan.json --skip-check ${{ parameters.CHECKOV_SKIP_TESTS }}
displayName: 'CheckOV Check with Skipped Tests'
workingDirectory: "${{ parameters.TERRAFORM_PATH }}"
continueOnError: false
condition: and(succeeded(), not(eq('${{ parameters.CHECKOV_SKIP_TESTS }}', ' ')))
enabled: true
- pwsh: |
New-Item -Path . -Name .terraform -ItemType "Directory" -Force ; `
terraform init `
-backend-config="storage_account_name=${{ parameters.TERRAFORM_STORAGE_ACCOUNT_NAME }}" `
-backend-config="container_name=${{ parameters.TERRAFORM_BLOB_CONTAINER_NAME }}" `
-backend-config="access_key=${{ parameters.TERRAFORM_STORAGE_KEY }}" `
-backend-config="key=${{ parameters.TERRAFORM_STATE_NAME }}" ; `
Write-Output "${{ parameters.TERRAFORM_WORKSPACE_NAME }}" > .terraform/environment ; `
terraform workspace new "${{ parameters.TERRAFORM_WORKSPACE_NAME }}" ; `
terraform workspace select "${{ parameters.TERRAFORM_WORKSPACE_NAME }}" ; `
terraform validate ; `
terraform plan -out pipeline.plan
terraform apply pipeline.plan
displayName: Terraform Init, Validate, Plan & Apply
workingDirectory: ${{ parameters.TERRAFORM_PATH }}
enabled: true
env:
TF_VAR_short: ${{ parameters.SHORTHAND_PROJECT_NAME }}
TF_VAR_env: ${{ parameters.SHORTHAND_ENVIRONMENT_NAME }}
TF_VAR_loc: ${{ parameters.SHORTHAND_LOCATION_NAME }}
TF_VAR_TERRAFORM_STORAGE_RG_NAME: ${{ parameters.TERRAFORM_STORAGE_RG_NAME }}
TF_VAR_TERRAFORM_STORAGE_ACCOUNT_NAME: ${{ parameters.TERRAFORM_STORAGE_ACCOUNT_NAME }}
TF_VAR_TERRAFORM_BLOB_CONTAINER_NAME: ${{ parameters.TERRAFORM_BLOB_CONTAINER_NAME }}
TF_VAR_TERRAFORM_STORAGE_KEY: ${{ parameters.TERRAFORM_STORAGE_KEY }}
ARM_CLIENT_ID: ${{ parameters.AZURE_TARGET_CLIENT_ID }}
ARM_CLIENT_SECRET: ${{ parameters.AZURE_TARGET_CLIENT_SECRET }}
ARM_SUBSCRIPTION_ID: ${{ parameters.AZURE_TARGET_SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ parameters.AZURE_TARGET_TENANT_ID }}
- ${{ if and(eq(parameters.TERRAFORM_DESTROY, true), eq(parameters.TERRAFORM_PLAN_ONLY, false)) }}:
- pwsh: |
New-Item -Path . -Name .terraform -ItemType "Directory" -Force ; `
terraform init `
-backend-config="storage_account_name=${{ parameters.TERRAFORM_STORAGE_ACCOUNT_NAME }}" `
-backend-config="container_name=${{ parameters.TERRAFORM_BLOB_CONTAINER_NAME }}" `
-backend-config="access_key=${{ parameters.TERRAFORM_STORAGE_KEY }}" `
-backend-config="key=${{ parameters.TERRAFORM_STATE_NAME }}" ; `
Write-Output "${{ parameters.TERRAFORM_WORKSPACE_NAME }}" > .terraform/environment ; `
terraform workspace new "${{ parameters.TERRAFORM_WORKSPACE_NAME }}" ; `
terraform workspace select "${{ parameters.TERRAFORM_WORKSPACE_NAME }}" ; `
terraform validate ; `
terraform plan -destroy -out pipeline.plan
displayName: 'Terraform Init, Validate & Plan Destroy'
workingDirectory: "${{ parameters.TERRAFORM_PATH }}"
continueOnError: false
enabled: true
env:
TF_VAR_short: ${{ parameters.SHORTHAND_PROJECT_NAME }}
TF_VAR_env: ${{ parameters.SHORTHAND_ENVIRONMENT_NAME }}
TF_VAR_loc: ${{ parameters.SHORTHAND_LOCATION_NAME }}
TF_VAR_TERRAFORM_STORAGE_RG_NAME: ${{ parameters.TERRAFORM_STORAGE_RG_NAME }}
TF_VAR_TERRAFORM_STORAGE_ACCOUNT_NAME: ${{ parameters.TERRAFORM_STORAGE_ACCOUNT_NAME }}
TF_VAR_TERRAFORM_BLOB_CONTAINER_NAME: ${{ parameters.TERRAFORM_BLOB_CONTAINER_NAME }}
TF_VAR_TERRAFORM_STORAGE_KEY: ${{ parameters.TERRAFORM_STORAGE_KEY }}
ARM_CLIENT_ID: ${{ parameters.AZURE_TARGET_CLIENT_ID }}
ARM_CLIENT_SECRET: ${{ parameters.AZURE_TARGET_CLIENT_SECRET }}
ARM_SUBSCRIPTION_ID: ${{ parameters.AZURE_TARGET_SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ parameters.AZURE_TARGET_TENANT_ID }}
- pwsh: |
New-Item -Path . -Name .terraform -ItemType "Directory" -Force ; `
terraform init `
-backend-config="storage_account_name=${{ parameters.TERRAFORM_STORAGE_ACCOUNT_NAME }}" `
-backend-config="container_name=${{ parameters.TERRAFORM_BLOB_CONTAINER_NAME }}" `
-backend-config="access_key=${{ parameters.TERRAFORM_STORAGE_KEY }}" `
-backend-config="key=${{ parameters.TERRAFORM_STATE_NAME }}" ; `
Write-Output "${{ parameters.TERRAFORM_WORKSPACE_NAME }}" > .terraform/environment ; `
terraform workspace new "${{ parameters.TERRAFORM_WORKSPACE_NAME }}" ; `
terraform workspace select "${{ parameters.TERRAFORM_WORKSPACE_NAME }}" ; `
terraform validate ; `
terraform plan -destroy -out pipeline.plan
terraform apply pipeline.plan
displayName: 'Terraform Init, Validate, Plan and Apply Destroy'
workingDirectory: "${{ parameters.TERRAFORM_PATH }}"
continueOnError: false
enabled: true
env:
TF_VAR_short: ${{ parameters.SHORTHAND_PROJECT_NAME }}
TF_VAR_env: ${{ parameters.SHORTHAND_ENVIRONMENT_NAME }}
TF_VAR_loc: ${{ parameters.SHORTHAND_LOCATION_NAME }}
TF_VAR_TERRAFORM_STORAGE_RG_NAME: ${{ parameters.TERRAFORM_STORAGE_RG_NAME }}
TF_VAR_TERRAFORM_STORAGE_ACCOUNT_NAME: ${{ parameters.TERRAFORM_STORAGE_ACCOUNT_NAME }}
TF_VAR_TERRAFORM_BLOB_CONTAINER_NAME: ${{ parameters.TERRAFORM_BLOB_CONTAINER_NAME }}
TF_VAR_TERRAFORM_STORAGE_KEY: ${{ parameters.TERRAFORM_STORAGE_KEY }}
ARM_CLIENT_ID: ${{ parameters.AZURE_TARGET_CLIENT_ID }}
ARM_CLIENT_SECRET: ${{ parameters.AZURE_TARGET_CLIENT_SECRET }}
ARM_SUBSCRIPTION_ID: ${{ parameters.AZURE_TARGET_SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ parameters.AZURE_TARGET_TENANT_ID }}
- ${{ if and(eq(parameters.TERRAFORM_DESTROY, true), eq(parameters.TERRAFORM_PLAN_ONLY, true)) }}:
- pwsh: |
New-Item -Path . -Name .terraform -ItemType "Directory" -Force ; `
terraform init `
-backend-config="storage_account_name=${{ parameters.TERRAFORM_STORAGE_ACCOUNT_NAME }}" `
-backend-config="container_name=${{ parameters.TERRAFORM_BLOB_CONTAINER_NAME }}" `
-backend-config="access_key=${{ parameters.TERRAFORM_STORAGE_KEY }}" `
-backend-config="key=${{ parameters.TERRAFORM_STATE_NAME }}" ; `
Write-Output "${{ parameters.TERRAFORM_WORKSPACE_NAME }}" > .terraform/environment ; `
terraform workspace new "${{ parameters.TERRAFORM_WORKSPACE_NAME }}" ; `
terraform workspace select "${{ parameters.TERRAFORM_WORKSPACE_NAME }}" ; `
terraform validate ; `
terraform plan -destroy -out pipeline.plan
displayName: 'Terraform Init, Validate & Plan Destroy'
workingDirectory: "${{ parameters.TERRAFORM_PATH }}"
continueOnError: false
enabled: true
env:
TF_VAR_short: ${{ parameters.SHORTHAND_PROJECT_NAME }}
TF_VAR_env: ${{ parameters.SHORTHAND_ENVIRONMENT_NAME }}
TF_VAR_loc: ${{ parameters.SHORTHAND_LOCATION_NAME }}
TF_VAR_TERRAFORM_STORAGE_RG_NAME: ${{ parameters.TERRAFORM_STORAGE_RG_NAME }}
TF_VAR_TERRAFORM_STORAGE_ACCOUNT_NAME: ${{ parameters.TERRAFORM_STORAGE_ACCOUNT_NAME }}
TF_VAR_TERRAFORM_BLOB_CONTAINER_NAME: ${{ parameters.TERRAFORM_BLOB_CONTAINER_NAME }}
TF_VAR_TERRAFORM_STORAGE_KEY: ${{ parameters.TERRAFORM_STORAGE_KEY }}
ARM_CLIENT_ID: ${{ parameters.AZURE_TARGET_CLIENT_ID }}
ARM_CLIENT_SECRET: ${{ parameters.AZURE_TARGET_CLIENT_SECRET }}
ARM_SUBSCRIPTION_ID: ${{ parameters.AZURE_TARGET_SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ parameters.AZURE_TARGET_TENANT_ID }}
Example Call of Template
---
name: $(Build.DefinitionName)-$(date:yyyyMMdd)$(rev:.r)
trigger: none
# parameters are typed with defaults so they are correctly populated, you will get a choice in the GUI to edit these, but you should keep all changes as code.
parameters:
- name: SHORTHAND_ENVIRONMENT_NAME
default: dev
displayName: "What is the shorthand name for this environment?"
type: string
values:
- dev
- poc
- mvp
- tst
- uat
- ppd
- prd
- name: SHORTHAND_PROJECT_NAME
type: string
default: "ldo"
displayName: "Shorthand Project e.g. lbdo for libredevops"
- name: SHORTHAND_LOCATION_NAME
type: string
default: "euw"
displayName: "3 character location name, e.g., uks, ukw, euw"
- name: TERRAFORM_PATH
type: string
default: "$(Build.SourcesDirectory)/azure-pipelines-module-development-build/terraform"
displayName: "What is the path to your terraform code?"
- name: TERRAFORM_VERSION
type: string
default: "1.1.7"
displayName: "Which version of Terraform should be installed?"
- name: VARIABLE_GROUP_NAME
type: string
default: "svp-kv-ldo-euw-dev-mgt-01"
displayName: "Enter the variable group which contains your authentication information"
# This variable sets up a condition in the template, if set to true, it will run terraform plan -destroy instead of the normal plan
- name: TERRAFORM_DESTROY
default: false
displayName: "Check box to run a Destroy"
type: boolean
- name: TERRAFORM_PLAN_ONLY
default: true
displayName: "Check box to run plan ONLY and never run apply"
type: boolean
- name: CHECKOV_SKIP_TESTS
type: string
default: ' '
displayName: "CheckOV tests to skip if comment skips don't work. All checks run if parameter is empty, empty by default"
# Declare variable group to pass variables to parameters, in this case, a libre-devops keyvault which is using a service principle for authentication
variables:
- group: ${{ parameters.VARIABLE_GROUP_NAME }}
# Sets what repos need cloned, for example, a library repo for modules and a poly-repo for target code
resources:
repositories:
- repository: azure-naming-convention
type: github
endpoint: github_service_connection
name: libre-devops/azure-naming-convention
ref: main
# You only need to declare a remote repository if its a GitHub repo, otherwise, you can just use the - self task to clone an Azure DevOps repo.
- repository: dev-build
type: github
endpoint: github_service_connection
name: libre-devops/dev-build
ref: dev
# You may wish to use a separate or self-hosted agent per job, by default, all jobs will inherit stage agent
pool:
name: Azure Pipelines
vmImage: ubuntu-latest
# Sets stage so that multiple stages can be used if needed, as it stands, only 1 stage is expected and is thus passed as a parameter
stages:
- stage: "${{ parameters.SHORTHAND_ENVIRONMENT_NAME }}"
displayName: "${{ parameters.SHORTHAND_ENVIRONMENT_NAME }} Stage"
jobs:
- job: Terraform_Build
workspace:
clean: all
displayName: Terraform Build
steps:
# Declare the repos needed from the resources list
- checkout: self
- checkout: azure-naming-convention
# Remotely fetch pipeline template, in this case, I am using one in my development repo.
- template: /templates/terraform-cicd-template.yml@azure-pipelines-module-development-build
parameters:
SHORTHAND_PROJECT_NAME: ${{ parameters.SHORTHAND_PROJECT_NAME }} # Parameters entered in YAML
SHORTHAND_ENVIRONMENT_NAME: ${{ parameters.SHORTHAND_ENVIRONMENT_NAME }}
SHORTHAND_LOCATION_NAME: ${{ parameters.SHORTHAND_LOCATION_NAME }}
TERRAFORM_PATH: ${{ parameters.TERRAFORM_PATH }}
TERRAFORM_VERSION: ${{ parameters.TERRAFORM_VERSION }}
TERRAFORM_DESTROY: ${{ parameters.TERRAFORM_DESTROY }}
TERRAFORM_PLAN_ONLY: ${{ parameters.TERRAFORM_PLAN_ONLY }}
TERRAFORM_STORAGE_RG_NAME: $(SpokeSaRgName) # Key Vault variable
TERRAFORM_STORAGE_ACCOUNT_NAME: $(SpokeSaName)
TERRAFORM_BLOB_CONTAINER_NAME: $(SpokeSaBlobContainerName)
TERRAFORM_STORAGE_KEY: $(SpokeSaPrimaryKey)
TERRAFORM_STATE_NAME: "${{ parameters.SHORTHAND_PROJECT_NAME }}-${{ parameters.SHORTHAND_LOCATION_NAME }}.terraform.tfstate"
TERRAFORM_WORKSPACE_NAME: $(System.StageName)
TERRAFORM_COMPLIANCE_PATH: "$(Build.SourcesDirectory)/azure-naming-convention/az-terraform-compliance-policy"
AZURE_TARGET_CLIENT_ID: $(SpokeSvpClientId)
AZURE_TARGET_CLIENT_SECRET: $(SpokeSvpClientSecret)
AZURE_TARGET_TENANT_ID: $(SpokeTenantId)
AZURE_TARGET_SUBSCRIPTION_ID: $(SpokeSubID)
CHECKOV_SKIP_TESTS: ${{ parameters.CHECKOV_SKIP_TESTS }}
Use the main copy
Want to use the copy held here rather than copying it locally? Try something like this
---
name: $(Build.DefinitionName)-$(date:yyyyMMdd)$(rev:.r)
trigger: none
# parameters are typed with defaults so they are correctly populated, you will get a choice in the GUI to edit these, but you should keep all changes as code.
parameters:
- name: SHORTHAND_ENVIRONMENT_NAME
default: dev
displayName: "What is the shorthand name for this environment?"
type: string
values:
- dev
- poc
- mvp
- tst
- uat
- ppd
- prd
- name: SHORTHAND_PROJECT_NAME
type: string
default: "ldo"
displayName: "Shorthand Project e.g. lbdo for libredevops"
- name: SHORTHAND_LOCATION_NAME
type: string
default: "euw"
displayName: "3 character location name, e.g., uks, ukw, euw"
- name: TERRAFORM_PATH
type: string
default: "$(Build.SourcesDirectory)/azure-pipelines-module-development-build/terraform"
displayName: "What is the path to your terraform code?"
- name: TERRAFORM_VERSION
type: string
default: "1.1.7"
displayName: "Which version of Terraform should be installed?"
- name: VARIABLE_GROUP_NAME
type: string
default: "svp-kv-ldo-euw-dev-mgt-01"
displayName: "Enter the variable group which contains your authentication information"
# This variable sets up a condition in the template, if set to true, it will run terraform plan -destroy instead of the normal plan
- name: TERRAFORM_DESTROY
default: false
displayName: "Check box to run a Destroy"
type: boolean
- name: TERRAFORM_PLAN_ONLY
default: true
displayName: "Check box to run plan ONLY and never run apply"
type: boolean
- name: CHECKOV_SKIP_TESTS
type: string
default: ' '
displayName: "CheckOV tests to skip if comment skips don't work. All checks run if parameter is empty, empty by default"
# Declare variable group to pass variables to parameters, in this case, a libre-devops keyvault which is using a service principle for authentication
variables:
- group: ${{ parameters.VARIABLE_GROUP_NAME }}
# Sets what repos need cloned, for example, a library repo for modules and a poly-repo for target code
resources:
repositories:
- repository: azure-naming-convention
type: github
endpoint: github_service_connection
name: libre-devops/azure-naming-convention
ref: main
# You only need to declare a remote repository if its a GitHub repo, otherwise, you can just use the - self task to clone an Azure DevOps repo.
- repository: dev-build
type: github
endpoint: github_service_connection
name: libre-devops/dev-build
ref: dev
- repository: terraform-azdo-pipeline-template
type: github
endpoint: github_service_connection
name: libre-devops/terraform-azdo-pipeline-template
ref: main
# You may wish to use a separate or self-hosted agent per job, by default, all jobs will inherit stage agent
pool:
name: Azure Pipelines
vmImage: ubuntu-latest
# Sets stage so that multiple stages can be used if needed, as it stands, only 1 stage is expected and is thus passed as a parameter
stages:
- stage: "${{ parameters.SHORTHAND_ENVIRONMENT_NAME }}"
displayName: "${{ parameters.SHORTHAND_ENVIRONMENT_NAME }} Stage"
jobs:
- job: Terraform_Build
workspace:
clean: all
displayName: Terraform Build
steps:
# Declare the repos needed from the resources list
- checkout: self
- checkout: azure-naming-convention
# Remotely fetch pipeline template, in this case, I am using one in my development repo.
- template: /.azurepipelines/.templates/terraform-cicd-template.yml@terraform-azdo-pipeline-template
parameters:
SHORTHAND_PROJECT_NAME: ${{ parameters.SHORTHAND_PROJECT_NAME }} # Parameters entered in YAML
SHORTHAND_ENVIRONMENT_NAME: ${{ parameters.SHORTHAND_ENVIRONMENT_NAME }}
SHORTHAND_LOCATION_NAME: ${{ parameters.SHORTHAND_LOCATION_NAME }}
TERRAFORM_PATH: ${{ parameters.TERRAFORM_PATH }}
TERRAFORM_VERSION: ${{ parameters.TERRAFORM_VERSION }}
TERRAFORM_DESTROY: ${{ parameters.TERRAFORM_DESTROY }}
TERRAFORM_PLAN_ONLY: ${{ parameters.TERRAFORM_PLAN_ONLY }}
TERRAFORM_STORAGE_RG_NAME: $(SpokeSaRgName) # Key Vault variable
TERRAFORM_STORAGE_ACCOUNT_NAME: $(SpokeSaName)
TERRAFORM_BLOB_CONTAINER_NAME: $(SpokeSaBlobContainerName)
TERRAFORM_STORAGE_KEY: $(SpokeSaPrimaryKey)
TERRAFORM_STATE_NAME: "${{ parameters.SHORTHAND_PROJECT_NAME }}-${{ parameters.SHORTHAND_LOCATION_NAME }}.terraform.tfstate"
TERRAFORM_WORKSPACE_NAME: $(System.StageName)
TERRAFORM_COMPLIANCE_PATH: "$(Build.SourcesDirectory)/azure-naming-convention/az-terraform-compliance-policy"
AZURE_TARGET_CLIENT_ID: $(SpokeSvpClientId)
AZURE_TARGET_CLIENT_SECRET: $(SpokeSvpClientSecret)
AZURE_TARGET_TENANT_ID: $(SpokeTenantId)
AZURE_TARGET_SUBSCRIPTION_ID: $(SpokeSubID)
CHECKOV_SKIP_TESTS: ${{ parameters.CHECKOV_SKIP_TESTS }}
Source: docs/quickstart/utils/azdo-example-terraform-template.md