Skip to content

GitLab CI template for Terraform

This project implements a GitLab CI/CD template to manage your infrastructure with Terraform.

Usage

This template can be used both as a CI/CD component or using the legacy include:project syntax.

Use as a CI/CD component

Add the following to your .gitlab-ci.yml:

include:
  # 1: include the component
  - component: $CI_SERVER_FQDN/to-be-continuous/terraform/gitlab-ci-terraform@5.5.5
    # 2: set/override component inputs
    inputs:
      # ⚠ this is only an example
      image: registry.hub.docker.com/hashicorp/terraform:5.3.0
      review-enabled: true
      staging-enabled: true
      prod-enabled: true

Use as a CI/CD template (legacy)

Add the following to your .gitlab-ci.yml:

include:
  # 1: include the template
  - project: 'to-be-continuous/terraform'
    ref: '5.5.5'
    file: '/templates/gitlab-ci-terraform.yml'

variables:
  # 2: set/override template variables
  # ⚠ this is only an example
  TF_IMAGE: registry.hub.docker.com/hashicorp/terraform:5.3.0
  TF_REVIEW_ENABLED: "true"
  TF_STAGING_ENABLED: "true"
  TF_PROD_ENABLED: "true"

Understand

This chapter introduces key notions and principle to understand how this template works.

Managed deployment environments

This template implements continuous deployment on your infrastructure using Terraform.

It allows you to manage creation, update & cleanup of standard predefined environments. Each environment can be enabled/disabled by configuration. If you're not satisfied with predefined environments and/or their associated Git workflow, you may implement you own environments and workflow, by reusing/extending the base (hidden) jobs. This is advanced usage and will not be covered by this documentation.

The following chapters present the managed predefined environments and their associated Git workflow.

Review environments

The template supports review environments: those are dynamic and ephemeral environments to deploy your ongoing developments (a.k.a. feature or topic branches).

When enabled, it deploys the result from upstream build stages to a dedicated and temporary environment. It is only active for non-production, non-integration branches.

It is a strict equivalent of GitLab's Review Apps feature.

It also comes with a cleanup job (accessible either from the environments page, or from the pipeline view).

Integration environment

If you're using a Git Workflow with an integration branch (such as Gitflow), the template supports an integration environment.

When enabled, it deploys the result from upstream build stages to a dedicated environment. It is only active for your integration branch (develop by default).

Production environments

Lastly, the template supports 2 environments associated to your production branch (main or master by default):

  • a staging environment (an iso-prod environment meant for testing and validation purpose),
  • the production environment.

You're free to enable whichever or both, and you can also choose your deployment-to-production policy:

  • continuous deployment: automatic deployment to production (when the upstream pipeline is successful),
  • continuous delivery: deployment to production can be triggered manually (when the upstream pipeline is successful).

Template jobs

The Terraform template implements - for each environment presented above - the following jobs:

  • environment creation/update plan: this job is optional and computes the environment changes before applying them. When enabled, the env creation has to be applied manually. By default, this job is enabled only for the production environment.
  • environment creation/update apply: applies the environment changes, whether manually by applying the upstream plan (when enabled), or automatically.
  • environment destruction: can be executed manually on non-production envs only.

Hook scripts

Terraform jobs also support optional hook scripts from your project, located in the $TF_SCRIPTS_DIR directory (root project dir by default, but may be overridden).

  • tf-pre-init.sh is executed before running terraform init
  • tf-pre-apply.sh is executed before running terraform apply
  • tf-post-apply.sh is executed after running terraform apply
  • tf-pre-destroy.sh is executed before running terraform destroy
  • tf-post-destroy.sh is executed after running terraform destroy

Terraform commands overrides

Instead of creating hook scripts, you can also override and/or decorate the Terraform commands using predefined .tf-commands template block, referenced by the !reference directive.

By default, the .tf-commands, block is composed as below:

.tf-commands:
  init:
    - !reference [ .tf-commands, default, init ]
  plan:
    - !reference [ .tf-commands, default, plan ]
  apply:
    - !reference [ .tf-commands, default, apply ]
  destroy:
    - !reference [ .tf-commands, default, destroy ]

You can override it for example in the following way:

.tf-commands:
  init:
    - source sandbox.env
    - !reference [ .tf-commands, default, init ]
    - echo "I'm executed after the terraform init command"

You can use this mechanism to source to the current shell your own environmental variables.

Deployment context variables

In order to manage the various deployment environments, this template provides a couple of dynamic variables that you might use in your hook scripts or Terraform scripts (as input variables):

  • environment_type: the current deployment environment type (review, integration, staging or production)
  • environment_name (equals $CI_ENVIRONMENT_NAME): the full environment name (ex: review/fix-prometheus-configuration, integration, staging or production)
  • environment_slug (equals $CI_ENVIRONMENT_SLUG): the sluggified environment name (ex: review-fix-promet-r13zmu, integration, staging or production)

Using variables

You have to be aware that your Terraform code has to be able to cope with various environments (review, integration, staging and production), each with different application names, exposed routes, settings, ...

Part of this complexity can be handled by using Terraform variables (in your Terraform files), and environment variables (in your hook scripts):

  1. deployment context variables provided by the template:
    • environment_type: the current deployment environment type (review, integration, staging or production)
    • environment_name (equals $CI_ENVIRONMENT_NAME): the full environment name (ex: review/fix-prometheus-configuration, integration, staging or production)
    • environment_slug (equals $CI_ENVIRONMENT_SLUG): the sluggified environment name (ex: review-fix-promet-r13zmu, integration, staging or production)
  2. use tfvars files for non-secret configuration:
    • default terraform.tfvars[.json] and *.auto.tfvars[.json] files are obviously supported by Terraform,
    • the template also auto-detects any file named $environment_type.env.tfvars[.json] (ex: staging.env.tfvars for staging environment) and uses it with all related terraform commands.
  3. any predefined GitLab CI variable may be freedly used in your hook scripts or extra options variables (ex: TF_EXTRA_OPTS: "-var project_name=$CI_PROJECT_NAME")
  4. you may also use custom GitLab variables to pass values to your hook script or even directly as Terraform variables using the right syntax (ex: env variable $TF_VAR_ssh_private_key_file will be visible as ssh_private_key_file Terraform variable in your code)

πŸ’‘ When managing multiple environments, it is a good practice to prefix your Terraform resource names with environment_slug variable.

Example:

resource "aws_instance" "web_server" {
  name = "myproj_${var.environment_slug}_web_server"
  ...
}

How to manage separate values per environment?

It may happen that you need to use different configuration variables depending on the environment you are deploying. For instance separate GOOGLE_CREDENTIALS if you're using Google Provider.

For this, you shall either use GitLab scoped variables or our scoped variables syntax to limit/override some variables values, using $CI_ENVIRONMENT_NAME as the conditional variable.

Example: different OpenStack provider configuration for production

variables:
  # global OpenStack provider configuration
  OS_AUTH_URL: "https://openstack-nonprod.api.domain/v3"
  OS_TENANT_ID: "my-nonprod-tenant"
  OS_TENANT_NAME: "my-project-nonprod"
  OS_INSECURE: "true"
  # OS_USERNAME & OS_PASSWORD are defined as secret GitLab CI variables

  # overridden configuration for production
  scoped__OS_AUTH_URL__if__CI_ENVIRONMENT_NAME__equals__production: "https://openstack-prod.api.domain/v3"
  scoped__OS_TENANT_ID__if__CI_ENVIRONMENT_NAME__equals__production: "my-prod-tenant"
  scoped__OS_TENANT_NAME__if__CI_ENVIRONMENT_NAME__equals__production: "my-project-prod"
  scoped__OS_INSECURE__if__CI_ENVIRONMENT_NAME__equals__production: "false"
  # OS_USERNAME & OS_PASSWORD are overridden as secret GitLab CI variables

Supported output artifacts

The Terraform template supports job artifacts that your Terraform code may generate and need to propagate to downstream jobs:

  • $TF_OUTPUT_DIR directory and all contained files are stored as job artifacts. Allows to propagate files.
  • If present, the terraform.env file is stored as a dotenv artifact. Allows to propagate environment variables.

Examples:

  • When used in conjuction with Ansible template, your Terraform script may generate the Ansible inventory file into the $TF_OUTPUT_DIR directory.
  • When dynamically obtaining a floating IP address, your Terraform script may generate the terraform.env file to propated it as an environment variables.

Terraform integration in Merge Requests

This template enables Terraform integration in Merge Requests.

As a result if you enabled your production environment, every merge request will compute and display infrastructure changes compared to master branch.

Terraform Backend management

GitLab managed Terraform State (default)

By default, this template enables GitLab managed Terraform State.

As mentionned in GitLab's documentation, that requires that your Terraform scripts declare the (unconfigured) Terraform HTTP backend, and the template will do the necessary to configure it automatically.

All you have to do if you want it is to add the following in one of your .tf files:

terraform {
  # using GitLab http backend
  backend "http" {
    # auto-configured by the template
  }
}

This default behavior can also be disabled by setting $TF_GITLAB_BACKEND_DISABLED to true. In that case, you'll have to declare and configure your backend and tfstate by yourself (see Implicit Backend configuration support below).

How to use GitLab backend in your development environment ?

First create a Project Access Token or Personal Access Token.

In your shell terminal, execute the following script:

#!/bin/bash

# TODO: replace 3 next variables
MY_PROJECT_PATH="path/to/my-project"
MY_ENV_NAME="dev"
TF_HTTP_PASSWORD="YOUR-ACCESS-TOKEN"

CI_API_V4_URL=https://gitlab.com/api/v4
CI_PROJECT_ID=${MY_PROJECT_PATH//\//%2f}

TF_HTTP_ADDRESS="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/$MY_ENV_NAME"
TF_HTTP_LOCK_ADDRESS="${TF_HTTP_ADDRESS}/lock"
TF_HTTP_LOCK_METHOD="POST"
TF_HTTP_UNLOCK_ADDRESS="${TF_HTTP_ADDRESS}/lock"
TF_HTTP_UNLOCK_METHOD="DELETE"
TF_HTTP_USERNAME="gitlab-token"
TF_HTTP_RETRY_WAIT_MIN="5"

terraform -v

terraform init \
    -reconfigure \
    -backend-config=address="${TF_HTTP_ADDRESS}" \
    -backend-config=lock_address="${TF_HTTP_LOCK_ADDRESS}" \
    -backend-config=unlock_address="${TF_HTTP_UNLOCK_ADDRESS}" \
    -backend-config=username="${TF_HTTP_USERNAME}" \
    -backend-config=password="${TF_HTTP_PASSWORD}" \
    -backend-config=lock_method="${TF_HTTP_LOCK_METHOD}" \
    -backend-config=unlock_method="${TF_HTTP_UNLOCK_METHOD}" \
    -backend-config=retry_wait_min="${TF_HTTP_RETRY_WAIT_MIN}"

Implicit Backend configuration support

If you disabled the GitLab-managed Terraform state (by setting $TF_GITLAB_BACKEND_DISABLED to true), the template supports an implicit backend configuration mechanism:

  1. Looks for a $environment_type.tfbackend file (ex: staging.tfbackend for staging environment),
  2. Fallbacks to default.tfbackend file.

If one of those files are found, it is automatically used by the template in the terraform init command (using the -backend-config CLI option).

Workspace management

You may want to make use of Terraform Workspace to ease segregating you multiple environments (tfstate management) by setting variables:

  • $TF_WORKSPACE to set default workspace,
  • $TF_REVIEW_WORKSPACE, $TF_INTEG_WORKSPACE, $TF_STAGING_WORKSPACE, $TF_PROD_WORKSPACE to override per environment.

Be aware of the following:

  • each of those variables support the value auto: in that case, the template will use the dynamic $environment_slug value as workspace name,
  • if the specified workspace doesn't exist, the template will create it,
  • HTTP backend doesn't support Workspaces. See supported backends here.
  • corollary as GitLab http backend is on by default workspace selection is explicitly bypassed unless gitlab-backend-disabled / TF_GITLAB_BACKEND_DISABLED is set to true. If kept enabled and if any of the TF_WORKSPACE variable is set the template will warn about it.
  • if using another (non-Gitlab) http backend nothing will prevent from trying to use workspaces, in that case you'll get a Terraform error workspaces not supported.

Using modules from private registries

The Terraform template supports using modules from private registries (GitLab's Registry or others).

Modules can be refered as usual in your Terraform code:

module "<module>" {
  source = "tf.registry.address/organization/provider/module_name"
}

And finally authentication credentials shall be defined as secret environment variable credentials. In the above example, that would mean defining a πŸ”’ TF_TOKEN_tf_registry_address project variable containing the authentication token.

⚠ by default the template automatically sets the authentication token for the GitLab Modules Registry using the $CI_JOB_TOKEN value. If you want to use another credential (personal access token or else), just define explicitly πŸ”’ TF_TOKEN_gitlab_com (or the right one for your GitLab server) as a project variable with the desired credential.

Terraform lock file

As explained in Terraform documentation, you should include the .terraform.lock.hcl file in your version control repository.

This will guarantee the Terraform template is using exactly the same dependencies version as you are using in your local development environment.

Configuration reference

Secrets management

Here are some advices about your secrets (variables marked with a πŸ”’):

  1. Manage them as project or group CI/CD variables:
    • masked to prevent them from being inadvertently displayed in your job logs,
    • protected if you want to secure some secrets you don't want everyone in the project to have access to (for instance production secrets).
  2. In case a secret contains characters that prevent it from being masked, simply define its value as the Base64 encoded value prefixed with @b64@: it will then be possible to mask it and the template will automatically decode it prior to using it.
  3. Don't forget to escape special characters (ex: $ -> $$).

Global configuration

The Terraform template uses some global configuration used throughout all jobs.

Input / Variable Description Default value
image / TF_IMAGE the Docker image used to run Terraform CLI commands
⚠ set the version required by your project
registry.hub.docker.com/hashicorp/terraform:latest
gitlab-backend-disabled / TF_GITLAB_BACKEND_DISABLED Set to true to disable GitLab managed Terraform State none (enabled)
project-dir / TF_PROJECT_DIR Terraform project root directory .
scripts-dir / TF_SCRIPTS_DIR Terraform (hook) scripts base directory (relative to $TF_PROJECT_DIR) .
output-dir / TF_OUTPUT_DIR Terraform output directory (relative to $TF_PROJECT_DIR). Everything generated in this directory will be kept as job artifacts. tf-output
extra-opts / TF_EXTRA_OPTS Default Terraform extra options (applies to all Terraform commands) none
init-opts / TF_INIT_OPTS Default Terraform extra init options none
workspace / TF_WORKSPACE Default Terraform project workspace none (do not use workspaces)
apply-opts / TF_APPLY_OPTS Default Terraform extra apply options none
destroy-opts / TF_DESTROY_OPTS Default Terraform extra destroy options none

Review environments configuration

Review environments are dynamic and ephemeral environments to deploy your ongoing developments (a.k.a. feature or topic branches).

They are disabled by default and can be enabled by setting the TF_REVIEW_ENABLED variable (see below).

Here are variables supported to configure review environments:

Input / Variable Description Default value
review-enabled / TF_REVIEW_ENABLED Set to true to enable your review environments none (disabled)
review-extra-opts / TF_REVIEW_EXTRA_OPTS Terraform extra options for review env (applies to all Terraform commands) $TF_EXTRA_OPTS
review-init-opts / TF_REVIEW_INIT_OPTS Terraform extra init options for review env $TF_INIT_OPTS
review-workspace / TF_REVIEW_WORKSPACE Terraform project workspace for review env $TF_WORKSPACE
review-plan-enabled / TF_REVIEW_PLAN_ENABLED Set to true to enable separate Terraform plan job for review env. none (disabled)
review-plan-opts / TF_REVIEW_PLAN_OPTS Terraform extra plan options for review env none
review-apply-opts / TF_REVIEW_APPLY_OPTS Terraform extra apply options for review env $TF_APPLY_OPTS
review-destroy-opts / TF_REVIEW_DESTROY_OPTS Terraform extra destroy options for review env $TF_DESTROY_OPTS
review-autostop-duration / TF_REVIEW_AUTOSTOP_DURATION The amount of time before GitLab will automatically stop review environments 4 hours

Enabling auto-cleanup

GitLab provides a way to automatically Stop an environment when a merge request is merged or closed.

It is not enabled by default in the Terraform template, mainly because it is not easy to undo.

Here is what you should add to your .gitlab-ci.yml file to enable it for review environments:

# auto cleanup review environments (when MR is merged or closed)
tf-review:
  environment:
    on_stop: tf-destroy-review

Integration environment configuration

The integration environment is the environment associated to your integration branch (develop by default).

It is disabled by default and can be enabled by setting the TF_INTEG_ENABLED variable (see below).

Here are variables supported to configure the integration environment:

Input / Variable Description Default value
integ-enabled / TF_INTEG_ENABLED Set to true to enable your integration env none (disabled)
integ-extra-opts / TF_INTEG_EXTRA_OPTS Terraform extra options for integration env (applies to all Terraform commands) $TF_EXTRA_OPTS
integ-init-opts / TF_INTEG_INIT_OPTS Terraform extra init options for integration env $TF_INIT_OPTS
integ-workspace / TF_INTEG_WORKSPACE Terraform project workspace for integration env $TF_WORKSPACE
integ-plan-enabled / TF_INTEG_PLAN_ENABLED Set to true to enable separate Terraform plan job for integration env. none (disabled)
integ-plan-opts / TF_INTEG_PLAN_OPTS Terraform extra plan options for integration env none
integ-apply-opts / TF_INTEG_APPLY_OPTS Terraform extra apply options for integration env $TF_APPLY_OPTS
integ-destroy-opts / TF_INTEG_DESTROY_OPTS Terraform extra destroy options for integration env $TF_DESTROY_OPTS
integ-autostop-duration / TF_INTEG_AUTOSTOP_DURATION The amount of time before GitLab will automatically stop the integration env never

Staging environment configuration

The staging environment is an iso-prod environment meant for testing and validation purpose associated to your production branch (main or master by default).

It is disabled by default and can be enabled by setting the TF_STAGING_ENABLED variable (see below).

Here are variables supported to configure the staging environment:

Input / Variable Description Default value
staging-enabled / TF_STAGING_ENABLED Set to true to enable your staging env none (disabled)
staging-extra-opts / TF_STAGING_EXTRA_OPTS Terraform extra options for staging env (applies to all Terraform commands) $TF_EXTRA_OPTS
staging-init-opts / TF_STAGING_INIT_OPTS Terraform extra init options for staging env $TF_INIT_OPTS
staging-workspace / TF_STAGING_WORKSPACE Terraform project workspace for staging env $TF_WORKSPACE
staging-plan-enabled / TF_STAGING_PLAN_ENABLED Set to true to enable separate Terraform plan job for staging env. none (disabled)
staging-plan-opts / TF_STAGING_PLAN_OPTS Terraform extra plan options for staging env none
staging-apply-opts / TF_STAGING_APPLY_OPTS Terraform extra apply options for staging env $TF_APPLY_OPTS
staging-destroy-opts / TF_STAGING_DESTROY_OPTS Terraform extra destroy options for staging env $TF_DESTROY_OPTS
staging-autostop-duration / TF_STAGING_AUTOSTOP_DURATION The amount of time before GitLab will automatically stop the staging env never

Production environment configuration

The production environment is the final deployment environment associated with your production branch (main or master by default).

It is disabled by default and can be enabled by setting the TF_PROD_ENABLED variable (see below).

Here are variables supported to configure the production environment:

Input / Variable Description Default value
prod-enabled / TF_PROD_ENABLED Set to true to enable your production env none (disabled)
prod-extra-opts / TF_PROD_EXTRA_OPTS Terraform extra options for production env (applies to all Terraform commands) $TF_EXTRA_OPTS
prod-init-opts / TF_PROD_INIT_OPTS Terraform extra init options for production env $TF_INIT_OPTS
prod-workspace / TF_PROD_WORKSPACE Terraform project workspace for production env $TF_WORKSPACE
prod-plan-enabled / TF_PROD_PLAN_ENABLED Set to true to enable separate Terraform plan job for production env. true (enabled)
prod-plan-opts / TF_PROD_PLAN_OPTS Terraform extra plan options for production env none
prod-apply-opts / TF_PROD_APPLY_OPTS Terraform extra apply options for production env $TF_APPLY_OPTS

tf-tflint job

tflint is a Terraform Linter and uses the following variables:

Input / Variable Description Default value
tflint-image / TF_TFLINT_IMAGE the Docker image used to run tflint ghcr.io/terraform-linters/tflint-bundle:latest
tflint-disabled / TF_TFLINT_DISABLED Set to true to disable tflint none (enabled)
tflint-args / TF_TFLINT_ARGS tflint extra options and args --enable-plugin=google --enable-plugin=azurerm --enable-plugin=aws --recursive

In addition to a textual report in the console, this job produces the following reports, kept for one day:

Report Format Usage
$TF_PROJECT_DIR/reports/tflint.xunit.xml xUnit test report(s) GitLab integration

[DEPRECATED] tf-tfsec job

⚠ tfsec has been deprecated, it is recommended to use trivy instead.

tfsec uses static analysis of your terraform templates to spot potential security issues and uses the following variables:

Input / Variable Description Default value
tfsec-image / TF_TFSEC_IMAGE the Docker image used to run tfsec registry.hub.docker.com/aquasec/tfsec-ci
tfsec-enabled / TF_TFSEC_ENABLED Set to true to enable tfsec none (disabled)
tfsec-args / TF_TFSEC_ARGS tfsec options and args .

In addition to a textual report in the console, this job produces the following reports, kept for one day and only available for download by users with the Developer role or higher:

Report Format Usage
$TF_PROJECT_DIR/reports/tfsec.xunit.xml xUnit test report(s) GitLab integration
$TF_PROJECT_DIR/reports/tfsec.native.json tfsec JSON DefectDojo integration
This report is generated only if DefectDojo template is detected

tf-trivy job

trivy is used to perform static analysis of your terraform templates to spot potential security issues and uses the following variables:

Input / Variable Description Default value
trivy-image / TF_TRIVY_IMAGE the Docker image used to run trivy registry.hub.docker.com/aquasec/trivy
trivy-disabled / TF_TRIVY_DISABLED Set to true to disable trivy none (enabled)
trivy-args / TF_TRIVY_ARGS trivy config options and args .

In addition to a textual report in the console, this job produces the following reports, kept for one day and only available for download by users with the Developer role or higher:

Report Format Usage
$TF_PROJECT_DIR/reports/tf-trivy.codeclimate.json Code Climate GitLab integration
$TF_PROJECT_DIR/reports/tf-trivy.trivy.json Trivy JSON report DefectDojo integration
This report is generated only if DefectDojo template is detected

tf-checkov job

checkov is a static code analysis tool for infrastructure-as-code and uses the following variables:

Input / Variable Description Default value
checkov-image / TF_CHECKOV_IMAGE the Docker image used to run checkov registry.hub.docker.com/bridgecrew/checkov
checkov-enabled / TF_CHECKOV_ENABLED Set to true to enable checkov none (disabled)
checkov-args / TF_CHECKOV_ARGS additional checkov options and args --framework terraform

Command line arguments for checkov are the result of the concatenation of --directory . and $TF_CHECKOV_ARGS. As a consequence additional --directory or --file arguments will be ignored.

In addition to a textual report in the console, this job produces the following reports, kept for one day and only available for download by users with the Developer role or higher:

Report Format Usage
$TF_PROJECT_DIR/reports/checkov.xunit.xml JUnit XML GitLab integration
$TF_PROJECT_DIR/reports/checkov.native.json checkov JSON DefectDojo integration
This report is generated only if DefectDojo template is detected

You can skip checkov specific check adding following comment in code :

resource "aws_s3_bucket" "foo-bucket" {
  region        = var.region
    #checkov:skip=CKV_AWS_20:The bucket is a public static content host
  bucket        = local.bucket_name
  force_destroy = true
  acl           = "public-read"
}

tf-infracost job

Infracost shows cloud cost estimates for infrastructure-as-code projects and uses the following variables:

Input / Variable Description Default value
infracost-enabled / TF_INFRACOST_ENABLED Set to true to enable infracost none (disabled)
infracost-image / TF_INFRACOST_IMAGE the infracost container image registry.hub.docker.com/infracost/infracost
infracost-args / TF_INFRACOST_ARGS infracost CLI options and args breakdown
infacost-usage-file / TF_INFACOST_USAGE_FILE infracost usage file infracost-usage.yml
πŸ”’ INFRACOST_API_KEY the infracost API key required

To use infracost, an api key is needed. To obtain it run :

docker run -it --name infracost infracost/infracost register
Please enter your name and email address to get an API key.
See our FAQ (https://www.infracost.io/docs/faq) for more details.
Name: Your Name
βœ” Email: you_email@domainβ–ˆ

Thank you !
Your API key is: api_key

Save the API key as πŸ”’ INFRACOST_API_KEY GitLab secret variable.

Set INFRACOST_CURRENCY variable to set currency ISO 4217 prices should be converted to. Defaults to USD.

tf-fmt job

The terraform fmt command is usually used to format Terraform source files to a canonical format and style.

This job uses the check mode, and fails if any file appears not being properly formatted. It uses the following variables:

Input / Variable Description Default value
fmt-enabled / TF_FMT_ENABLED Set to true to enable terraform fmt none (disabled)
fmt-args / TF_FMT_ARGS terraform fmt options -diff -recursive

tf-validate job

The terraform validate command is usually used to validate the configuration files in a directory. It is very useful to check a terraform module.

Input / Variable Description Default value
validate-enabled / TF_VALIDATE_ENABLED Set to true to enable terraform validate none (disabled)

tf-docs job

Build Terraform documentation based on terraform docs.

Input / Variable Description Default value
docs-enabled / TF_DOCS_ENABLED Set to true to enable terraform docs none (disabled)
docs-image / TF_DOCS_IMAGE terraform docs container image quay.io/terraform-docs/terraform-docs:edge
docs-extra-opts / TF_DOCS_EXTRA_OPTS Extra terraform docs option none
docs-config / TF_DOCS_CONFIG terraform docs configuration file (relative to $TF_PROJECT_DIR) .terraform-docs.yml
docs-output-dir / TF_DOCS_OUTPUT_DIR terraform docs output directory (relative to $TF_PROJECT_DIR). docs

In order to generate your documentation with the right format and options, you should add a terraform docs configuration file (.terraform-docs.yml) at the root of your Terraform project ($TF_PROJECT_DIR) in your Git repository. As long as you don't, this job will simply generate the pretty doc format in the output console.

tf-publish-module job

Publish your Terraform project as a module to GitLab's Terraform Module Registry.

When enabled, this job triggers on a Git tag with semantic version pattern (v?[0-9]+\.[0-9]+\.[0-9]+, configurable) and publishes the module with the same version.

Input / Variable Description Default value
publish-enabled / TF_PUBLISH_ENABLED Set to true to enable Terraform Module Publish none (disabled)
publish-image / TF_PUBLISH_IMAGE Container image used to publish module. registry.hub.docker.com/curlimages/curl:latest
module-name / TF_MODULE_NAME The module name. May not contain any spaces or underscores. $CI_PROJECT_NAME
module-system / TF_MODULE_SYSTEM The module system or provider (example: local, aws, google). local
module-version / TF_MODULE_VERSION The module version. It must be valid according to the semantic versioning specification. $CI_COMMIT_TAG (leave default unless you have good reasons to override)
module-files / TF_MODULE_FILES Glob patterns matching files to include into the Terraform module (⚠ does not support double star). *.tf *.tpl *.md

Example: publish module with sub-modules

By default the template has the right configuration to publish a single module Terraform project (with tf, tpl and md files).

If you're willing to publish a more complex module, possibly with submodules, then you'll have to override the default TF_MODULE_FILES variable, with something like the following:

variables:
  # grab submodules from modules/*, templates from templates/ and examples from examples/
  TF_MODULE_FILES: "*.tf *.md templates/* modules/*/*.tf modules/*/*.md examples/*.tf examples/*.md"

Variants

The Terraform template can be used in conjunction with template variants to cover specific cases.

Vault variant

This variant allows delegating your secrets management to a Vault server.

Configuration

In order to be able to communicate with the Vault server, the variant requires the additional configuration parameters:

Input / Variable Description Default value
TBC_VAULT_IMAGE The Vault Secrets Provider image to use (can be overridden) registry.gitlab.com/to-be-continuous/tools/vault-secrets-provider:latest
vault-base-url / VAULT_BASE_URL The Vault server base API url none
vault-oidc-aud / VAULT_OIDC_AUD The aud claim for the JWT $CI_SERVER_URL
πŸ”’ VAULT_ROLE_ID The AppRole RoleID must be defined
πŸ”’ VAULT_SECRET_ID The AppRole SecretID must be defined

Usage

Then you may retrieve any of your secret(s) from Vault using the following syntax:

@url@http://vault-secrets-provider/api/secrets/{secret_path}?field={field}

With:

Parameter Description
secret_path (path parameter) this is your secret location in the Vault server
field (query parameter) parameter to access a single basic field from the secret JSON payload

Example

include:
  # main template
  - component: $CI_SERVER_FQDN/to-be-continuous/terraform/gitlab-ci-terraform@5.5.5
  # Vault variant
  - component: $CI_SERVER_FQDN/to-be-continuous/terraform/gitlab-ci-terraform-vault@5.5.5
    inputs:
      # audience claim for JWT
      vault-oidc-aud: "https://vault.acme.host"
      vault-base-url: "https://vault.acme.host/v1"
      # $VAULT_ROLE_ID and $VAULT_SECRET_ID defined as a secret CI/CD variable

variables:
    # Secrets managed by Vault
    AWS_ACCESS_KEY_ID: "@url@http://vault-secrets-provider/api/secrets/b7ecb6ebabc231/aws/prod/account?field=access_key_id"
    AWS_SECRET_ACCESS_KEY: "@url@http://vault-secrets-provider/api/secrets/b7ecb6ebabc231/aws/prod/account?field=secret_access_key"

Google Cloud variant

This variant uses Application Default Credentials through the GOOGLE_APPLICATION_CREDENTIALS variable as explained in the Google Terraform provider configuration Running Terraform Outside of Google Cloud using Workload Identity federation.

List of requirements before using this variant:

  1. You must have a Workload Identity Federation Pool and Provider configured,
  2. You must have a Service Account with the roles/iam.workloadIdentityUser IAM role granted to the Workload Identity principal matching your Gitlab project or group,
  3. Optionally, you can set the GOOGLE_CLOUD_PROJECT template variable to define the default Google Cloud project.

The Gitlab documentation has some details about Workload Identity Federation integration.

This blog post about OIDC impersonation through Workload Identify Federation might also be of help.

Configuration

The variant requires the additional configuration parameters:

Input / Variable Description Default value
gcp-oidc-aud / GCP_OIDC_AUD The aud claim for the JWT token $CI_SERVER_URL
gcp-oidc-provider / GCP_OIDC_PROVIDER Default Workload Identity Provider associated with GitLab to authenticate with OpenID Connect none
gcp-oidc-account / GCP_OIDC_ACCOUNT Default Service Account to which impersonate with OpenID Connect authentication none
gcp-review-oidc-provider / GCP_REVIEW_OIDC_PROVIDER Workload Identity Provider associated with GitLab to authenticate with OpenID Connect on review environment (only define to override default) none
gcp-review-oidc-account / GCP_REVIEW_OIDC_ACCOUNT Service Account to which impersonate with OpenID Connect authentication on review environment (only define to override default) none
gcp-integ-oidc-provider / GCP_INTEG_OIDC_PROVIDER Workload Identity Provider associated with GitLab to authenticate with OpenID Connect on integration environment (only define to override default) none
gcp-integ-oidc-account / GCP_INTEG_OIDC_ACCOUNT Service Account to which impersonate with OpenID Connect authentication on integration environment (only define to override default) none
gcp-staging-oidc-provider / GCP_STAGING_OIDC_PROVIDER Workload Identity Provider associated with GitLab to authenticate with OpenID Connect on staging environment (only define to override default) none
gcp-staging-oidc-account / GCP_STAGING_OIDC_ACCOUNT Service Account to which impersonate with OpenID Connect authentication on staging environment (only define to override default) none
gcp-prod-oidc-provider / GCP_PROD_OIDC_PROVIDER Workload Identity Provider associated with GitLab to authenticate with OpenID Connect on production environment (only define to override default) none
gcp-prod-oidc-account / GCP_PROD_OIDC_ACCOUNT Service Account to which impersonate with OpenID Connect authentication on production environment (only define to override default) none

Example

With a common default GCP_OIDC_PROVIDER and GCP_OIDC_ACCOUNT configuration for non-prod environments, and a specific one for production:

include:
  # main template
  - component: $CI_SERVER_FQDN/to-be-continuous/terraform/gitlab-ci-terraform@5.5.5
  # Google Cloud variant
  - component: $CI_SERVER_FQDN/to-be-continuous/terraform/gitlab-ci-terraform-gcp@5.5.5
    inputs:
      # common OIDC config for non-prod envs
      gcp-oidc-provider: "projects/<gcp_nonprod_proj_id>/locations/global/workloadIdentityPools/<pool_id>/providers/<provider_id>"
      gcp-oidc-account: "<name>@$<gcp_nonprod_proj_id>.iam.gserviceaccount.com"
      # specific OIDC config for prod
      gcp-prod-oidc-provider: "projects/<gcp_prod_proj_id>/locations/global/workloadIdentityPools/<pool_id>/providers/<provider_id>"
      gcp-prod-oidc-account: "<name>@$<gcp_prod_proj_id>.iam.gserviceaccount.com"

AWS variant

This variant enables OpenID Connect to retrieve temporary credentials from AWS.

If you wish to use this authentication mode, please follow carefully the GitLab guide, then configure appropriately the related variables:

  • AWS_OIDC_ROLE_ARN for any global/common access,
  • AWS_REVIEW_OIDC_ROLE_ARN and/or AWS_INTEG_OIDC_ROLE_ARN and/or AWS_STAGING_OIDC_ROLE_ARN and/or AWS_PROD_OIDC_ROLE_ARN if you wish to use a separate role with any of your environments.

Provided you successfully configured the above, this variant automatically sets the appropriate Assume Role with Web Identity configuration (environment variables) supported by the AWS Provider for Terraform.

Configuration

The variant supports the following configuration:

Input / Variable Description Default value
aws-oidc-aud / AWS_OIDC_AUD The aud claim for the JWT $CI_SERVER_URL
aws-oidc-role-arn / AWS_OIDC_ROLE_ARN Default IAM Role ARN associated with GitLab to authenticate using OpenID Connect none (disabled)
aws-review-oidc-role-arn / AWS_REVIEW_OIDC_ROLE_ARN IAM Role ARN associated with GitLab to authenticate using OpenID Connect on review env (only define to override default) none (disabled)
aws-integ-oidc-role-arn / AWS_INTEG_OIDC_ROLE_ARN IAM Role ARN associated with GitLab to authenticate using OpenID Connect on integration env (only define to override default) none (disabled)
aws-staging-oidc-role-arn / AWS_STAGING_OIDC_ROLE_ARN IAM Role ARN associated with GitLab to authenticate using OpenID Connect on staging env (only define to override default) none (disabled)
aws-prod-oidc-role-arn / AWS_PROD_OIDC_ROLE_ARN IAM Role ARN associated with GitLab to authenticate using OpenID Connect on production env (only define to override default) none (disabled)

Example

include:
  # main template
  - component: $CI_SERVER_FQDN/to-be-continuous/terraform/gitlab-ci-terraform@5.5.5
  # AWS variant
  - component: $CI_SERVER_FQDN/to-be-continuous/terraform/gitlab-ci-terraform-aws@5.5.5
    inputs:
      # audience claim for JWT
      aws-oidc-aud: "https://gitlab.acme.com"
      # common OIDC role ARN for non-prod envs
      aws-oidc-role-arn: "arn:aws:iam::111111111111:role/cicd-role"
      # specific OIDC role ARN for prod
      aws-prod-oidc-role-arn: "arn:aws:iam::222222222222:role/cicd-role"