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 だったり、指定ミスをやりがち😇
Terraform blocks
概要
この記事における 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.tfremoved.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