★★ 中級

09. state とワークフロー

Terraform の中核には state ファイル があります。「どのコードがどの実物に対応しているか」を覚えておく台帳。これを正しく扱えるかが、Terraform を実務で使えるかの分かれ目です。

なぜ state があるのか

Terraform は「設定」と「実物」を結びつけて管理するために、JSON ファイル terraform.tfstate を使います。中身は「aws_instance.web(コード上の名前)は AWS の i-0123abcd(実物の ID)」というマッピング。これがないと、コードを変えた時に「どの実物を直せばいいのか」がわからなくなります。

CLI ワークフロー

terraform init       # 初期化(provider と module をダウンロード、backend を設定)
terraform fmt         # 整形(推奨: pre-commit で自動化)
terraform validate    # 構文・型チェック
terraform plan        # 差分計算。AWS は変えない。レビューに使う
terraform apply       # plan 通りに AWS を変える。確認プロンプトで yes
terraform destroy     # 全 resource を削除。学習が終わったら必ず

plan を保存して apply

# CI/CD では plan を保存 → 承認後にその plan を適用
terraform plan -out=tfplan
terraform apply tfplan       # ← 引数があると確認プロンプト無し

output / show / console

terraform output                # root の output を表示
terraform output -json          # JSON で
terraform output -raw vpc_id    # 1 つだけ生で

terraform show                  # 現在の state を読める形で
terraform show -json > state.json  # JSON で

terraform console               # 対話型 REPL(式・関数の試打台)

backend ─ state をどこに置くか

ローカル backend(デフォルト)リモート backend
state は手元の terraform.tfstateS3 / Azure Blob / GCS / TFC など
個人学習・1 人作業ならこれで OKチーム作業/CI から apply するなら必須
同時 apply のロックなしDynamoDB 等でロック → 競合事故防止

S3 + DynamoDB のリモート backend 例

AWS の標準パターン。state を S3 に、ロック情報を DynamoDB に置きます。

terraform {
  backend "s3" {
    bucket         = "tfstate-myorg-prd"
    key            = "envs/prd/terraform.tfstate"
    region         = "ap-northeast-1"
    encrypt        = true
    dynamodb_table = "tfstate-lock"
  }
}

backend を変更したらどうする?

backend を初めて書いた時、または変更した時は terraform init が「state を移行しますか?」と聞いてきます。

terraform init -migrate-state   # 既存 state を新 backend に移行
terraform init -reconfigure     # 移行せず再構成(中身を捨てる)

backend のための「先有りの卵」問題

S3 backend を使うには、その S3 バケットと DynamoDB テーブルが すでに存在している 必要があります。が、Terraform はそれらも IaC で作りたい。「ブートストラップ」専用ディレクトリ を別に作って、そこだけは ローカル state で運用するのが定石です。

my-infra/
├── terraform/
│   ├── bootstrap/         # ← state バケット+DynamoDB を作る(ローカル state)
│   │   ├── main.tf
│   │   └── README.md
│   └── envs/
│       ├── dev/           # ← bootstrap で作った S3 を backend に使う
│       └── prd/

このサイト自身もそのパターンで構築しています(AWS 章 08)。

terraform state コマンド

terraform state list                          # state にあるアドレス一覧
terraform state show aws_s3_bucket.logs       # 1 つの詳細
terraform state mv aws_s3_bucket.a aws_s3_bucket.b  # 名前変更(destroy/create なし)
terraform state rm aws_s3_bucket.legacy       # state から外す(実物は残る)
terraform state pull > backup.tfstate          # state をローカルに取得
terraform state push backup.tfstate           # state を上書き(要注意)

moved ブロックでのリファクタ(推奨)

terraform state mv は手作業+追跡不可で危険なので、可能なら moved ブロックで PR と共に履歴を残すのが推奨です(07 章)。

既存リソースを取り込む import

「マネジメントコンソールで作ったリソース」を後から Terraform 管理に入れる時。

import {
  to = aws_s3_bucket.legacy
  id = "my-existing-bucket-name"
}

resource "aws_s3_bucket" "legacy" {
  bucket = "my-existing-bucket-name"
}
terraform plan      # import 計画を確認
terraform apply     # state にだけ取り込まれる(実物は変えない)

やってはいけないこと

HCL 編はここまで お疲れ様でした。次は AWS セクションで、ここまで覚えた構文を使って具体的なリソースを書いていきます。