tfmigrate と terraform import/removed/moved block の使い分け
はじめに
trocco SRE チームの高塚 (@tk3fftk) です。
Terraform の state 操作は基本的に CLI で行う必要があり、即座に state に反映されてしまうため、CI ツール上で行うのが煩雑になる、ローカルで試していたつもりがうっかり本番の state を触ってしまっていた…などの課題があったかと思います。
この隙間を埋めてくれるのが @minamijoyo 氏の minamijoyo/tfmigrate です。
state 操作を非破壊的に検証・レビュー可能にし、かつ CI にも組み込みやすく[1]、という(少なくとも個人的には)神ツールです。
一方、Terraform 1.7 より removed
block が利用可能になりました。
そのため、terraform plan/apply
経由での state 操作において頻出の import/rm/mv
が出揃ったことになります。
tfmigrate
からこっちに切り替えたほうがもしかして良いのでは…? この前の LT [2] でも 「ネイティブの機能を使おう」って自分で言ってたし…
ということで、trocco SRE チームでは、ここ数ヶ月間で大量の state 操作とレビューする機会[3]があり、tfmigrate
と Terraform ネイティブの state 操作 blocks
(以降は単に blocks
と記載します[4]) の両方を使ってみたうえでの使い分けに関する考えや運用方法を綴ってみます。
前提として、trocco SRE チームでは suzuki-shunsuke/tfaction を利用して Terraform 実行ワークフローを組んでいます。
TL;DR
-
安全に state を操作したいなら
tfmigrate
を使うべき。- state 操作後の状態と Terraform 設定ファイルの状態が異なると失敗扱いにしてくれる。
- trocco SRE チームでは基本的に
tfmigrate
を使いましょう、とルールを定めました。
-
import/removed/moved
と設定の修正を同時に行ったほうが効率や見通しが良いなら terraform 組み込みの state 操作を利用すると便利-
blocks
が意図通りに解釈されていることを目視確認する必要があるため、plan 結果のレビューは複雑になる。 - 操作終了後の
blocks
はその後も対象リソースを操作した場合にも読み込まれるので副作用が起こる可能性あり。
-
tfmigrate
概要
@minamijoyo 氏の minamijoyo/tfmigrate は、state 操作を非破壊的に検証・レビュー可能にする、かつ操作履歴の保存が可能となります。
設計思想や使い方は GitHub リポジトリや作者のTerraformのstate操作をgitにコミットしたくてtfmigrateというツールを書いた- Qiitaを読むのがわかりやすいと思います。
pros
- state 操作後の状態と設定ファイルにズレがあったら失敗扱いにしてくれるため安全に state 操作ができる。
- 手で作成してきたリソースを大量に
import
するケースや、 module 化のタイミングで大量にstete mv
のような Pull Request をレビューするケースで 「tfmigrate plan
通ってるからヨシ!」ができるので助かる。
- 手で作成してきたリソースを大量に
- state 操作の定義方法が、以下のようにほぼ terraform コマンドそのままリストに列挙するだけなので学習コストが低い。
- Copilot Chat など LLM にも書いてもらいやすい (気がする)
migration "state" "test" {
actions = [
"mv aws_security_group.foo aws_security_group.foo2",
"mv aws_security_group.bar aws_security_group.bar2",
]
}
cons
- 仕組み上実行時間が長くなってしまう。
- .tf ファイルではないのでエディタによっては補完が効かせにくい。
- (主観ですが) 列挙されたコマンドたちを目 grep, 目 diff するのがつらい。
- 何かしら typo だったり、指定ミスをやりがち😇
blocks
Terraform 概要
この記事における blocks
は import
, removed
, moved
block の総称としています。
それぞれの block について、何をするのか、相当するコマンド、利用可能になったバージョン、公式ドキュメントのリンクを表にまとめました。
block | どのような操作か | 相当するコマンド | version | 公式ドキュメント |
---|---|---|---|---|
import |
既存のリソースを terraform の state 管理下に入れる | terraform import |
v1.5+ | Import |
removed |
リソースを terraform の state 管理下から外す | terraform state rm |
v1.7+ | Resources |
moved |
terraform の state 管理上のリソースアドレスを変える | terraform state mv |
v1.1+ | Refactoring |
以下、サンプルコードです。
- import
import {
to = aws_instance.example
id = "i-abcd1234"
}
- removed
removed {
from = aws_instance.example
lifecycle {
destroy = false # true にするとリソース自体の削除も行う
}
}
- moved
moved {
from = aws_instance.old_name
to = aws_instance.new_name
}
pros
- state 操作と対象のリソースの修正を同時に行える。
- 例えば、
import
しつつ、aws provider のdefault-tags
で付与される tag の追加など。-
tfmigrate
だと手で AWS 側の差分を解消するか、force
をつけて通したあとに別途apply
する必要がある。
-
- 例えば、
- Terraform のコードとして書ける。
- CLI と HCL のコンテキストスイッチが不要
- エディタ補完も効きやすい。
-
tfmigrate
と比較すると高速
cons
-
blocks
が意図通りに解釈されていることを目で確認する必要がある。- 例えば、10 リソース import したい場合、定義が仮に漏れている場合でも plan は成功するため、
import block
が漏れなく書かれていることを plan 結果から目で確認する必要があります。
- 例えば、10 リソース import したい場合、定義が仮に漏れている場合でも plan は成功するため、
-
blocks
をどこに定義していくべきか問題がある。-
import
に関しては公式ドキュメントでimports.tf
というファイル名が言及されています[5] が、これに合わせてmoved.tf
removed.tf
的な感じで3ファイル作るべきか…?- trocco SRE チームでは
state_migrate.tf
というファイル名にしています。
- trocco SRE チームでは
-
-
blocks
が state 操作が済んだあとも解釈されてしまう。- 例えば、
import block
で import したあと、そのリソースが不要となって tf ファイルから削除して plan 実行するとエラーになります。-
import block
も合わせて削除する必要あり - 削除以外の state 操作に関しても同様
-
- 削除するフローとした場合、いつのタイミングで消してもノイズ感がある。
- 後続の任意の Pull Request に混ぜるのか、削除だけの Pull Request を出すのか、意図せぬふるまいをし始めたら消すのか…
- 例えば、
まとめ
-
tfmigrate
とblocks
の pros/cons を並べてみました。 - terraform 1.7 の現時点では、行いたい操作に応じて使い分けるのが良さそうです。
- ようやく公式が追いついてきましたが、
tfmigrate
は神ツール
- ようやく公式が追いついてきましたが、
-
minamijoyo/tfmigrate: A Terraform / OpenTofu state migration tool for GitOps - Why? ↩︎
-
公式の総称がない気がする。どなたか知ってたら教えてください… ↩︎
-
A common pattern is to create an imports.tf file, or to place each import block beside the resource block it imports into.
Discussion