「既存のAWSリソースをTerraformの管理から外したいのに、誤ってdestroyされてしまわないか心配だ」
「チームメンバーが手動でEC2を増設してしまい、tfstateと実態がずれた。どうやって修正すればいい?」
Terraform運用を本格化するほど、こうした「stateをどう操作するか」という問題に必ずぶつかります。
この記事では、terraform stateサブコマンド(list・show・mv・rm)の使い方を実践形式で解説します。モジュール化に伴うリソース移動、管理対象からの除外、stateのバックアップとロールバック、よくある操作ミスの対処まで、現場で即使える手順を説明します。
実行環境:Terraform 1.8.x、AWS Provider 5.x
この記事のポイント
・terraform state listで管理中リソースのアドレス一覧を確認できる
・terraform state mvはdestroyなしでリソースを移動・リネームできる
・terraform state rmは実インフラを削除せず管理対象から外すだけ
・操作前のterraform state pullによるバックアップが安全運用の絶対条件
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
tfstateとは何か──なぜ直接state操作が必要になるのか
Terraformはtfstate(terraform.tfstate)というJSONファイルで、管理しているインフラの「現在の状態」を追跡しています。terraform planやterraform applyは、このtfstateと実際のクラウドリソースを比較して差分を算出します。通常の作業はHCLを書いて
terraform applyするだけですが、以下のような状況ではtfstateを直接操作する必要が生じます。・HCLのリソース定義をモジュール構成に整理し直したとき(アドレスが変わるためplanでdestroy/createが出る)
・手動で作成したリソースをTerraform管理下に取り込んだとき(
terraform importの前後調整)・特定のリソースだけTerraform管理から外したいとき(destroyせずに管理対象から除外)
・過去のplanエラーやstate破損後のリカバリ
state操作は強力な反面、誤操作するとインフラの管理状態が壊れます。操作前のバックアップを徹底することが最重要です。
terraform state listで管理中リソースを一覧確認する
1. 基本的な実行と出力の読み方
terraform state listは、現在のtfstateに記録されているリソースのアドレスを一覧表示します。# 基本的な実行(カレントディレクトリのtfstateが対象) $ terraform state list # 実行例出力 aws_instance.web aws_security_group.web_sg aws_vpc.main aws_subnet.public_a aws_subnet.public_b module.rds.aws_db_instance.main module.rds.aws_db_subnet_group.main module.alb.aws_lb.main module.alb.aws_lb_listener.http module.alb.aws_lb_target_group.web
・リソース単体:
リソースタイプ.リソース名(例:aws_instance.web)・モジュール内リソース:
module.モジュール名.リソースタイプ.リソース名(例:module.rds.aws_db_instance.main)・for_eachのリソース:
リソースタイプ.リソース名["キー"](例:aws_subnet.public["ap-northeast-1a"])このアドレスが、後述の
state mvやstate rmでそのまま使う引数になります。2. 特定モジュール内のリソースだけを絞り込む
大規模な構成ではリソース数が数百を超えることもあります。-idオプションやgrepと組み合わせて絞り込みましょう。# モジュール名で絞り込む(grepを使う) $ terraform state list | grep module.rds module.rds.aws_db_instance.main module.rds.aws_db_subnet_group.main module.rds.aws_db_parameter_group.mysql8 # リソースIDで逆引き(特定のEC2 IDがどのアドレスか調べる) $ terraform state list -id=i-0a1b2c3d4e5f67890 aws_instance.web
terraform state showでリソースの詳細属性を確認する
terraform state showは、stateに記録された特定リソースのすべての属性を表示します。AWSコンソールと実態を比較したいときや、属性値を参照して別リソースの設定に使いたいときに使います。# リソースアドレスを指定して詳細表示 $ terraform state show aws_instance.web # resource "aws_instance" "web" { # ami = "ami-0c02fb55956c7d316" # arn = "arn:aws:ec2:ap-northeast-1:123456789012:instance/i-0a1b2c3d4e5f67890" # associate_public_ip_address = true # availability_zone = "ap-northeast-1a" # id = "i-0a1b2c3d4e5f67890" # instance_state = "running" # instance_type = "t3.micro" # private_ip = "10.0.1.45" # public_ip = "13.xx.xx.xx" # tags = { # "Env" = "production" # "Name" = "web-server" # } # vpc_security_group_ids = [ # "sg-0abcdef1234567890", # ] # }
terraform state showで表示される属性は、HCLのoutputブロックやdata sourceで参照する値の確認にも使えます。terraform outputが使えない段階での属性確認に重宝します。terraform state mvでリソースを移動・名前変更する
1. モジュール化に伴うリソース移動(最も多い用途)
HCLをモジュール構成に整理し直すと、リソースのアドレスが変わります。このままだとterraform planで「旧アドレスをdestroy → 新アドレスをcreate」という差分が出ます。terraform state mvはstateだけを書き換えてこのdestroy/createを防ぎます。例として、フラットな構成のEC2を
module.webに移動する場合:# Before(モジュール化前のアドレス) # resource "aws_instance" "web" { ... } # After(モジュール化後のアドレス) # module "web" { # source = "./modules/web" # } # modules/web/main.tf: # resource "aws_instance" "web" { ... } # state mvで旧アドレス → 新アドレスに移動 $ terraform state mv aws_instance.web module.web.aws_instance.web Move "aws_instance.web" to "module.web.aws_instance.web" Successfully moved 1 object(s). # 移動後にplanを実行して差分がないことを確認 $ terraform plan No changes. Your infrastructure matches the configuration.
state mv後は必ずterraform planを実行して「No changes」になることを確認してください。差分が残っている場合は移動元・移動先のアドレスが一致していない可能性があります。2. リソース名の変更(destroyを回避するリネーム)
既存のリソース定義名(HCL上の名前)を変更したい場合も同様です。HCLの名前を変えると旧名をdestroy・新名をcreateとみなされます。state mvでstateを書き換えれば、実リソースへの影響なくリネームできます。# リソース名を "app" から "web" に変更する例 $ terraform state mv aws_instance.app aws_instance.web Move "aws_instance.app" to "aws_instance.web" Successfully moved 1 object(s).
3. 実行前の確認手順(バックアップとstate listで二重確認)
state mvは実行直後には元に戻せません。以下の手順を必ず踏んでください。# ステップ1: tfstateのバックアップをとる $ terraform state pull > terraform.tfstate.backup.20260707_100550 # ステップ2: 移動元アドレスを正確に確認 $ terraform state list | grep 移動したいリソース名 # ステップ3: state mvを実行(アドレスは厳密に一致させること) $ terraform state mv "aws_instance.app" "aws_instance.web" # ステップ4: planで差分ゼロを確認 $ terraform plan
terraform state pullでカレントのtfstate全体をローカルにダウンロードできます。これが唯一の「手動バックアップ」手段です。
Terraformマニュアルを無料で受け取る >>
terraform state rmでTerraform管理から除外する
1. 基本的な使い方(インフラ削除なしで管理を外す)
terraform state rmは、tfstateのエントリを削除するコマンドです。実際のAWSリソースは削除されません。Terraform管理から外すだけです。主な用途は以下のとおりです。
・後から手動管理に切り替えたいリソースをTerraform管理から外す
・
terraform importで重複インポートしたときの修正・モジュールごと管理から外したいとき(全リソースをまとめて除外)
# 単一リソースを管理から除外 $ terraform state rm aws_instance.legacy_server Removed aws_instance.legacy_server Successfully removed 1 resource instance(s). # モジュール内の全リソースをまとめて除外 $ terraform state rm module.deprecated_module Removed module.deprecated_module.aws_instance.main Removed module.deprecated_module.aws_security_group.main Removed module.deprecated_module.aws_eip.main Successfully removed 3 resource instance(s).
state rm後、そのリソースのHCL定義を削除すれば「Terraform管理対象外」の状態になります。逆に、HCL定義を残したままにすると次のterraform planで「新規作成」として差分が出るので注意してください。2. 誤操作を防ぐ安全な手順
state rmも操作前のバックアップが必須です。また、除外すべきアドレスを誤認しないよう、state listで必ず確認してから実行してください。# ステップ1: バックアップ $ terraform state pull > terraform.tfstate.backup.20260707_100550 # ステップ2: 対象アドレスの確認(除外したいリソースのアドレスを特定) $ terraform state list | grep legacy aws_instance.legacy_server aws_security_group.legacy_sg # ステップ3: state rmを実行 $ terraform state rm aws_instance.legacy_server # ステップ4: 残りのstateを確認 $ terraform state list | grep legacy aws_security_group.legacy_sg
tfstateのバックアップとロールバック
state操作に失敗したとき、または操作後のplanで予期しない差分が出たとき、バックアップから復元できます。# バックアップを作成(操作前に必ず実行) $ terraform state pull > terraform.tfstate.backup.20260707_100000 # バックアップからロールバック(pushで元のstateを戻す) $ terraform state push terraform.tfstate.backup.20260707_100000 # ロールバック後にplanで確認 $ terraform plan
・ローカルバックエンド:terraform.tfstateファイルをそのままコピーして保存
・S3バックエンド:terraform state pull → state pushでロールバック、またはS3バージョン管理から復元
・Terraform Cloud / HCP Terraform:UIからstateの履歴を参照してロールバック可能
terraform state pushを実行する際は-forceフラグが必要なことがあります(シリアル番号の不一致)。ただし-forceはstate破損のリスクがあるため、確信がある場合のみ使用してください。「terraform moved」ブロック──state mvをコード化する方法(Terraform 1.1以降)
Terraform 1.1以降、terraform state mvの代わりとしてmovedブロックをHCLに記述できます。state mvはコマンドラインで手動実行しなければなりませんが、movedブロックはHCLにコード化できるため、チーム全員がterraform applyするだけで同じstate変更が適用されます。# moved ブロックの書き方(HCLファイルに追記) moved { from = aws_instance.app to = aws_instance.web } # または、モジュール化に伴う移動 moved { from = aws_instance.web to = module.web.aws_instance.web } # terraform planで移動が確認できる $ terraform plan # Terraform will perform the following actions: # # aws_instance.app has moved to aws_instance.web # resource "aws_instance" "web" { # id = "i-0a1b2c3d4e5f67890" # # (no changes) # } # Plan: 0 to add, 0 to change, 0 to destroy.
movedブロックはCI/CDパイプラインと相性が良く、「このコミットでこのリソースを移動した」という変更履歴がgitに残ります。terraform apply後はmovedブロックを削除するか、そのままで問題ありません(Terraformは適用済みのmovedを無視します)。トラブルシュート──state操作でよくあるエラーと対処法
1. 「Error: Invalid target address」が出る
アドレスの指定が間違っています。terraform state listで正確なアドレスを確認し、引用符付きで指定し直してください。# for_eachのリソースはキーを含めて指定する(引用符が必要) $ terraform state rm 'aws_subnet.public["ap-northeast-1a"]'
2. state mvで「Source resource not found」が出る
移動元のアドレスがtfstateに存在しないエラーです。terraform state listで移動元のアドレスを再確認してください。モジュール内のリソースを移動する場合、module.名前.リソースタイプ.リソース名という完全なアドレスが必要です。3. state rmしたリソースが次のplanで「created」になる
HCLにそのリソースの定義が残っていると、tfstateに存在しないリソースを「新規作成すべき差分」として検出します。意図的に管理外にする場合は、HCLから定義も削除してください。# state rmしたリソースのHCL定義を残すとplanでcreateと判定される # → HCL定義も削除するか、lifecycle { ignore_changes = all } で抑制する resource "aws_instance" "legacy" { # ... lifecycle { ignore_changes = all } }
ignore_changes = allはあくまで一時的な回避策です。最終的にはHCL定義を削除してTerraform管理から完全に切り離すことを推奨します。4. 「state push」でシリアル番号エラーが出る
# エラー例 Error: State serial 5 does not match current 8 # バックアップのJSONを開き、"serial" の値を現在より大きい値に変更してから再push # (例:"serial": 9 に変更) $ terraform state push terraform.tfstate.backup.20260707_100000
本記事のまとめ
terraform stateサブコマンドの主要な操作を整理します。| やりたいこと | コマンド |
|---|---|
| 管理中リソースの一覧を確認する | terraform state list |
| 特定リソースIDからアドレスを逆引きする | terraform state list -id=リソースID |
| リソースの詳細属性を確認する | terraform state show アドレス |
| リソースを移動・リネームする(destroyなし) | terraform state mv 移動元 移動先 |
| リソースをTerraform管理から除外する | terraform state rm アドレス |
| tfstateをローカルにバックアップする | terraform state pull > ファイル名 |
| バックアップからstateを復元する | terraform state push ファイル名 |
| state mvをHCLコード化する(1.1以降) | moved { from = ... to = ... } |
・操作前のバックアップは絶対:
terraform state pullで保存してから操作する・操作後は必ずplanで確認:「No changes」になれば成功。差分が残る場合はアドレスの指定ミスを疑う
・チーム運用ではmovedブロックを優先:コマンドラインのstate mvはローカルにしか反映されないため、チームには
movedブロックをHCLに書いてapplyで共有するTerraformマニュアルを無料で受け取る >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます