↓メインコンテンツへスキップ
  1. Blogs/

Terraform StateをCloud Storage Remote Backendへ移行してみた

2 分
Terraform GoogleCloud CloudStorage
0222-nnn
著者
0222-nnn
猫が好き
目次
Terraform-GoogleCloud - この記事は連載の一部です
パート 20: この記事

概要
#

Local Stateのままでは、PCを変えると続きを操作できません。複数人で同じ構成を触ることもできません。

では State Bucket 自体をTerraformで作ればよいのか。今度は、そのStateをどこに置くのかという循環にぶつかります。

解き方は、Bucketを作る構成と、それを使う構成を分けることです。Bootstrap構成はLocal Stateで作ります。demo/側は、そのBucketをRemote Stateとして参照する形にしました。

Terraformの基本操作は前提としています。Local Stateしか使ったことがない方向けです。

State Locking、Workspaceによる環境分離、CI/CDからのBackend利用は扱いません。削除順序を誤るとStateだけが残るため、後片付けの手順も確認します。

検証すること
#

  • Bootstrap構成でVersioning付きState Bucketを作成できること
  • 別のTerraform構成をGCS Backendで初期化できること
  • Remote State Objectと過去世代を確認できること
  • Consumer、Backendの安全な削除順序を確認できること

前提環境
#

  • Google Cloud CLIとApplication Default Credentials(ADC)
  • Terraform 1.5以降
  • Cloud StorageとBucket IAMを操作できる検証用Google Cloud Project
  • 検証時のバージョン:Terraform 1.14.3、google 7.43.0

今回の構成
#

構成を2つに分けます。Bucketを作る側はLocal Stateのまま、それを使う側がRemote Stateを参照します。

flowchart TB
    subgraph Local["手元の作業ディレクトリ"]
        BootTF["Bootstrap 構成"]
        BootState["terraform.tfstate
(Local State)"] DemoTF["demo/ 構成"] BootTF --- BootState end subgraph GCP["Google Cloud"] subgraph Project["Project"] subgraph GCS["Cloud Storage"] Bucket["State Bucket
Versioning 有効"] Obj["default.tfstate"] Demo["demo 用 Bucket"] Bucket --- Obj end end end BootTF -->|"作成"| Bucket DemoTF -->|"backend で読み書き"| Obj DemoTF -->|"作成"| Demo

Bootstrap 構成のStateだけが手元に残ります。ここを Remote にしようとすると、そのStateをどこへ置くのかという循環に戻ります。

初期化から削除までの流れは次のとおりです。

sequenceDiagram
    participant Boot as Bootstrap 構成
    participant Bucket as State Bucket
    participant Demo as demo/ 構成
    participant Res as demo 用 Bucket

    Boot->>Bucket: apply(Local State で作成)
    Demo->>Bucket: init -backend-config
    Note over Demo,Bucket: State の置き場所を登録
    Demo->>Res: apply
    Demo->>Bucket: State を書き込み

    Note over Demo,Res: 削除は逆順
    Demo->>Res: destroy
    Boot->>Bucket: destroy

削除の順序を誤ると、Bucketが先に消えてStateだけが行方不明になります。

使用するTerraformコード
#

Backend用Bucketを先に作る
#

resource "google_storage_bucket" "tfstate" {
  name                        = "${var.bucket_name_prefix}-${var.project_id}"
  location                    = var.location
  force_destroy               = var.force_destroy
  uniform_bucket_level_access = true
  public_access_prevention    = "enforced"

  versioning {
    enabled = true
  }
}

Backendはterraform init時に必要なので、同じState内で同時作成できません。まず親DirectoryでBucketを作成し、その名前をdemo/backend.hclへ設定します。

bucket = "YOUR_TFSTATE_BUCKET"
prefix = "advanced/gcs-remote-backend/demo"

Remote Backendを初期化する
#

cd Advanced-Examples/04-gcs-remote-backend
cp terraform.tfvars.example terraform.tfvars
terraform init
terraform apply

cd demo
cp backend.hcl.example backend.hcl
terraform init -backend-config=backend.hcl
terraform plan
terraform apply

親(Bootstrap)は2リソース、demo/は1リソースが作成されます。

# 親
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:

state_bucket_name = "tf-adv-tfstate-YOUR_PROJECT_ID"
state_bucket_url = "gs://tf-adv-tfstate-YOUR_PROJECT_ID"

# demo/(GCS Backend経由)
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

demo_bucket_name = "tf-adv-remote-demo-YOUR_PROJECT_ID"

GCP側で確認する
#

Versioningが有効か確認します。

gcloud storage buckets describe "gs://$(terraform output -raw state_bucket_name)"
versioning_enabled: true

State Objectと世代を確認します。

gcloud storage ls -r "gs://$(terraform output -raw state_bucket_name)/demo/"
terraform state list
gs://tf-adv-tfstate-YOUR_PROJECT_ID/demo/:
gs://tf-adv-tfstate-YOUR_PROJECT_ID/demo/default.tfstate

demo/側でterraform state pullを実行すると、GCS上のStateがそのまま読めます。

Remote Backendにより複数端末から同じStateを参照できます。ただしStateには機密値が含まれる可能性があるため注意が必要です。Bucket IAMをTerraform実行者に限定します。Public Access Prevention、Versioning、監査Logも検討します。

削除順序
#

cd demo
terraform destroy
cd ..
terraform destroy
# demo/
Destroy complete! Resources: 1 destroyed.

# 親
Destroy complete! Resources: 2 destroyed.

必ずRemote Stateを利用するdemo/を先に削除します。Backend Bucketを先に消すと、Terraformが管理対象を追跡できません。force_destroyを使う場合も、State履歴まで削除する影響を理解して設定します。

まとめ
#

  • Backend用InfrastructureはBootstrapとして分離する
  • backend.hclで環境固有のBucket名を渡せる
  • VersioningによりStateの過去世代を保持できる
  • 削除はConsumer側、Backend側の順で行う

参考資料
#

次回
#

次回はGitHub Actionsから認証します。Workload Identity FederationでGoogle Cloudへつなぎます。

Terraform-GoogleCloud - この記事は連載の一部です
パート 20: この記事

関連記事

TerraformからGoogle Cloudへ接続してProject情報を取得してみた
3 分
Terraform GoogleCloud
TerraformでArtifact RegistryのDocker Repositoryを作成してみた
2 分
Terraform GoogleCloud Docker
TerraformでArtifact RegistryのコンテナをCloud Runへデプロイしてみた
2 分
Terraform GoogleCloud CloudRun ArtifactRegistry
TerraformでBigQuery DatasetとTableを作成してみた
2 分
Terraform GoogleCloud
TerraformでCloud KMSのKeyRingとCryptoKeyを作成してみた
2 分
Terraform GoogleCloud
TerraformでCloud MonitoringのAlert Policyを作成してみた
2 分
Terraform GoogleCloud