- Enable a
terraform destroyjob - Run a custom
terraformcommand in a job - Add custom debug tools to jobs
- Add custom container images
- Automatically deploy from the default branch
- Deploy Terraform to multiple environments
Terraform template recipes
You can customize your Terraform integration by adding the recipes on this page to your pipeline.
If you’d like to share your own Terraform configuration, consider contributing a recipe to this page.
Enable a terraform destroy job
Add the following snippet to your .gitlab-ci.yml:
include:
- template: Terraform.latest.gitlab-ci.yml
destroy:
extends: .terraform:destroy
The destroy job is part of the cleanup stage. Like the deploy
job, the destroy job is always manual and is not tied to the
default branch.
To connect the destroy job to the GitLab environment:
include:
- template: Terraform.latest.gitlab-ci.yml
deploy:
envrionment:
name: $TF_STATE_NAME
action: start
on_stop: destroy
destroy:
extends: .terraform:destroy
environment:
name: $TF_STATE_NAME
action: stop
In this configuration, the destroy job is always created. However, you might want to create a destroy job only if certain
conditions are met.
The following configuration creates a destroy job, runs a destroy plan and omits the deploy job only if TF_DESTROY is true:
include:
- template: Terraform.latest.gitlab-ci.yml
build:
rules:
- if: $TF_DESTROY == "true"
variables:
TF_CLI_ARGS_plan: "-destroy"
- when: on_success
deploy:
envrionment:
name: $TF_STATE_NAME
action: start
on_stop: destroy
rules:
- if: $TF_DESTROY == "true"
when: never
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $TF_AUTO_DEPLOY == "true"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual
destroy:
extends: .terraform:destroy
dependencies:
- build
variables:
TF_CLI_ARGS_destroy: "${TF_PLAN_CACHE}"
environment:
name: $TF_STATE_NAME
action: stop
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $TF_DESTROY == "true"
when: manual
This configuration has a known issue: when the destroy job is not in the same pipeline as the deploy job, the on_stop environment action does not work.
Run a custom terraform command in a job
To define a job that runs a custom terraform command, the
gitlab-terraform wrapper can be used in any job:
include:
- template: Terraform.latest.gitlab-ci.yml
state-list:
stage: validate # you can use any stage, just make sure to define it
script: gitlab-terraform state list
The gitlab-terraform command sets up a terraform command and runs
it with the given arguments.
To run this job in the Terraform state-specific resource group,
assign the job with resource_group:
include:
- template: Terraform.latest.gitlab-ci.yml
state-list:
stage: validate # you can use any stage, just make sure to define it
resource_group: ${TF_STATE_NAME}
script: gitlab-terraform state list
Add custom debug tools to jobs
The default image used by Terraform template jobs contains only minimal tooling. However, you might want to add additional tools for debugging.
To add an additional tool:
- Install the tool in the
before_scriptof a job or pipeline. - Use the tool in the
scriptorafter_scriptblock.- If you use the
scriptblock, be sure to re-add the template job commands.
- If you use the
For example, the following snippet installs bash and jq in the before_script for all
jobs in the pipeline:
include:
- template: Terraform.latest.gitlab-ci.yml
default:
before_script: apk add --update bash jq
To add it to only the build and deploy jobs, add it to those jobs directly:
include:
- template: Terraform.latest.gitlab-ci.yml
build:
before_script: apk add --update bash jq
deploy:
before_script: apk add --update bash jq
Add custom container images
For debug tools and simple installations, you should
add a custom debug tool to your job.
If your tool is complex or benefits from caching,
you can create a custom container image based on the
gitlab-terraform images.
You can use your custom image in subsequent Terraform jobs.
To define a custom container image:
-
Define a new
Dockerfilewith custom tooling. For example, installbashandjqin.gitlab/ci/Dockerfile:FROM registry.gitlab.com/gitlab-org/terraform-images/stable:latest RUN apk add --update bash jq - In a new job, define a
preparestage that builds the image whenever theDockerfilechanges.- The built image is pushed to the GitLab Container Registry. A tag is applied to indicate whether the image was built from a merge request or from the default branch.
- Use your image in your Terraform jobs, such as
buildanddeploy.- You can combine your image with specialized
before_scriptconfigurations to perform setup commands, like to generate inputs for Terraform.
- You can combine your image with specialized
For example, a fully functioning pipeline configuration might look like:
include:
- template: Terraform.latest.gitlab-ci.yml
variables:
IMAGE_TAG: latest
workflow:
rules:
- if: $CI_MERGE_REQUEST_IID
changes:
- .gitlab/ci/Dockerfile
variables:
IMAGE_TAG: ${CI_COMMIT_REF_SLUG}
- when: always
stages:
- prepare
- validate
- test
- build
- deploy
- cleanup
prepare:image:
needs: []
stage: prepare
image:
name: gcr.io/kaniko-project/executor:v1.9.0-debug
entrypoint: [""]
rules:
# Tag with the commit SHA if we're in an MR
- if: $CI_MERGE_REQUEST_IID
changes:
- .gitlab/ci/Dockerfile
variables:
DOCKER_TAG: $CI_COMMIT_REF_SLUG
# If we're on our main branch, tag with "latest"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
changes:
- .gitlab/ci/Dockerfile
variables:
DOCKER_TAG: latest
before_script:
# Authenticate to the docker registry and dependency proxy
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json
script:
- /kaniko/executor
--context "${CI_PROJECT_DIR}/.gitlab/ci"
--cache=true
--dockerfile "${CI_PROJECT_DIR}/.gitlab/ci/Dockerfile"
--destination "${CI_REGISTRY_IMAGE}:${DOCKER_TAG}"
build:
image: ${CI_REGISTRY_IMAGE}:${IMAGE_TAG}
deploy:
image: ${CI_REGISTRY_IMAGE}:${IMAGE_TAG}
For an example repository, see the GitLab Terraform template usage project.
Automatically deploy from the default branch
You can automatically deploy from the default branch by setting the TF_AUTO_DEPLOY variable
to "true". All other values are interpreted as "false".
variables:
TF_AUTO_DEPLOY: "true"
include:
- template: Terraform.latest.gitlab-ci.yml
Deploy Terraform to multiple environments
You can run pipelines in multiple environments, each with a unique Terraform state.
stages:
- validate
- test
- build
- deploy
include:
- template: Terraform/Base.latest.gitlab-ci.yml
- template: Jobs/SAST-IaC.latest.gitlab-ci.yml
variables:
# x prevents TF_STATE_NAME from beeing empty for non environment jobs like validate
# wait for https://gitlab.com/groups/gitlab-org/-/epics/7437 to use variable defaults
TF_STATE_NAME: x${CI_ENVIRONMENT_NAME}
TF_CLI_ARGS_plan: "-var-file=vars/${CI_ENVIRONMENT_NAME}.tfvars"
fmt:
extends: .terraform:fmt
validate:
extends: .terraform:validate
plan dev:
extends: .terraform:build
environment:
name: dev
plan prod:
extends: .terraform:build
environment:
name: prod
apply dev:
extends: .terraform:deploy
environment:
name: dev
apply prod:
extends: .terraform:deploy
environment:
name: prod
This configuration is modified from the base GitLab template.