トラス構造が幾何学的に組まれた建築物のファサード
技術解説

Terraformのtfstate事故はなぜ起きるか 確認手順と対策

目次を見る

Terraform(HashiCorp社が開発したIaCツール。インフラ構成をコードで宣言的に管理する仕組み)を使い始めたチームで、ある日突然 terraform apply が意図しないリソースを削除してしまう事故が起こることがあります。

この記事は、Terraformをすでに本番環境で使っている、あるいはこれから導入しようとしているインフラ担当・SREの方に向けた内容です。IaCの便利さの裏にある落とし穴と、その確認方法・対策を整理しました。

何が起きるか

Terraformは構成ファイル(.tf)に書いた内容と、実際のクラウド上のリソースの差分を計算し、terraform apply で反映します。この「今の状態」を記録したファイルが tfstate(ステートファイル)です。

tfstateが壊れる、あるいはチーム内で競合すると、次のような事象が起こります。

  • 別メンバーが同時に apply して、片方の変更が上書きされる
  • ローカルのtfstateとリモートの実態がずれ、意図しないリソースが「削除対象」と判定される
  • ステートファイルの参照先が変わり、既存の本番データベースやロードバランサーが再作成扱いになる

影響範囲は構成ファイルの粒度次第です。1つの .tf ファイルで本番のVPC・DB・EC2をまとめて管理していると、1回の事故が全体停止に直結します。

なぜ起きるか

原因は段階的に分解できます。

第一に、tfstateはデフォルトではローカルのディスクに保存されます。チームで共有していないと、各メンバーが自分だけの世界線を持つことになります。

第二に、リモートで共有していても「ロック機構」がないと、複数人が同時に apply した際に競合が起きます。TerraformはS3やTerraform Cloudなどのバックエンドと組み合わせることで、DynamoDBなどを使ったステートロック(同時実行を防ぐ仕組み)を実現できますが、これは自動で有効になるわけではありません。

第三に、terraform plan を確認せずに apply する運用です。plan はコマンド実行前に「何が作られ・変更され・削除されるか」を表示してくれますが、CI上で自動適用にしていると人間の目を通さずに反映されてしまいます。

第四に、シークレット(APIキーやDBパスワードなど)を変数ファイルに平文で書き、それがtfstateにも平文で残るケースです。tfstateはリソースの属性値をそのまま保持するため、意図せず機密情報の置き場になっていることがあります。

自分のプロジェクトが該当するか確認する方法

まず、tfstateの保存場所を確認します。

terraform show terraform.tfstate | head -n 5
cat backend.tf

backend.tf(または各 .tf ファイル内の terraform { backend } ブロック)に local としか書かれていない、もしくはbackend設定自体が存在しない場合は、ステートがローカルにしかない状態です。

次に、ロックの有無を確認します。

terraform plan -lock=true

バックエンドがS3で、DynamoDBのロックテーブルが設定されていない場合、このオプションを付けても実質的には機能しません。backend.tfdynamodb_table の指定があるかを見てください。

CI設定(GitHub Actions・GitLab CI・Jenkinsfileなど)も確認します。

- name: Terraform Apply
  run: terraform apply -auto-approve

-auto-approve が付いたまま本番ブランチに直結していると、plan の結果を人間が見る前に適用されてしまいます。

最後に、機密情報の混入を確認します。

terraform state show aws_db_instance.main | grep -i password

パスワードやトークンがそのまま表示される場合、tfstate自体をアクセス制御された場所(暗号化されたS3バケットなど)に置く必要があります。

対策の手順

1つ目の対策は、リモートバックエンドとロックを設定することです。

terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "prod/terraform.tfstate"
    region         = "ap-northeast-1"
    dynamodb_table = "terraform-lock"
    encrypt        = true
  }
}

S3バケットのバージョニングを有効にしておくと、tfstateが壊れた際に過去のバージョンへ復元できます。

2つ目は、plan を必ず人間の目でレビューする体制です。

CIでは plan の結果をPull Requestのコメントに投稿し、承認後にのみ apply を実行する2段階構成にします。atlantisやTerraform Cloudの「Plan→Apply」フローはこの目的で使われる仕組みです。

3つ目は、状態ファイルの機密情報を管理することです。

シークレットは環境変数やAWS Secrets Manager、HashiCorp Vaultなどの外部ストアから注入し、.tf ファイルや terraform.tfvars に直接書かないようにします。tfstate自体の暗号化(encrypt = true)とアクセス権限の最小化も合わせて行います。

4つ目は、モジュール分割で影響範囲を絞ることです。

本番のネットワーク・DB・アプリケーション層を1つの .tf ファイル群にまとめず、環境ごと・レイヤーごとにステートを分割します。これにより1回の apply ミスが波及する範囲を限定できます。

5つ目は、terraform plan の出力を定期的に監視することです。

意図しないドリフト(コード上の定義とクラウド上の実態のずれ)を検出するため、terraform plan を定期実行し差分が出たらアラートを出す運用にします。これはSLO(サービスレベル目標)の可用性指標を守るうえでも、手動変更による構成崩れを早期に発見する手段になります。

導入前に確認すること

Terraformはインフラの再現性とレビュー可能性を高めてくれる一方、運用が雑だとステート管理そのものが単一障害点になります。

  • backend.tf にリモートバックエンドとロック設定があるか
  • CIの apply 実行前に plan のレビュー工程があるか
  • tfstateに機密情報が平文で残っていないか
  • 本番環境のステートが環境・レイヤーごとに分割されているか

まずは手元のプロジェクトで cat backend.tf を実行し、ロック設定の有無を確認するところから始めてみてください。

参考

Terraform: The Future of Cloud Infrastructure Automation ☁️🚀

この記事について: 本記事は AI を活用して作成し、forva AI 編集部が内容を確認・監修しています。

AI 駆動開発のご相談は forva AI へ。まずはお気軽にどうぞ。