Terraformの「Plan」は神託ではない:破壊的変更を封じ込め、インフラの不変性を極めるための深層技術論
Terraformの `plan` をただの「確認作業」だと思っているなら、君のCI/CDパイプラインはいつか必ず、プロダクション環境の壊滅という名の「死」を迎える。
インフラをコードで定義する(IaC)という行為の本質は、記述したコードとクラウドプロバイダーのAPIステータスの間に存在する「解釈のズレ」をいかにゼロに近づけるか、に尽きる。本稿では、`terraform plan` が隠し持つ罠を暴き、リソースの再作成(Replacement)という破滅的なイベントをいかにして「完全に無力化」するか、その極意を伝授しよう。
—
1. 破壊的変更のメカニズム:なぜ「意図せぬ再作成」は起きるのか
Terraformにおけるリソースの再作成は、`ForceNew` フラグが立つことで発生する。これは、API側が「更新(Update)」を許容せず「削除して再作成(Delete & Create)」しか受け付けない属性を変更した時にトリガーされる。
現場で遭遇する「地雷」のパターン
1. API仕様の暗黙の変更: クラウドプロバイダー(AWS/GCP/Azure)側のAPI仕様がサイレントアップデートされ、これまで `Update` 可能だったフィールドが `ForceNew` 属性に変更されるケース。
2. 循環依存とネストの罠: `depends_on` を過剰に使用し、間接的な参照がリソースの作成順序を入れ替えた結果、既存リソースの識別子が変更と見なされるケース。
3. データソースの不一致: `data` ブロックで取得した値を使ってリソースを構成している際、データソース側のキャッシュやリージョン間の伝搬遅延が `plan` に不整合な差分を生むケース。
—
2. Planを「真の予言」にする:`-refresh-only` の戦略的運用
多くのエンジニアは `plan` を実行する際、ステートファイルが現在のクラウド環境と同期していると盲信している。だが、手動操作やAPI経由の横入りがある環境では、その前提は崩壊する。
そこで活用すべきは `-refresh-only` だ。これはステートと実際のインフラを照合し、差分をステートに反映させるだけで、コード自体は書き換えない。
ステートと実環境の乖離を特定し、不整合を「見える化」する
terraform plan -refresh-only -out=refresh.tfplan
その結果をステートに書き込む(破壊的変更の予兆を事前に検知)
terraform apply refresh.tfplan
極限の知見: CIパイプラインの冒頭で必ずこれを走らせよ。「コード上の正義」と「API上の現実」を同期させない限り、次の `plan` はただのノイズだ。
—
3. 破壊的防御術:`lifecycle` ブロックによる絶対防衛圏
リソースの再作成を物理的に阻止したい場合、`lifecycle` 設定は最強の盾となる。しかし、ただ `prevent_destroy` を入れるだけでは甘い。
戦略的防御:`ignore_changes` との合わせ技
API側が勝手に付与・変更するタグや、自動スケーリングによるスペック変更を `plan` の差分から除外する。
resource “aws_db_instance” “production_db” {
# … 構成定義 …
lifecycle {
# 物理的な削除操作を完全にロックする
prevent_destroy = true
# APIやオートスケーラーが外部から変更する可能性のある属性を無視
ignore_changes = [
instance_class,
tags[“AutoScalingGroup”],
]
}
}
エキスパートの助言: `prevent_destroy = true` は最終防衛線だ。本番環境のデータベースなど、再作成がビジネス停止に直結するリソースには必ず記述せよ。これがあるだけで、人間のミスによる `terraform destroy` をコンパイルエラー(実行時エラー)レベルで防げる。
—
4. 自動化の深淵:Terraform Planの解析・フィルタリングスクリプト
`terraform plan` の出力結果は、人間が目で追うにはあまりに巨大で複雑だ。我々エキスパートは、JSON形式で出力し、jqで「再作成(Replacement)」が発生するリソースのみを抽出してアラートを飛ばす自動化を組む。
!/bin/bash
破壊的変更(Replacement)の検知スクリプト
set -e
JSON出力して解析
terraform plan -json > plan.json
replacement(入れ替え)が発生するリソースを抽出
REPLACEMENTS=$(jq ‘.resource_changes[] | select(.change.actions[] == “create” and .change.actions[] == “delete”) | .address’ plan.json)
if [ -n “$REPLACEMENTS” ]; then
echo “警告: 以下のリソースで破壊的変更が検知されました:”
echo “$REPLACEMENTS”
exit 1 # パイプラインを即座に停止せよ
fi
このスクリプトをCIの `plan` ステージに組み込め。これにより、「気づかずにapplyしてしまった」という言い訳が、組織から永遠に消滅する。
—
5. 伝説的アーキテクトからの提言
Terraformを扱うということは、「状態という名のカオスを、宣言という名の秩序で制圧する」ということだ。
- APIを疑え: プロバイダーのドキュメントに書かれていない仕様は、APIのレスポンス(`terraform providers schema -json`)を確認するんだ。
- メモリ消費を恐れるな: 大規模なステートファイルは `terraform plan` 時にメモリを食いつぶす。リモートステートのロックと並行して、適切な `state mv` やモジュール分割を行い、ステートを細分化(Sharding)せよ。
- 人間を排除せよ: `apply` は常にパイプラインから実行されるべきだ。ローカルのPCから実行される `terraform apply` は、もはやインフラに対するテロ行為と同義である。
君が扱うインフラは、君のコードの鏡だ。`plan` の出力に少しでも違和感を感じたら、その直感を信じろ。それが、伝説的なSREへの第一歩だ。