Reusable pipeline components for CNP Azure DevOps pipelines
The template files below contain steps to add Terraform Init/Plan/Apply/Destroy tasks to a pipeline.
├── scripts
├── steps
│ └── terraform-precheck.yaml # Precheck stage tasks
│ └── terraform.yaml # Terraform plan and apply stage tasks
├── tasks
├── vars
-
Create the required folder structure in your repository
-
Add the cnp-azure-devops-libraries repository resource as below
resources: repositories: - repository: cnp-azuredevops-libraries type: github ref: refs/heads/master name: hmcts/cnp-azuredevops-libraries endpoint: 'hmcts (1)'
-
Use the vars/input-variables.yaml template to add the common variables to your pipeline.
Make sure you use the correct syntax when declaring a mixture of regular variables and templates, like below.
Syntax example
variables: # a regular variable - name: myvariable value: myvalue # a variable group - group: myvariablegroup # a reference to a variable template - template: myvariabletemplate.yml
Full example
variables: - name: timeoutInMinutes value: 60 - name: agentPool value: ubuntu-latest - name: build value: $(Build.BuildNumber) - name: product value: cft-platform - name: terraformInitSubscription value: b8d29n39-8007-49m0-95b8-3c8691e90kb - template: vars/input-variables.yaml@cnp-azuredevops-libraries
More information on the correct syntax when using regular variables, variables groups and templates.
-
Add the terraform-precheck.yaml template to a
Precheck
stage -
Add the terraform.yaml template to a
TerraformPlanApply
stage -
First time pipeline run:
- Run build with the Terraform plan option. State file will be created in new location
- Copy state file from old location to overwrite new state file
- Run build with Terraform plan to confirm plan reflects migrated state file
-
Run pipeline with plan/apply option as required
- In storage accounts in the
HMCTS-CONTROL
subscription - Storage account name derived from the resources subscription id as below:
'c' + '1st-8th character of subscription id' + '25th-36th character of subscription id' + 'sa' e.g. 'cb72ab7b703b0f2c6a1bbsa'
- Stored in the 'subscription-tfstate' storage container in the folder path derived as below:
'location/product/build repo name/environment/component name/terraform.tfstate' e.g. 'UK South/cft-platform/azure-platform-terraform/sbox/shutter/terraform.tfstate'
Template requires the below folder structure for the build repository.
Repo
├── components
│ └── <a> (e.g. network) # group of .tf files
│ │ └── .terraform-version (symlink) # link to .terraform-version at root level (for local testing)
│ │ │ command: ln -s ../../.terraform-version .terraform-version
│ │ └── *.tf
│ └── <n>
├── environments # Environment specific .tfvars files
│ └── <env>
│ │ └── *.tfvars
├── azure_pipeline.yaml
├── .terraform-version # terraform version (read by tfenv)
Some repositories have separate tfvars
files based on region i.e. UK South
or UK West
and having all enviroment
variables in a single tfvars
file may not meet the requirement.
If you need to use a different set of variables for your build then you can pass the multiRegion
parameter and
set this as true
, the default is false
.
Using multiple region support requires the below environments folder structure for the build repository.
Repo
├── environments # Environment specific .tfvars files
│ └── <env>
│ │ └── <location>.tfvars # Region specific tfvars file without spaces e.g. prod-ukwest.tfvars
With this a different variable file will be used. An example can be found in the hub-panorama repo.
Some repositories do not have the tfvars
file in the standard location, or do not need tfvars
file at all. In such cases, the tfVarsFile
option can be used to specify this
If tfvars
file is in a non-standard location, the tfVarsFile
option can be used to specify the full path of the tfvars
file, as shown below
tfVarsFile: "$(System.DefaultWorkingDirectory)/$(buildRepoSuffix)/environments/network/${{ parameters.env }}.tfvars"
If the component does not need a tfvars
file, then a special NULL
string (all caps) can be specified for tfVarsFile
, as shown below.
tfVarsFile: NULL
Note: This is different from the terraform reserved word null
and is essentially a special string to indicate that no tfvars
file is needed.
In a monorepo, it may be needed to override the components folder if there's multiple applications that are built from the same repository. You can pass the baseDirectory
option to specify the location of the components folder.
baseDirectory: "terraform/network/components"
You can pass environment variables directly to terraform tasks (plan, apply, destroy) Which then can be used as variable within terraform code as shown in below example:
- template: steps/terraform.yaml@cnp-azuredevops-libraries
parameters:
overrideAction: ${{ parameters.overrideAction }}
environment: ${{ deployment.environment }}
component: ${{ deployment.component }}
serviceConnection: ${{ deployment.service_connection }}
terraformInitSubscription: ${{ variables.terraformInitSubscription }}
product: ${{ variables.product }}
terraformEnvironmentVariables:
TF_VAR_foo: $(bar)
In terraform you can then reference this variable as var.foo