使用 Terraform moved Block 重新命名資源
簡介
moved block 是 Terraform 1.1+ 提供的宣告式語法,用於通知 Terraform 某個資源的「位址」(資源類型 + 名稱,或再加上模組路徑)已經變更,但底層真實的基礎設施並未改變。Terraform 會自動將 state 中舊位址下的資源紀錄遷移到新位址,避免不必要的銷毀與重建。
為什麼需要
Terraform 以「位址」(例如 aws_subnet.public)作為 state 中識別資源的鍵。若你只是把 .tf 檔案中的資源重新命名(例如改為 aws_subnet.first_public),Terraform 會視為:
- 舊資源
aws_subnet.public已不存在於配置 → 應銷毀 - 新資源
aws_subnet.first_public不存在於 state → 應建立
結果就是真實基礎設施被銷毀重建,造成服務中斷與資料遺失。moved block 即用來解決此問題。
moved block vs terraform state mv
| 項目 | moved block | terraform state mv |
|---|---|---|
| 形式 | 宣告式(寫在 .tf 中) | 命令式(CLI 操作) |
| 是否進入版本控制 | 是 | 否(CLI 操作不會留在 repo 中) |
| 團隊協作 | 任何人 pull 後跑 plan/apply 即生效 | 每位成員都需手動執行命令 |
| 推薦場景 | 一般情境 | 例外情境(如跨 state 檔搬移) |
通常推薦使用 moved block。
使用步驟
-
在任一
.tf檔案中(建議單獨開一個moved.tf,方便事後清理)加入movedblock:moved { from = <舊位址> to = <新位址> } - 修改
.tf檔案中的資源宣告名稱,並更新所有內部引用。 - 執行
terraform validate確認語法正確。 - 執行
terraform plan,預期看到n to move, 0 to add, 0 to change, 0 to destroy。 - 若 plan 結果符合預期,執行
terraform apply。 - apply 完成後,
movedblock 已成為 no-op,可在後續 commit 中刪除(保留也可,僅是冗餘)。
實際範例
假設原有以下資源:
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
# ...
}
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
output "public_subnet_id" {
value = aws_subnet.public.id
}
要將 aws_subnet.public 重新命名為 aws_subnet.first_public:
步驟 1:新增 moved.tf
moved {
from = aws_subnet.public
to = aws_subnet.first_public
}
步驟 2:修改資源宣告
resource "aws_subnet" "first_public" { # 原本為 "public"
vpc_id = aws_vpc.main.id
...
}
步驟 3:更新所有引用該資源的位置
resource "aws_route_table_association" "first_public" {
subnet_id = aws_subnet.first_public.id # 引用已更新
route_table_id = aws_route_table.public.id
}
步驟 4:更新 output(output 名稱保持不變,以維持下游 stack 的契約)
output "public_subnet_id" { # output 名稱不變
value = aws_subnet.first_public.id # 引用更新
}
步驟 5:執行 plan 與 apply
terraform plan # 預期:1 to move,0 to add/change/destroy
terraform apply
注意事項
movedblock 中的from必須是仍存在於 state 中的舊位址;若 state 中已無該資源,plan 會報錯。- 若同時重新命名多個彼此引用的資源,每個都需要各自的
movedblock,不能省略。 -
movedblock 也可跨模組搬移資源,例如從根模組搬入子模組:moved { from = aws_instance.nat to = module.network.aws_instance.nat } - 重新命名只是改變 Terraform 的識別位址,不會更動 AWS 端的實體屬性(例如 Name tag)。若想同步更新 Name tag,需另外修改
tags區塊;這是真實異動,會被 plan 反映出來。
清理
apply 成功後:
movedblock 已完成任務,可以安全刪除。- 刪除後再次執行
terraform plan,應顯示No changes。 - 若使用 S3 + DynamoDB 共享 backend,任何人 apply 後 state 即同步,可立即刪除
moved.tf;若是個人 local state 或非共享 backend,建議等所有成員都 apply 過後再刪除。