今回はGitLabとTerraformを連携し、GitLab CI/CDからTerraform (OpenTofu) を利用する方法を紹介します。
背景
GitLabはTerraformと連携する以下のような機能を提供しています。
- GitLabをTerraformのstateファイル保存場所として提供
- GitLab CI/CDからTerraform/OpenTofuを利用するテンプレートを提供
- Terraform module registryの提供
- GitLab Terraform Helperの提供
今回は上記機能も使いつつ、GitLab CI/CDからTerraform/OpenTofuによるplan / applyを実行してみました。
検証
検証環境は以下の通りです。
素のTerraformをCI/CD Jobから呼び出す
GitLab CI/CDからTerraformコマンドを実行する方法は複数ありますが、まずはTerraformコマンドを含んだコンテナイメージを使い実行する方法を試してみました。
まず、今回使ったTerraformファイルを紹介します。
backend.tf
terraform { backend "http" { address="https://gitlab.com/api/v4/projects/<Project ID>/terraform/state/default" lock_address = "https://gitlab.com/api/v4/projects/<Project ID>/terraform/state/default/lock" unlock_address = "https://gitlab.com/api/v4/projects/<Project ID>/terraform/state/default/lock" lock_method = "POST" unlock_method = "DELETE" retry_wait_max = 5 } }
main.tf
terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.0" } } } provider "aws" { region = "ap-northeast-1" } resource "aws_s3_bucket" "test" { bucket = "terraform-gitlab-test-20240311" }
main.tf
は特に説明不要かと思うので、ここではbackend.tf
について補足します。
今回はTerraform stateの管理場所にGItLabを選択しました。GitLabをTerraform state管理場所として使うには、terraform init
で以下のようなコマンドを実行する必要があります。
export GITLAB_ACCESS_TOKEN=<YOUR-ACCESS-TOKEN> export TF_STATE_NAME=default terraform init \ -backend-config="address=https://gitlab.com/api/v4/projects/<Project ID>/terraform/state/$TF_STATE_NAME" \ -backend-config="lock_address=https://gitlab.com/api/v4/projects/<Project ID>/terraform/state/$TF_STATE_NAME/lock" \ -backend-config="unlock_address=https://gitlab.com/api/v4/projects/<Project ID>/terraform/state/$TF_STATE_NAME/lock" \ -backend-config="username=fy0323" \ -backend-config="password=$GITLAB_ACCESS_TOKEN" \ -backend-config="lock_method=POST" \ -backend-config="unlock_method=DELETE" \ -backend-config="retry_wait_min=5"
上記コマンドはGitLab Project画面から 操作
→ Terraformステータス
と移動しても確認できます。
これをGitLab CI/CDから実行するため、backend.tf
に秘匿情報以外を記載しておき、initコマンドのオプションから秘匿情報を渡します。
次に .gitlab-ci.yml
は以下の通りです。
variables: GITLAB_ACCESS_TOKEN: $GITLAB_ACCESS_TOKEN GITLAB_USERNAME: $GITLAB_USER_LOGIN TF_ROOT: ${CI_PROJECT_DIR} stages: - init - plan - apply default: image: name: hashicorp/terraform:1.7 entrypoint: [""] cache: key: "${TF_ROOT}" paths: - ${TF_ROOT}/.terraform/** - ${TF_ROOT}/.terraform - ${TF_ROOT}/.terraform.lock.hcl init: stage: init script: - > terraform init \ --backend-config="username=${GITLAB_USERNAME}" \ --backend-config="password=${GITLAB_ACCESS_TOKEN}" artifacts: when: always plan: stage: plan needs: - init script: - terraform plan artifacts: when: always apply: stage: apply needs: - init - plan script: - terraform apply -auto-approve when: manual
いくつか補足します。
variables
:GITLAB_ACCESS_TOKEN
は、あらかじめ取得したAccess tokenをCI/CD変数に設定します。GITLAB_USER_LOGIN
CI_PROJECT_DIR
は定義済みの変数 (Predefined variables) です。default
: ここではHashiCorpの提供するTerraformコンテナイメージを使用しています。またterraform init
実行後に生成されるファイルを後段で使うためcache
の設定をしています。init
: 変数に設定したユーザー名・Access tokenを使い、初期化を行います。plan
: 前段のinit
stageで生成されたファイルを使うためneeds
でinit
stageを指定します。apply
:plan
と同じような設定ですが、Jobは手動実行のみ許可しています。
なお、上記3つのファイルを配置する前に、GitLab・AWSへの認証を有効にするため、CI/CD変数を設定しておきます。
変数を設定後、ファイルを配置してパイプラインを起動すると、plan
stageが成功する様子を確認できます。手動実行で apply
stageを起動すると、そちらも成功しました。
実行ログを確認すると、plan/applyの様子を確認できます。
一応AWS側も確認しますが、想定通りのS3バケットが作成されていました。
OpenTofu componentを使う
GitLabはTerraform / OpenTofuを利用するためのtemplate / CI/CD Catalogを提供しています。ただしTerraformのほうはGitLab 18.0で廃止予定のため、今回はOpenTofuのほうを使います。
OpenTofuのCI/CD Catalogは以下に配置されています。
OpenTofuで利用したファイルは以下の通りです。ここでもTarraform stateの管理先はGitLabとします。先ほどと比べ、 backend.tf
.gitlab-ci.yml
の記述量が少なく、シンプルであることがわかります。
backend.tf
terraform { backend "http" { } }
main.tf
terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.0" } } } provider "aws" { region = "ap-northeast-1" } resource "aws_s3_bucket" "test" { bucket = "terraform-gitlab-test-20240330" }
.gitlab-ci.yml
include: - component: gitlab.com/components/opentofu/validate-plan-apply@~latest inputs: version: latest opentofu_version: 1.6.1 stages: - validate - build - deploy fmt: stage: validate plan: stage: build apply: stage: deploy
上記ファイルに加え、CI/CD変数の設定もしておきます。
変数設定後にパイプラインを実行すると、以下のようなJobが実行されます。ここでも apply
は手動実行をトリガーにしています。
apply
も実行後にログを見ると、 gitlab-tofu
というコマンドが各処理を実行していることが分かります。
この gitlab-tofu
の実体はこちらのShell scriptであり、 gitlab-opentofu
というコンテナイメージに含まれる tofu
バイナリが処理を行う中心です。
参考情報
- Terraform と GitLab CI で インフラCI/CD入門 - istyle Tech Blog
- GitLabをTerraformのStateの保存先として使う - CLOVER🍀
- lockfile - Inconsistent dependency when i do terraform apply from plan -out=file - Stack Overflow
- gitlab-ci: Terraform has no command named "sh". Did you mean "show" - Stack Overflow
- GitLabのOpenTofuコンポーネントとOIDCを使って、アクセスキーを発行せずにAWSリソースを構築してみた - BATONZ Tech Blog