Terraformのmoved・removedブロックで安全にリファクタリングする方法|state mvに頼らない宣言型の構成変更

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Terraform > Terraformのmoved・removedブロックで安全にリファクタリングする方法|state mvに頼らない宣言型の構成変更
「リソース名を変えたいが、terraform state mv を打ち間違えたら取り返しがつかない」
Terraform で本番インフラを管理していると、そんな不安を抱える場面は必ずあります。

CLI コマンドによる state 操作は強力ですが、誰がいつ実行したかの履歴が残らず、チームで共有もできません。

Terraform 1.1 以降で導入された moved ブロック、そして 1.7 以降の removed ブロックを使えば、リソースの移動や state 管理除外を HCL コードとして宣言できます。変更内容を terraform plan で事前確認し、Pull Request でレビューして、チームに共有しながら安全にリファクタリングできる。これが宣言型アプローチの本質です。

この記事では、moved ブロック・removed ブロックの構文から実務リファクタリングパターン、よくあるエラーの対処まで体系的に解説します。

この記事のポイント

・moved ブロックは state mv の宣言型代替で VCS 管理・PR レビューが可能
・removed ブロックの destroy=false でインフラを残したまま state 除外できる
・plan 実行時に「Moved」表示で事前確認できるため誤操作リスクがほぼゼロ
・Terraform 1.1 以降で moved、1.7 以降で removed が利用可能


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

movedブロックとは何か:state mvとの本質的な違い

Terraform でリソース名を変更したり、モジュール間で移動したりするとき、従来は terraform state mv コマンドを使っていました。しかし CLI 操作には次の問題があります。

・実行ログが state ファイルに残らない(誰がいつ変更したか追跡困難)
・コマンドを誰かが個別に実行する必要がある(チーム運用でアドホックになりやすい)
・ミスった場合のロールバックが難しい

moved ブロックは Terraform 1.1 で導入された宣言型の解決策です。.tf ファイルに記述して git commit し、terraform apply を実行するだけで、Terraform が自動的に state を移行します。

比較項目 terraform state mv(CLI) moved ブロック(宣言型)
VCS 管理 できない できる(.tf ファイルに記述)
PR レビュー できない できる
plan 確認 できない できる(Moved 表示で事前確認)
チーム共有 個別実行が必要 apply 時に全員に自動適用
必要バージョン Terraform 0.x 以降 Terraform 1.1 以降

movedブロックの書き方と基本構文

1. 基本構文

moved ブロックは fromto の2つの引数だけで構成されます。

# aws_instance.server を aws_instance.web_server に名前変更する moved { from = aws_instance.server to = aws_instance.web_server } # 変更後のリソース定義(新名称) resource "aws_instance" "web_server" { ami = "ami-0abcdef1234567890" instance_type = "t3.micro" }

moved ブロックを追加したら、terraform plan を実行します。

2. terraform plan の出力確認

plan 実行時に「Moved」表示が出たら正しく認識されています。

$ terraform plan Terraform will perform the following actions: # aws_instance.server has moved to aws_instance.web_server resource "aws_instance" "web_server" { id = "i-0a1b2c3d4e5f67890" ami = "ami-0abcdef1234567890" instance_type = "t3.micro" tags = { "Name" = "web-server-prod" } # (12 unchanged attributes hidden) } Plan: 0 to add, 0 to change, 0 to destroy.

「0 to add, 0 to change, 0 to destroy」と表示されれば、実インフラは何も変更されず、state のアドレスだけが移行されます。この確認ができるのが、CLI の state mv にはない最大のメリットです。

terraform apply で適用後、moved ブロックはそのまま残しておいても問題ありません(後から削除した場合も state は変わりません)。チームで「いつ何を移動したか」の記録として残す運用も一般的です。

モジュール内リソースの移動に使うパターン

1. フラットなリソースをモジュールへ移動する

コードが肥大化してモジュール化を進める場面は多いはずです。既存リソースをモジュールに移動する場合も、moved ブロックが使えます。

# ルートモジュールにあった aws_s3_bucket.logs を # module.logging 内の aws_s3_bucket.main に移動する moved { from = aws_s3_bucket.logs to = module.logging.aws_s3_bucket.main } # modules/logging/main.tf に移動後のリソース定義を記述 # module "logging" の呼び出しをルートモジュールに追加

2. モジュール間の移動

既存モジュールから別モジュールへの移動にも対応しています。

# module.old_network から module.vpc への移動例 moved { from = module.old_network.aws_vpc.main to = module.vpc.aws_vpc.main }

3. count / for_each 使用リソースの移動

インデックス指定も可能です。count から for_each への変換時に役立ちます。

# count から for_each へ変換する際の moved ブロック例 moved { from = aws_security_group.allow_http[0] to = aws_security_group.allow_http["web"] }

removedブロックとは:リソースを壊さず管理から外す

リファクタリングで「Terraform の管理対象から除外したいが、インフラそのものは消したくない」という要件があります。

例として、以下のようなケースが挙げられます。

・別の Terraform プロジェクトや別チームに管理を移管する
・一時的に手動管理に切り替える
・既存リソースを IaC 管理から外してコストを削減するリファクタリング

従来は terraform state rm コマンドを使っていましたが、Terraform 1.7 で導入された removed ブロックを使えば、同じ操作を宣言型で行えます。

比較項目 terraform state rm(CLI) removed ブロック(宣言型)
VCS 管理・PR レビュー できない できる
インフラを残す設定 できる(state rm は常にインフラ存続) destroy = false で指定
インフラを削除する設定 別途 destroy が必要 destroy = true で同時実行
必要バージョン Terraform 0.x 以降 Terraform 1.7 以降

removedブロックの書き方とdestroyオプション

1. インフラを残して state から除外する(destroy = false)

最もよく使うパターンです。AWS リソースを残したまま Terraform の管理対象から外します。

# aws_instance.legacy を state から除外するが、EC2 インスタンスは残す removed { from = aws_instance.legacy lifecycle { destroy = false } }

terraform plan の出力確認例:

$ terraform plan Terraform will perform the following actions: # aws_instance.legacy will no longer be managed by Terraform after apply # (your real infrastructure will be left intact) resource "aws_instance" "legacy" { id = "i-0b2c3d4e5f6a7b8c9" instance_type = "t3.small" # ... } Plan: 0 to add, 0 to change, 0 to destroy.

apply 後、そのリソースは Terraform の state から消えますが、EC2 インスタンス自体は AWS 上で動き続けます。

2. state 除外と同時にインフラも削除する(destroy = true)

destroy = true はデフォルト値です。設定を省略した場合と同じ挙動になります。

# aws_instance.old を state 除外と同時に実インフラも削除する removed { from = aws_instance.old lifecycle { destroy = true # デフォルト値。省略可 } }

注意:destroy = true の場合は terraform apply で実インフラが削除されます。本番環境では必ず terraform plan で事前確認し、削除対象が意図通りかを確認してから apply してください。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、moved・removedブロックを含むTerraform宣言型リファクタリングの実践をハンズオン形式で学べるセミナーを開催しています。
>> Terraform実践セミナーの詳細はこちら

moved+removedを組み合わせたリファクタリング実例

実務では moved と removed を組み合わせる場面があります。ここでは「ルートモジュールのリソースをモジュール化し、不要になった古いリソースを state から除外する」リファクタリング例を示します。

1. リファクタリング前の構成

# main.tf(リファクタリング前) resource "aws_s3_bucket" "app_data" { bucket = "my-app-data-prod" } resource "aws_s3_bucket" "logs_old" { bucket = "my-logs-legacy-prod" # このバケットは別チームに移管済みで Terraform 管理不要 }

2. リファクタリング後の構成

# main.tf(リファクタリング後) # 既存リソースをモジュールへ移動 module "storage" { source = "./modules/storage" } # aws_s3_bucket.app_data を module.storage.aws_s3_bucket.main へ移動 moved { from = aws_s3_bucket.app_data to = module.storage.aws_s3_bucket.main } # logs_old は別チーム管理になったため state から除外(バケット自体は残す) removed { from = aws_s3_bucket.logs_old lifecycle { destroy = false } }

3. modules/storage/main.tf

# modules/storage/main.tf resource "aws_s3_bucket" "main" { bucket = "my-app-data-prod" }

この構成で terraform plan を実行すると、

aws_s3_bucket.app_datamodule.storage.aws_s3_bucket.main へ移動(Moved 表示)
aws_s3_bucket.logs_old が state 除外(削除なし)

が確認できます。実インフラへの変更は発生せず、「Plan: 0 to add, 0 to change, 0 to destroy」と表示されます。

よくあるエラーと対処法

1. バージョン不一致エラー(Terraform 1.1 / 1.7 未満)

Error: Unsupported block type on main.tf line 1: 1: moved { Blocks of type "moved" are not expected here.

対処:terraform version でバージョンを確認し、1.1 未満の場合はアップグレードしてください。removed ブロックは 1.7 以上が必要です。required_version に下限を設定しておくと、チーム内のバージョン不一致を防げます。

2. from アドレスの記述ミス

Error: Invalid "moved" block target address from address "aws_instance.old_server" doesn't refer to an existing resource in the state.

対処:terraform state list で現在の state アドレスを確認し、from の記述を一致させてください。モジュールを使っている場合は module.name.resource_type.name の形式が必要です。

3. 循環参照エラー

Error: Cycle in moved block references Terraform detected a cycle in the moved block references: aws_instance.a -> aws_instance.b -> aws_instance.a

対処:moved ブロックが循環参照になっています。A→B、B→C のように一方向になるよう整理してください。移動の順序が複雑な場合は段階的に apply することも有効です。

4. destroy = false なのに削除が走るケース

removed ブロックで destroy = false を指定しているにもかかわらず、plan に削除操作が含まれる場合は、他の設定変更が副作用を引き起こしている可能性があります。

対処:plan の全出力を精査し、removed ブロック以外の変更で削除が発生していないか確認してください。

まとめ

やりたいこと 使うブロック 必要バージョン
リソース名を変更する moved { from = ... to = ... } Terraform 1.1 以上
ルートモジュール→モジュールへ移動 moved { from = res to = module.name.res } Terraform 1.1 以上
state 除外・インフラは残す removed + lifecycle { destroy = false } Terraform 1.7 以上
state 除外・インフラも削除 removed + lifecycle { destroy = true } Terraform 1.7 以上
moved ブロックと removed ブロックは、従来の CLI 操作(state mvstate rm)を宣言型に置き換えるものです。コードとして記述することで VCS 管理・PR レビュー・plan 事前確認が可能になり、チーム運用での安全性が大幅に高まります。

moved ブロックは Terraform 1.1 以降で利用可能。リソース名変更やモジュール移動を HCL で宣言する
removed ブロックは Terraform 1.7 以降で利用可能。destroy = false でインフラを残したまま state 除外できる
・どちらも terraform plan で事前確認できるため、CLI 操作のミスリスクをほぼゼロにできる

state 操作のリスクを減らしながら安全にリファクタリングを進めたい場合は、まず moved ブロックと removed ブロックの導入を検討してみてください。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Terraform 実務で即使えるリファクタリング設計・state 管理・CI/CD 連携を少人数ハンズオン形式で体系的に学べるセミナーを開催しています。
>> Terraform実践セミナーの詳細はこちら

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

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

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

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

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

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

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

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

この記事を書いた人

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

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

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