CTS-KB

Terraform IaC 実践ガイド:運用編 — State drift 解消とトラブルシューティング

⏱ 約 9 分で読めます
#Terraform #IaC #State管理 #State drift #運用 #GCP #AWS

🔧 「書き終えた後」が運用の本番

本記事はシリーズ 「Terraform IaC 実践ガイド」第 5 回(運用編・最終回) です。第 4 回 CI/CD パイプライン編 までで、コードは安全に本番へ流れるようになりました。しかし Terraform 運用の事故は、**コードを書くフェーズではなく「運用フェーズ」**で起きます。

  • 誰かが GCP コンソールから手で直した → State と実態がズレる(State drift
  • terraform plan が突然「3 リソース作成」と言い出す
  • apply 直前に Saved plan is stale で止まる
  • allUsers への公開を入れたら組織ポリシーで弾かれる

本記事は、これらの実際に踏んだ運用トラブルと、その解消手順をまとめます。State は本番インフラの「正本」です。壊すと復旧が高くつくため、操作前の手順を徹底します。

🌊 State drift とは何か

State drift = Terraform の State(管理台帳)と、クラウドの実態がズレた状態です。主な原因は 3 つ。

原因
手動変更コンソール / CLI でリソースを直接変更・削除した
外部自動化オートスケーラや別ツールが属性を書き換えた
import 漏れ既存リソースを State に取り込まずコード化した

drift は 3 つの型に分類でき、型ごとに解消手段が違います

症状解消
State にあるが実体がないplan が「N 個を新規作成」と表示terraform state rm で台帳から外す
実体があるが State にないコンソールにあるのに plan に出ないterraform import で台帳に取り込む
属性のズレCPU・メモリ等が plan で差分表示コードを実態に合わせて修正 → apply

🛑 State を触る前の「必須 4 ステップ」

terraform state rm / import / mv台帳を直接書き換える破壊的操作です。実行前に、必ずこの順で確認します。この手順を飛ばした state 操作が、運用事故の最大の発生源です。

# ① State を確認(台帳側の現状)
terraform state list

# ② 実態を確認(クラウド側の現状)— これを必ずやる
gcloud run services list --project="$PROJECT" --region=asia-northeast1
aws lightsail get-instances --region us-east-1

# ③ バックアップを取る(戻せるようにする)
terraform state pull > state-backup-$(date +%Y%m%d-%H%M%S).json

# ④ ①②③の後で初めて操作する
terraform state rm 'module.xxx.resource.name'

②の「実態確認」を省略しないこと。State だけ見て state rm すると、「実は実体が存在していた」場合にリソースが Terraform 管理外で放置されます。

📋 State コマンド早見表

安全(読み取り専用)用途
terraform state list管理中リソースの一覧
terraform state show 'addr'1 リソースの属性詳細
terraform state pull > backup.jsonState をローカルに退避
破壊的(GCS 上の State を書き換える)用途
terraform state rm 'addr'台帳から削除(実体は残す)
terraform import 'addr' 'id'既存リソースを台帳に取り込む
terraform state mv 'old' 'new'台帳上のアドレスを改名

State はリモート(GCS)にあり、git にはコミットしません。 ローカルの .terraform/ は一時ファイルです。「State 変更をコミットしなきゃ」と思ったら、それは設計を誤解しています。State の正本は GCS バックエンド側にあります。

🧯 実例:State にあるが実体がない drift を解消する

あるクライアントの Lightsail スタックで、terraform plan が「3 リソースを新規作成」と表示しました。コードは変えていないのに新規作成 = drift のサインです。

# ① State 確認 → 該当リソースは台帳にある
terraform state list | grep lightsail

# ② 実態確認 → AWS には存在しない(手動削除されていた)
aws lightsail get-instances --region us-east-1

# ③ バックアップ
terraform state pull > state-backup-20260512.json

# ④ 台帳から外す(実体が無いので rm が正しい)
terraform state rm 'module.subsystem.aws_lightsail_instance.main'    # 3 リソース分

# ⑤ 再 plan → 「6 個を作成」(外した 3 + 元から未作成の 3)= 整合
terraform plan

学び:

  • AWS/GCP CLI での実態確認は必須。State だけ見て判断しない
  • state rm不可逆。必ず ③ のバックアップを先に取る
  • state 変更は git コミット不要(GCS が正本)

🚑 よくある障害と対処

障害 1:Saved plan is stale

apply ジョブが、保存済み plan を使おうとして止まる。

  • 原因: plan を作った後に State が変わった(別の apply・import・state rm が走った)
  • 対処: 同一ジョブ内で plan し直すか、apply 前に新しい plan を作る。第 4 回の plan/apply 分離 + plan artifact の TTL(expire_in: 1 day はこの事故を減らすための設計

障害 2:State lock のタイムアウト

前のジョブがクラッシュしてロックを掴んだまま死んだ。

# 他のプロセスが本当に動いていないことを確認してから
terraform force-unlock <LOCK_ID>

force-unlock本当に誰も apply していない確証があるとき限定。並行 apply 中に外すと State が壊れます。GCS バックエンドの state lock + GitLab resource_group: の二重化(第 4 回参照)で、そもそもロック競合を起こさない設計にしておくのが先決です。

障害 3:403 SERVICE_DISABLED

リソース作成時に API 無効エラー。対象プロジェクトでは API を有効化済みなのに出る。

  • 原因: bootstrap プロジェクト側で API が有効化されていない。マルチプロジェクト構成では、対象プロジェクトと bootstrap プロジェクトの両方で API 有効化が要る場合がある
  • 対処: bootstrap の required_apis に該当 API を追加して apply。準備の落とし穴は第 2 回 準備編 で詳説

障害 4:allUsers IAM が組織ポリシーで弾かれる

公開エンドポイントに allUsers(誰でも invoke 可)を付けたらエラー。

  • 原因: 組織ポリシーが allUsers をデフォルトでブロック。解放用の Tag が未設定、または Tag 伝播待ち
  • 対処: 解放用の Tag を先に付与し、公開 IAM を後に適用する順序制御がカギ。Terraform では depends_on で「Tag → IAM」の順序を固定する

順序を固定しないと、Tag の伝播前に公開 IAM が走り、組織ポリシー違反で失敗します。組織ポリシーによる allUsers 制御の有無と解放手段は、組織ごとに設定が異なるため、自組織のポリシーに合わせて準備段階で確認してください(第 2 回 準備編)。

🔁 環境移行(dev → stg → prod)の作法

環境を増やす・移すときは、環境差分を変数で吸収します。コードを分岐コピーしないのが原則です。

WIF の認可条件を環境で分岐

認証境界も環境変数で切り替えます。prod は保護ブランチからしか apply できないよう絞り、stg は MR からも回せるよう緩める——という条件分岐を、同じモジュールで環境ごとに変えます(具体の条件式は自組織のブランチ運用に合わせて定義)。

ドメイン命名を環境で分岐

domain = var.env_name == "prod"
  ? "api.${var.client_domain}"
  : "${var.env_name}-api.${var.client_domain}"   # stg-api.example.com

移行手順

  1. environments/stg/<stack>/ を作成(main.tf / variables.tf / outputs.tf / backend.hcl)— 専用 Terraform エージェントが既存の類似構成を手本に規約どおり生成
  2. 環境用の GCP プロジェクトを分離(stg と prod は別プロジェクト)
  3. Secret Manager に環境分の tfvars を登録(命名規約は第 2 回参照)
  4. WIF 条件・ドメインを env で分岐
  5. 既存リソースがあれば terraform import、無ければ新規作成
  6. DNS レコード・SSL 証明書を検証

🚫 運用フェーズの禁止事項

禁止理由
gcloud / aws CLI でリソースを直接作成・変更即 State drift の原因
ローカルから prod へ直接 applyレビュー・監査が残らない。必ず CI 経由
terraform state rm を勢いで実行不可逆。①②③の確認とバックアップが先
force-unlock を確認なしで実行並行 apply 中だと State 破損
State ファイルを git にコミットState は GCS が正本。機密も含む

🤖 全工程を Claude Code で管理・運用する

本基盤は、インフラの開発から運用までを一貫して Claude Code(AI エージェント)で管理しています。コード生成だけでなく、ローカルでの plan 確認 → git push → MR 作成までを Claude Code が運用し、人間はレビューと本番承認のゲートを担います。

一連の運用ループ

① タスク受領 → サイズ判定(S / M / L)
② M 以上はステアリング(要件・設計・決定の SSoT)を起こす
③ 専用エージェントが既存規約を手本に tf を生成 / 変更
④ ローカルで terraform plan を実行し、差分を確認
⑤ pre-push チェック → git push → MR を作成        ← ここまで Claude Code が運用
⑥ 人間が MR をレビュー・承認                          ← ヒューマンゲート
⑦ CI が plan/apply(stg 自動・prod は手動承認)       ← 第 4 回の動的 child pipeline

③〜⑤は Claude Code が一貫して回します。一方で、本番 apply は必ず人間の承認を通る(第 4 回の when: manual)よう、ゲートはモデル速度や自動化と独立に効かせます。「自動化するほど、ゲートは構造で固める」のが原則です。

ただし、品質を作り込むのは最後の承認ゲートではなく、その手前の「壁打ち」とステアリングです。①②の段階で AI と十分に壁打ちし、要件・設計・手順を固めておくほど、③以降の生成・plan・apply は素直に通ります。迷ったら手を止めて壁打ちする——この往復が不具合と手戻りを最も効率よく減らす、というのがステアリング駆動開発の核心です(壁打ちの位置づけ)。

サイジングで扱いを変える

インフラ変更は「サイズ」で扱いを変えます。

サイズ基準ステアリング
Sバグ修正・単一ファイル不要WIF 条件の 1 行修正
M単一スタック・モジュール追加必要あるスタックへのリソース追加
L複数スタック・アーキ変更・クラウド統合必要クラウド統合(複数フェーズ)

M / L のタスクは、いきなり apply せず、ステアリングを起こしてから着手します。State drift 解消のような不可逆操作こそ、「実態確認 → バックアップ → 操作」の手順を AI に逸脱させないためにステアリングが効きます。

このインフラ基盤は、リポジトリ直下に CLAUDE.md(禁止事項・必須ルール)、.claude/(Skill / Agent)、.steering/(タスク単位の SSoT)を備え、「ローカルから prod へ直接 apply 禁止」「state 操作前の実態確認必須」といった運用ルールを AI に守らせる設計にしています。手法そのものの詳細は AI 開発シリーズに譲ります。

インフラ × AI 駆動開発の接続点は、第 1 回 概要 の「ステアリング駆動で開発する」節と、ステアリング駆動開発シリーズ を参照してください。

🎯 まとめ

運用の落とし穴構造的な対策
手動変更による State driftCLI 直接操作を禁止し、全変更を Terraform 経由に
State 操作の事故「list → 実態確認 → backup → 操作」の 4 ステップ厳守
stale planplan/apply 分離 + plan artifact の TTL
state lock 競合GCS lock + resource_group: の二重化
403 SERVICE_DISABLEDbootstrap プロジェクトでも API 有効化
allUsers ブロックTag Binding → IAM の順序を depends_on で固定
不可逆操作を AI が逸脱ステアリングで手順を SSoT 化し逸脱を防ぐ

Terraform は「書く」より「運用する」フェーズの設計が事故を分けます。State を正本として丁寧に扱い、不可逆操作には必ず手順を踏む——これが IaC を 3 年運用しても壊れない基盤に保つ作法です。本シリーズはこれで完結です。

📚 シリーズ記事

#タイトル内容
1概要マルチクラウド構成の全体像と AI 駆動運用
2準備編アカウント設計・WIF 認証・bootstrap・State 管理
3モジュール設計編再利用可能なモジュールの設計原則
4CI/CD パイプライン編動的 child pipeline・WIF キーレス認証・GCP↔AWS
5運用編(本記事)State drift 解消・障害対応・AI 運用ループ

関連記事

外部資料