Terraformのstate操作コマンド実践ガイド|state list・state mv・state rmで既存インフラを安全に管理する方法

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Terraform > Terraformのstate操作コマンド実践ガイド|state list・state mv・state rmで既存インフラを安全に管理する方法
「TerraformのHCLを整理してモジュール化しようとしたら、terraform planで大量のdestroy/createが出てしまった」
「既存の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によるバックアップが安全運用の絶対条件


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
図解60P/登録10秒/解除も3秒 / 詳細はこちら

tfstateとは何か──なぜ直接state操作が必要になるのか

Terraformはtfstate(terraform.tfstate)というJSONファイルで、管理しているインフラの「現在の状態」を追跡しています。terraform planterraform 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 mvstate 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

リソースIDによる逆引きは、「このAWSリソースがTerraformのどのリソース定義に紐づいているか」を調べるときに便利です。

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

S3バックエンドを使っている場合、terraform state pullでカレントのtfstate全体をローカルにダウンロードできます。これが唯一の「手動バックアップ」手段です。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、TerraformのIaC設計知識をLinuxマスターの無料マニュアルで体系的に学べます。state操作・module設計・マルチ環境構成まで現役エンジニアが解説します。
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

S3バックエンドを使っている場合、S3のバージョニングが有効であればAWSコンソールから過去バージョンを直接復元することもできます。

ローカルバックエンド: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

シリアル番号はtfstateの更新履歴カウンターです。ロールバック時にはシリアル番号の不一致が起きることがあります。バックアップファイルのシリアル番号を現在のstateよりも大きくすることで解決できます。慎重に作業してください。

本記事のまとめ

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 = ... }
state操作の鉄則を3つ挙げます。

操作前のバックアップは絶対:terraform state pullで保存してから操作する
操作後は必ずplanで確認:「No changes」になれば成功。差分が残る場合はアドレスの指定ミスを疑う
チーム運用ではmovedブロックを優先:コマンドラインのstate mvはローカルにしか反映されないため、チームにはmovedブロックをHCLに書いてapplyで共有する

現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、TerraformのIaC設計知識をLinuxマスターの無料マニュアルで体系的に学べます。state設計・module化・マルチ環境対応まで、現役エンジニアが現場の実践知識をまとめています。
Terraformマニュアルを無料で受け取る >>

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

登録10秒/合わなければ解除3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。