OpenTofu 1.12 新機能まとめ:dynamic prevent_destroy でマルチ環境IaCがついに楽になった

DevOps

この記事の概要

2026年5月14日、OpenTofu 1.12.0 がリリースされました。
最大の目玉は prevent_destroy を変数で動的に制御できるようになったこと。
本番環境では削除を禁止しつつ、開発環境では自由に壊せる設定を1つのモジュールで表現できます。
本記事では実用的なコード例とともに主要4機能を解説します。


OpenTofu 1.12 を使えば「本番だけ削除禁止」がモジュール分岐なしで実現できる

インフラエンジニアなら一度は悩んだことがあるはずです。
「本番のデータベースは誰も消せないようにしたい。
でも開発環境では気軽に作り直したい」——Terraform / OpenTofu の prevent_destroy は便利な機能ですが、これまでは 静的な設定しかできない という制約がありました。

環境ごとにモジュールを分岐させたり、ワークスペースを使い分けたりして、地味に管理コストが積み上がっていた方も多いのではないでしょうか。
OpenTofu 1.12 は、そこに正面から答えを出してきました。


なぜ従来の prevent_destroy では限界があったのか

従来の Terraform / OpenTofu では、prevent_destroy の値はリテラルの truefalse しか書けませんでした。

# これはできる
lifecycle {
  prevent_destroy = true
}

# これは従来NG(変数参照ができなかった)
lifecycle {
  prevent_destroy = var.is_production
}

この制約のせいで、チームは次のような回避策を強いられてきました。

  • モジュールを本番用と開発用に分岐させる → コードの二重管理が発生
  • ワークスペースごとに設定ファイルを書き換える → ヒューマンエラーのリスク
  • prevent_destroy を使わず、IAM 権限で削除を制限する → 本質的な解決ではない

Terraform の Issue トラッカーには「prevent_destroy を動的にしたい」というリクエストが約10年前から存在していました。
Terraform は長年この課題を放置しましたが、OpenTofu 1.12 でついに解決されました。


実際に OpenTofu 1.12 の新機能を使ってみる

実装例①:dynamic prevent_destroy

以下のように lifecycle.prevent_destroy に変数を渡せるようになりました。

variable "prevent_destroy_database" {
  type    = bool
  default = true  # デフォルトは削除禁止
}

resource "aws_db_instance" "main" {
  identifier        = "myapp-db"
  engine            = "mysql"
  engine_version    = "8.0"
  instance_class    = "db.t3.medium"
  allocated_storage = 20

  lifecycle {
    prevent_destroy = var.prevent_destroy_database  # 変数で動的制御!
  }
}

本番環境の terraform.tfvars では何も書かずにデフォルトの true を使い、開発環境では以下のように明示的に無効化します。

# dev/terraform.tfvars
prevent_destroy_database = false

これで1つのモジュールを本番・開発の両環境で共有しながら、削除保護の挙動を切り替えられます。

実装例②:destroy = false メタ引数

もう1つ実用的な新機能が destroy = false ライフサイクルオプションです。

resource "aws_s3_bucket" "legacy" {
  bucket = "myapp-legacy-bucket"

  lifecycle {
    destroy = false  # Terraform 管理から外すが、実際のバケットは削除しない
  }
}

これは「OpenTofu の管理対象から外したいが、実際のリソースは残しておきたい」というケースで非常に役立ちます。
従来は terraform state rm コマンドで手動操作が必要でしたが、コードとして宣言できるようになりました。

実装例③:プロバイダーロックファイルの改善

従来、CI/CD パイプラインでは tofu providers lock を別途実行しないと、ロックファイルが不完全になる問題がありました。
1.12 からは tofu init だけで zh:h1: 両方のハッシュが自動的に記録されます。

# 1.11 以前はこれが必要だった
tofu init
tofu providers lock -platform=linux_amd64 -platform=darwin_arm64

# 1.12 からは init だけでOK
tofu init

実装例④:JSON出力と人間向け出力の同時利用

-json-into=FILENAME オプションにより、ターミナルには通常の出力を表示しながら、JSON形式のログをファイルに保存できます。

# 人間向け出力をターミナルに表示しつつ、JSON ログをファイルに保存
tofu apply -json-into=/tmp/tofu-apply.json

# 名前付きパイプを使えばリアルタイムに別プロセスで消費できる
mkfifo /tmp/tofu-pipe
cat /tmp/tofu-pipe | jq '.type' &
tofu plan -json-into=/tmp/tofu-pipe

CI/CD のダッシュボードや Slack 通知といったカスタム UI を作りたいチームにとって、大幅に実装が楽になります。

実務での活用シナリオ

ユースケース 使う機能 効果
本番/開発のマルチ環境管理 dynamic prevent_destroy モジュール分岐を解消
レガシーリソースの管理外し destroy = false state rm の手動操作が不要に
CI/CD のプロバイダー管理 ロックファイル改善 providers lock の別途実行が不要
カスタム監視 UI -json-into ターミナル出力と JSON ログを両立

今すぐ OpenTofu 1.12 にアップグレードすべき3つの理由

ここまで見てきた通り、OpenTofu 1.12 は「地味だが実務で痛かった問題」を着実に解消しています。
特にアップグレードを推奨する理由を3つ挙げます。

① Terraform が10年積み残した課題を解決した
prevent_destroy の動的制御は、Terraform コミュニティが長年要望していたものです。
OpenTofu はオープンソースのアジリティを活かし、これを実現しました。

② 後方互換性が高く、移行コストが低い
既存の .tf ファイルはほぼそのまま動きます。
新機能はすべてオプトイン型のため、既存コードへの影響はほぼゼロです。
まず tofu init && tofu plan でロックファイルの差分を確認するだけで移行が始められます。

③ Terraform との分岐が広がっている
Terraform は BSL ライセンスに移行後、オープンな機能追加の速度が落ちています。
一方 OpenTofu は CNCF プロジェクトとして積極的に開発が続いています。
ライセンスリスクを避けながら最新機能を使うなら、今が乗り換えの好機です。


まとめ

OpenTofu 1.12 の主な新機能は以下の4点でした。

  1. dynamic prevent_destroy — 変数で削除保護を動的に切り替えられる
  2. destroy = false — リソースを状態管理から外しても実物は残す
  3. プロバイダーロックファイルの自動完全化tofu init だけで全プラットフォームのハッシュが記録される
  4. -json-into オプション — 人間向けとマシン向けの出力を同時に使える

prevent_destroy の動的制御は、マルチ環境を管理するインフラチームにとって特に嬉しい変更です。
まずは開発環境の .tf ファイルに prevent_destroy = var.prevent_destroy_db を試してみてください。


参考リンク

コメント