09. state とワークフロー
Terraform の中核には state ファイル があります。「どのコードがどの実物に対応しているか」を覚えておく台帳。これを正しく扱えるかが、Terraform を実務で使えるかの分かれ目です。
この章の目次
なぜ state があるのか
Terraform は「設定」と「実物」を結びつけて管理するために、JSON ファイル terraform.tfstate を使います。中身は「aws_instance.web(コード上の名前)は AWS の i-0123abcd(実物の ID)」というマッピング。これがないと、コードを変えた時に「どの実物を直せばいいのか」がわからなくなります。
- 1 ディレクトリ = 1 state:
terraform initしたフォルダごとに 1 つ。だから環境分離はディレクトリ分割が基本 - state には機密が入りうる: DB パスワード、API キーなどが平文で保存されることもある
- 絶対に Git にコミットしない: .gitignore で必ず除外
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.tfstate | S3 / 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 にだけ取り込まれる(実物は変えない)
やってはいけないこと
- state を Git にコミット: 機密漏洩。即ローテーション
- 同じ state に対して同時 apply: ロックが無いと state 破損。DynamoDB 等でロック
- state を手で書き換える: 必ず
terraform state系コマンドを使う - マネコンで直接変更: 次の plan で「巻き戻し差分」が出る。
ignore_changesか import して取り込む - 同じリソースを複数 state で管理: 双方が「自分のもの」と認識して干渉する
HCL 編はここまで
お疲れ様でした。次は AWS セクションで、ここまで覚えた構文を使って具体的なリソースを書いていきます。