- Published on
OpenTofu 1.12の動的prevent_destroy — lifecycleの静的制約が緩んだ場所と、その代償
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- はじめに — 「Variables may not be used here」
- OpenTofu 1.12がしたこと — 評価のタイミングを移す
- 何を参照でき、何はできないか
- destroy = false — これは別物だ
- prevent_destroyが動的になっても、まだ塞げないもの
- Terraformは別の方向へ行った
- で、これを使うべきか
- おわりに
- 参考資料
はじめに — 「Variables may not be used here」
Terraformを長く使っている人なら、このエラーに出会ったことがあるはずです。データベースインスタンスを誤って消してしまわないよう守りたくて、こう書くと、
variable "allow_destroy_database" {
type = bool
default = false
}
resource "aws_db_instance" "example" {
# ...
lifecycle {
prevent_destroy = !var.allow_destroy_database
}
}
Terraformは拒否します。lifecycleブロックの中では変数を使えません。本番環境は守りつつ開発環境は自由に消したくても、同じモジュールを二重に用意するかcountトリックを使うほかに方法がありませんでした。
これは怠慢で生まれた制約ではありません。HashiCorpのlifecycle文書は理由をはっきり述べています——lifecycleの設定はTerraformが依存関係グラフを構築し走査する方法に影響するため、「only literal values can be used」だということです。任意の式を評価するには処理のタイミングが早すぎるという意味です。グラフを作るにはlifecycleの値を知らなければならず、式を評価するにはグラフが必要です。鶏と卵です。
要望自体は古くからあります。hashicorp/terraform#10730(「Unable to use var for ignore_changes or prevent_destroy」)は2016年12月14日に開かれ2019年8月27日に閉じられ、いまも開いている正式な要望#25534(「Allow variables in lifecycle block」)は2020年7月9日に開かれました。この記事を書いている時点で#25534には👍リアクションが66件付いています。リアクション数は需要の正確な指標ではなく弱いシグナルにすぎず、GitHubのリアクション数はいつでも変わりうる値だという点は割り引いて読んでください。
OpenTofu 1.12がしたこと — 評価のタイミングを移す
OpenTofu v1.12.0は2026年5月14日にリリースされ(GitHubのリリースメタデータ基準)、この制約を緩めました。公式文書の表現では、lifecycleブロックのprevent_destroy引数がいまや同じモジュール内の他のシンボル——たとえばモジュールの入力変数——を参照できるようになりました。
興味深いのはこれをどう緩めたかです。PR #3474の説明は明快です。以前は設定ローダー(config loader)の中でprevent_destroy式を即座に評価していたため、真偽値を返す定数式であるほかありませんでした。いまはローダーが受け取った式をそのまま保存しておき、評価は言語ランタイムに任せます。そうすると同じモジュール内の動的に決まる値を参照できるようになります。
つまり「グラフを作る前に知っておく必要がある」という前提そのものに手を付けたのではなく、評価を先送りできるだけ先送りしたということです。tofu validateの段階では、以前のローダーがしていたのと同等の真偽値型チェックを依然として行い、nil評価コンテキストで評価できない式はplan段階に回してそこで検査します。plan時点のほうが情報が多いのでエラーメッセージもより正確になる、というのがPRの主張です。
冒頭の例——prevent_destroy = !var.allow_destroy_database——は、いまやOpenTofuでそのまま動きます。もとの要望であるopentofu#2522は2025年2月14日に開かれ、このPRで閉じられました。
何を参照でき、何はできないか
ここは正直になるべきところです。「動的」という言葉は「何でもあり」という意味ではありません。
範囲は同じモジュール内に限定されます。そしてPR #3474は、扱いの難しいケース——不明な値(unknown values)、一時的な値(ephemeral values)、機密な値(sensitive values)、count.indexのようなローカルシンボル参照——について意図的にもっとも保守的な選択をしたと明言しています。理由は妥当です。あとのバージョンでもっと許可を広げるのはいつでもできますが、間違いだったとわかったときに、すでに許可したものを取り消すことはできないからです。ルールを緩めるのは後方互換ですが、締めるのはそうではありません。
ここから読み取れる実質的な結論はこうです——リソース属性を参照してprevent_destroyを決めることはできません。リソース属性はapplyまでunknownでありうるし、unknownは保守的に塞がれています。この機能は「変数で環境ごとの保護レベルを決めること」であって、「実行時の状態を見て判断すること」ではありません。
このパターンは初めてではありません。OpenTofuはすでにv1.11.0(2025年12月9日)でenabledメタ引数をlifecycleブロックに導入していて、公式文書が挙げるenabledの制約はほぼ同じ顔ぶれです——不明な値、機密な値、null値、一時的な値、真偽値でない値は使えず、countやfor_eachと一緒に使うこともできません。lifecycleブロックが動的になる仕方には一貫した線があるわけです。値は動的でよいが、plan時点で分かっていなければならない。
destroy = false — これは別物だ
同じ1.12リリースに、destroyという新しいlifecycleメタ引数が入りました。名前が似ているので同じ括りに見えますが性格はかなり違い、実はこちらのほうが状態管理にずっと直接的に影響します。
デフォルトでは、OpenTofuはリソースが設定から消えたとき、置き換えが必要になったとき、あるいはtofu destroyで明示的に消されたときに、実際のインフラオブジェクトを破棄します。destroy = falseにしておくと、破棄の代わりに忘れる(forget)——実オブジェクトはそのままにして状態(state)からのみ取り除く——ことを計画します。
resource "aws_db_instance" "example" {
# ...
lifecycle {
destroy = false
}
}
公式文書が挙げる用例は納得できます。残りの環境をすべて落としながら規制対象のリソースだけ残したいとき、置き換え中に旧インスタンスを残してロールバックをしやすくしたいとき、外部依存のためそもそも破棄できないオブジェクトを扱うとき。これまでtofu state rmを手で叩いていた作業を、レビュー可能な設定変更に変えてくれるというのが要点です。
しかし付いてくる条件は軽くありません。ひとつずつ見ていきましょう。
第一に、定数しか受け付けません。 文書に釘が刺さっています——lifecycle.destroyは定数の真偽値(trueまたはfalse)しか受け付けません。たったいまprevent_destroyが動的になったと言いましたが、同じリリースに入ったdestroyは動的ではありません。lifecycleブロックがまるごと動的になったわけではなく引数ごとに違うということで、この非対称性を知らずに触るとまごつきます。
第二に、状態に居座ります。 この引数は状態に保存されます。一度destroy = falseを適用すると、明示的にtrueへ戻すか設定からそのオプションを消すまで、OpenTofuはそのリソースの破棄を計画しません。最後に適用された設定を基準に保守的に判断し続けるということです。単一インスタンスのリソース(count・for_eachを使わない)ならremovedブロックにdestroy = trueを書いて状態のこの値を上書きできますが、countやfor_eachを使うリソースにはその逃げ道がありません。
第三に、prevent_destroyを無力化します。 これが一番重要です。文書の明示的な警告どおり、destroy = falseを設定するとOpenTofuはdestroyアクションをforgetアクションの変種に置き換え、prevent_destroyはいっさい効果を持ちません。両方の引数を「安全装置」という同じ引き出しに入れて両方オンにすればより安全だろうと考えたくなりますが、実際には後者が前者を消してしまいます。
第四に、忘れたリソースを元に戻すのが面倒です。 文書の警告そのままです——いったん忘れられた(状態から取り除かれた)リソースは、OpenTofuがもう追跡しません。あとで同じアドレスで設定にもう一度入れると、OpenTofuは新しいリソースを作ろうとし、実オブジェクトがまだ生きていれば、その試みは失敗しえます。生き返らせるにはimportをするか、別のリソースアドレスを使うしかありません。
第五に、終了コードが変わります。 destroy = falseのリソースがある状態でtofu destroyを走らせると、それらは破棄ではなく忘れられ、コマンドは一部のリソースが完全には取り除かれなかったことを知らせるため、0以外の終了コードで終わります。CIはこれを失敗と読みます。1.12はこのケースのためにtofu destroy -suppress-forget-errorsもあわせて追加しました。
そしてもうひとつ——これはOpenTofu独自の発明ではありません。Terraformにもremovedブロックがあり、そこにdestroy = falseを与えると、実リソースを破棄せず状態からのみ取り除きます。違いは適用される場所です。removedブロックは設定からリソースを外しながら使うリファクタリングの道具であり、OpenTofuのlifecycle引数はリソースが設定に残っている間の破棄動作を変えます。OpenTofuの文書自身もこの線を引いています——リファクタリング目的ならremovedブロックを、設定に残っているリソースの破棄動作を制御したいときはlifecycleのdestroyを使うよう勧めています。
prevent_destroyが動的になっても、まだ塞げないもの
動的なprevent_destroyがうれしい一方で、これが直さない穴もはっきりさせておく必要があります。
prevent_destroyの保護は、その引数が設定に残っている間だけ有効です。文書自身が述べているとおり、resourceブロックを設定からまるごと消すとprevent_destroyの設定も一緒に消え、OpenTofuは破棄を許してしまいます。つまりこの安全装置は「このリソースを消すplan」は防ぎますが、「このリソースの定義を消すコミット」は防げません。誤ってリソースを消してしまう一番よくある経路がまさに後者であることを思うと、これはかなり大きな限界です。
動的にしたからといってこれが良くなるわけではありません。むしろ表面的には悪くなる余地すらあります——いまやprevent_destroyの値が変数に懸かっているので、tfvarsの一行やCIの環境変数ひとつが保護を切るスイッチになります。そのスイッチがコードレビューを通る場所にあるか、それとも誰でもパイプライン変数で上書きできる場所にあるかは、いまやあなたの問題です。機能がもたらしたのは柔軟性であり、柔軟性は規律を要求します。
PR #3474が残した未解決の問いもひとつあります。動的なprevent_destroyの効果をtofu testでどうテストするのかという問いですが、PRの著者はこのPRの中では一切答えず、関心があれば後で別途扱えるだろうと書き残しています。保護ロジックを変数で分岐させておきながら、その分岐をテストする標準的な方法はまだない、ということです。
Terraformは別の方向へ行った
同じ時期にTerraformは、静的評価の問題をまったく別の場所で解こうとしています。
Terraform v1.15.0は2026年4月29日に出て、新機能一覧にこういう項目があります——モジュールのsourceとversion属性に変数とローカルを使えるようになりました(#38217)。これも「設定読み込み時点で知っておく必要がある値」という同じ系統の制約を緩めたものです。モジュールを取り込むにはsourceを知らなければならず、その値が変数なら変数を先に評価しなければならないからです。方向は同じです。場所が違うだけです。
同じリリースには、deprecated属性を変数・出力ブロックに付ける機能(#38001)、精密なインライン型変換のためのconvert関数(#38160)、backendブロックまで検査するterraform validate(#38021)、Windows ARM64ビルドも入りました。そしてlifecycleブロックは——先に引用した文書の一文がいまもそのままです——依然としてリテラルしか受け付けません。
面白いのは収斂も同時に起きていることです。両プロジェクトとも今回のサイクルでs3バックエンドのaws login資格情報サポートを入れました(OpenTofu #3767、Terraform #37976)。フォークが分岐するだけでなく、エコシステムが同じ要求を押し上げれば両側が並んで同じものを作ることもあるわけです。
この記事を書いている2026年7月16日時点で最新の安定版はOpenTofuがv1.12.4(7月13日)、Terraformがv1.15.8(7月8日)で、Terraformはv1.16.0アルファを回している最中です。
で、これを使うべきか
価値がある場合
- モジュールひとつでdev・staging・prodをすべてカバーしつつ、保護レベルだけ環境ごとに変えたい。これが動的
prevent_destroyの正確な使いどころで、モジュールを複製したりcountトリックを使ったりするよりも確実にすっきりします。 - 環境を落としながら特定のリソースだけ残す手順があり、それをいまは
tofu state rmの手作業ルールブックで回している。destroy = falseはその手作業を、planに現れる設定変更に変えてくれます。
先送りするか避けるべき場合
- 設定がTerraformとOpenTofuの両方で動かなければならない。この構文を使った瞬間、その設定はTerraformではもう動きません。フォーク間を行き来する余地を残しておきたいなら、これは片道切符です。モジュールを公開配布するならなおさらです——利用者の誰がどちらを使うかをあなたが決められないからです。
- 狙いが「うっかりリソースを消してしまう事故を防ぐこと」なら、
prevent_destroyはそもそもその穴(設定からブロックを消す経路)を塞げません。planレビュー、-targetの禁止、CIゲート、状態のバックアップといったプロセス側の仕組みがいまも本体であり、これは補助にすぎません。 destroy = falseを「とりあえずオンにしておけば安全」という発想で扱う場合。状態に居座り、prevent_destroyを無効化し、元に戻すにはimportが要ります。オンにするのは簡単でオフにするのは難しいスイッチです。
おわりに
まとめるとこうです。Terraformのlifecycleブロックがリテラルしか受け付けないのは、依存関係グラフを構築する時点で処理されるからで、これは2016年から指摘されてきた構造的な制約です。OpenTofu 1.12はprevent_destroyの評価を設定ローダーから言語ランタイムへ移すことでこの制約を緩めました——ただし範囲を同じモジュール内に限り、unknown・機密・一時的な値とcount.indexは意図的に保守的に塞いだままにして。取り返しのつかない失敗を避けるために狭く始めたのであり、私はこの判断が正しいと思います。
同じリリースのdestroy = falseは、名前から受ける印象よりずっと重い代物です。定数しか受け付けず、状態に保存され、prevent_destroyを無力化し、忘れたリソースを元に戻すにはimportが必要で、tofu destroyの終了コードを変えます。有用ではありますが、オンにする前にこの5つを全部知っておく必要があります。
そして大きく見れば、これはフォークの「どちらが優れているか」という話ではありません。両方とも静的評価という同じ壁を押していて、押す場所が違うだけです——OpenTofuはlifecycleを、Terraformはモジュールのsourceとversionを。その結果として実務者の手元に実際に残るのは機能一覧ではなく、移植性の決断です。この構文を使った瞬間、あなたの設定はどちらか片方にしか属さなくなります。それが構わないかどうかが、機能が格好いいかどうかより先に答えるべき問いです。
参考資料
- OpenTofu v1.12.0 リリース告知(2026-05-14)
- OpenTofu 1.12 CHANGELOG — 全項目
- OpenTofu 文書 — リソースの挙動とlifecycleのカスタマイズ(
prevent_destroy、destroy) - OpenTofu 文書 —
enabledメタ引数(v1.11で導入) - opentofu/opentofu#3474 — 動的prevent_destroy実装PRと設計根拠
- opentofu/opentofu#2522 — もとの機能要望(2025-02-14)
- Terraform 文書 — lifecycleメタ引数(リテラル値の制約)
- Terraform 文書 —
removedブロック - Terraform v1.15 CHANGELOG(v1.15.0、2026-04-29)
- hashicorp/terraform#25534 — Allow variables in lifecycle block(2020-07-09、未解決)
- hashicorp/terraform#10730 — Unable to use var for ignore_changes or prevent_destroy(2016-12-14)
- OpenTofu vs Terraform 2026 完全比較:IaCツール選択とマイグレーション戦略(関連記事)