GitLab が管理する Terraform の状態

  • GitLab 13.0から導入されました
  • ピリオドを含むステート名のサポートはGitLab 15.7でallow_dots_on_tf_state_names というフラグで導入されました。デフォルトでは無効。
  • ピリオドを含むステート名のサポートはGitLab 16.0で一般的に利用可能。機能フラグallow_dots_on_tf_state_names を削除。

Terraformはステートファイルを使ってインフラ設定の詳細を保存します。Terraformリモートバックエンドでは、ステートファイルをリモートと共有のストアに保存できます。

GitLabはTerraform HTTPバックエンドを提供し、最小限の設定で安全にステートファイルを保存できます。

GitLabでは以下のことができます:

  • Terraformのステートファイルをバージョン管理します。
  • 転送中と静止時の両方で状態ファイルを暗号化します。
  • 状態のロックとロック解除
  • terraform plan およびterraform apply コマンドをリモートで実行。
caution
ディザスタリカバリ計画Terraformのステートファイルは、ディスクやオブジェクトストレージにあるときはlockbox Ruby gemで暗号化されます。ステートファイルを復号化するには、GitLabが利用可能である必要があります。GitLabがオフラインで、GitLabが必要とするインフラストラクチャ(仮想マシン、Kubernetesクラスター、ネットワークコンポーネントなど)をデプロイするためにGitLabを使用している場合、ステートファイルに簡単にアクセスしたり、復号化したりすることはできません。さらに、GitLabがTerraformモジュールやGitLabのブートストラップに必要なその他の依存関係を提供する場合、それらにアクセスできなくなります。このイシューを回避するには、これらの依存関係をホスティングしたりバックアップしたりするために別の手配をするか、共有障害点のない別のGitLabインスタンスを使うことを検討してください。

前提条件

セルフマネージドGitLabの場合、TerraformのステートファイルにGitLabを使用する前:

  • 管理者がTerraformのステートストレージを設定する必要があります。
  • プロジェクトのインフラメニューを有効にする必要があります。Settings > GeneralVisibility, project features, permissions を展開し、Infrastructureで toggle をオンにします。

GitLab CI/CDを使ってバックエンドとしてTerraformの状態を初期化します。

terraform init コマンドを実行した後、GitLab CI/CD を使ってterraform コマンドを実行することができます。

前提条件:

  • terraform apply を使用して状態をロック、アンロック、書き込みするには、少なくともメンテナーのロールが必要です。
  • terraform plan -lock=false を使用して状態を読み取るには、少なくとも開発者ロールを持っている必要があります。
caution
他のジョブのアーティファクトと同様に、Terraformのプランデータはリポジトリ上でGuestロールを持つ人なら誰でも見ることができます。TerraformもGitLabもデフォルトではプランファイルを暗号化しません。Terraformプランにパスワード、アクセストークン、証明書などの機密データが含まれている場合は、プラン出力を暗号化するか、プロジェクトの可視性設定を変更する必要があります。また、アーティファクトの公開フラグをfalse (public: false) に設定して、公開パイプラインを 無効にすることを強くお勧めします。この設定によって、アーティファクトは GitLab 管理者とレポーターロール以上のプロジェクトメンバーだけがアクセスできるようになります。

GitLab CI/CDをバックエンドとして設定するには:

  1. Terraformプロジェクトのbackend.tf のような.tf ファイルで、HTTPバックエンドを定義します:

    terraform {
      backend "http" {
      }
    }
    
  2. プロジェクトリポジトリのルートディレクトリに、.gitlab-ci.yml ファイルを作成します。Terraform.gitlab-ci.yml テンプレートを使って入力します。
  3. プロジェクトを GitLab にプッシュします。このアクションがパイプラインのトリガーとなり、gitlab-terraform initgitlab-terraform validategitlab-terraform plan コマンドが実行されます。
  4. gitlab-terraform apply コマンドを実行する前のパイプラインから手動deploy ジョブをトリガーして、定義されたインフラストラクチャをプロビジョニングします。

上記のterraform コマンドの出力は、ジョブログで見ることができるはずです。

gitlab-terraform CLI はterraform CLI のラッパーです。詳細はGitLab Terraformヘルパーを参照するか、gitlab-terraformのソースコードをご覧ください。

terraform コマンドを明示的に呼び出したい場合は、テンプレートをオーバーライドして、代わりに実現できることのリファレンスとして使うことができます。

Terraform環境変数のカスタマイズ

Terraform.gitlab-ci.yml テンプレートを使用すると、CI/CD ジョブを定義する際にTerraform HTTP 設定変数を使用できます。

terraform init をカスタマイズして Terraform の設定を上書きするには、terraform init -backend-config=... のアプローチの代わりに環境変数を使います。-backend-config を使用する場合、設定は次のようになります:

  • terraform plan コマンドの出力にキャッシュされます。
  • 通常はterraform apply コマンドに渡されます。

この設定は、CIジョブでTerraformのステートファイルをロックできないなどの問題につながる可能性があります。

ローカルマシンから状態にアクセス

ローカルマシンからGitLabが管理するTerraformの状態にアクセスすることができます。

caution
GitLabのクラスター化されたデプロイでは、ローカルストレージを使うべきではありません。ノード間で状態が分割され、その後のTerraformの実行に一貫性がなくなる可能性があります。代わりに、リモートストレージリソースを使用してください。
  1. Terraformの状態がCI/CD用に初期化されていることを確認してください。
  2. Terraforminit コマンドをコピーします:

    1. 左のサイドバーで「検索」または「移動」を選択してあなたのプロジェクトを検索します。
    2. オペレーション > Terraformの状態を選択します。
    3. 使用したい環境の横で、Actions({ellipsis_v})を選択し、Copy Terraform init commandを選択します。
  3. ターミナルを開き、ローカルマシンでこのコマンドを実行します。

GitLabが管理するTerraformの状態にマイグレーションします。

Terraformはバックエンドが変更されたり再設定されたときに状態をコピーすることをサポートしています。別のバックエンドからGitLabが管理するTerraformの状態にマイグレーションするには、以下のアクションを使います。

GitLabが管理するTerraformの状態へのマイグレーションに必要なコマンドを実行するには、ローカルターミナルを使用する必要があります。

以下の例は、ステート名を変更する方法を示しています。異なるステートストレージバックエンドからGitLab管理下のTerraformステートにマイグレーションする場合も同じワークフローが必要です。

初期バックエンドのセットアップ

PROJECT_ID="<gitlab-project-id>"
TF_USERNAME="<gitlab-username>"
TF_PASSWORD="<gitlab-personal-access-token>"
TF_ADDRESS="https://gitlab.com/api/v4/projects/${PROJECT_ID}/terraform/state/old-state-name"

terraform init \
  -backend-config=address=${TF_ADDRESS} \
  -backend-config=lock_address=${TF_ADDRESS}/lock \
  -backend-config=unlock_address=${TF_ADDRESS}/lock \
  -backend-config=username=${TF_USERNAME} \
  -backend-config=password=${TF_PASSWORD} \
  -backend-config=lock_method=POST \
  -backend-config=unlock_method=DELETE \
  -backend-config=retry_wait_min=5
Initializing the backend...

Successfully configured the backend "http"! Terraform will automatically
use this backend unless the backend configuration changes.

Initializing provider plugins...

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
re-run this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

バックエンドの変更

terraform init.terraform/ ディレクトリを作成し、古い状態の場所を知っているので、新しい場所を教えることができます:

TF_ADDRESS="https://gitlab.com/api/v4/projects/${PROJECT_ID}/terraform/state/new-state-name"

terraform init \
  -migrate-state \
  -backend-config=address=${TF_ADDRESS} \
  -backend-config=lock_address=${TF_ADDRESS}/lock \
  -backend-config=unlock_address=${TF_ADDRESS}/lock \
  -backend-config=username=${TF_USERNAME} \
  -backend-config=password=${TF_PASSWORD} \
  -backend-config=lock_method=POST \
  -backend-config=unlock_method=DELETE \
  -backend-config=retry_wait_min=5
Initializing the backend...
Backend configuration changed!

Terraform has detected that the configuration specified for the backend
has changed. Terraform will now check for existing state in the backends.


Acquiring state lock. This may take a few moments...
Do you want to copy existing state to the new backend?
  Pre-existing state was found while migrating the previous "http" backend to the
  newly configured "http" backend. No existing state was found in the newly
  configured "http" backend. Do you want to copy this state to the new "http"
  backend? Enter "yes" to copy and "no" to start with an empty state.

  Enter a value: yes


Successfully configured the backend "http"! Terraform will automatically
use this backend unless the backend configuration changes.

Initializing provider plugins...

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

yes と入力すると、古い場所から新しい場所に状態がコピーされます。そして、GitLab CI/CDでの実行に戻ることができます。

GitLabバックエンドをリモートデータソースとして使う

GitLabが管理するTerraformのステートバックエンドをTerraformのデータソースとして使うことができます。

  1. main.tf などの関連ファイルで、これらの変数を宣言してください。値は空のままにしておいてください。

    variable "example_remote_state_address" {
      type = string
      description = "Gitlab remote state file address"
    }
       
    variable "example_username" {
      type = string
      description = "Gitlab username to query remote state"
    }
       
    variable "example_access_token" {
      type = string
      description = "GitLab access token to query remote state"
    }
    
  2. 前のステップの値を上書きするには、example.auto.tfvars という名前のファイルを作成します。このファイルはプロジェクトのリポジトリにバージョン管理されていないはずです。

    example_remote_state_address = "https://gitlab.com/api/v4/projects/<TARGET-PROJECT-ID>/terraform/state/<TARGET-STATE-NAME>"
    example_username = "<GitLab username>"
    example_access_token = "<GitLab Personal Access Token>"
    
  3. .tfTerraformの入力変数を使ってデータソースを定義します:

    data "terraform_remote_state" "example" {
      backend = "http"
       
      config = {
        address = var.example_remote_state_address
        username = var.example_username
        password = var.example_access_token
      }
    }
    
    • アドレス:データソースとして使いたいリモート状態のバックエンドのURL。例えば、https://gitlab.com/api/v4/projects/<TARGET-PROJECT-ID>/terraform/state/<TARGET-STATE-NAME>
    • username: データソースで認証するためのユーザ名。認証にパーソナルアクセストークンを使っている場合、この値はGitLabのユーザー名になります。GitLab CI/CDを使用している場合、この値は'gitlab-ci-token'
    • password: データソースで認証するためのパスワード。認証にパーソナルアクセストークンを使用している場合、この値はトークンの値になります(トークンはAPIスコープを持っている必要があります)。GitLab CI/CDを使用している場合、この値は${CI_JOB_TOKEN} CI/CD変数の内容です。

データソースからの出力はdata.terraform_remote_state.example.outputs.<OUTPUT-NAME> を使って Terraform リソースで参照できるようになりました。

ターゲットプロジェクトで Terraform の状態を読み込むには、少なくとも Developer ロールが必要です。

Terraform ステートファイルの管理

GitLab 13.8 で導入されました

Terraformの状態ファイルを見るには:

  1. 左のサイドバーで「検索」または「移動」を選択してあなたのプロジェクトを検索します。
  2. オペレーション > Terraformの状態を選択します。

このUIの改善を追跡するエピックが存在します。

個々のTerraform状態のバージョンを管理

GitLab 13.4 で導入されました

GitLab REST APIを使って、個々のステートのバージョンを管理することができます。

少なくとも開発者ロールを持っていれば、シリアル番号を使ってステートバージョンを取得することができます:

curl --header "Private-Token: <your_access_token>" "https://gitlab.example.com/api/v4/projects/<your_project_id>/terraform/state/<your_state_name>/versions/<version-serial>"

少なくともメンテナーのロールを持っていれば、シリアル番号を使ってステートバージョンを削除できます:

curl --header "Private-Token: <your_access_token>" --request DELETE "https://gitlab.example.com/api/v4/projects/<your_project_id>/terraform/state/<your_state_name>/versions/<version-serial>"

状態ファイルの削除

少なくともメンテナーのロールを持っていれば、状態ファイルを削除することができます。

  1. 左サイドバーで、Operate > Terraform statesを選択します。
  2. アクション]列で[アクション({ellipsis_v})]を選択し、[状態のファイルとバージョンを削除]を選択します。

APIを使用して状態ファイルを削除します。

状態ファイルを削除するには、REST API にリクエストします。例えば

curl --header "Private-Token: <your_access_token>" --request DELETE "https://gitlab.example.com/api/v4/projects/<your_project_id>/terraform/state/<your_state_name>"

GraphQL APIを使用することもできます。