OpenTofu 1.12で「dynamic prevent_destroy」がついに実現 — Terraformが10年放置した機能をIaCモジュール設計の武器にする

IaC & Automation

この記事の概要

2026年5月14日、OpenTofu 1.12.0がリリースされました。
最大のハイライトは、Terraform 0.7(2016年)から約10年間要望され続けながら放置されてきた「dynamic `prevent_destroy`」の実装です。
`lifecycle`ブロック内の`prevent_destroy`に変数を参照できるようになったことで、本番環境と開発環境でモジュールを分岐せずに安全性をコントロールできるようになりました。
加えてプロバイダーのチェックサム自動補完や`-json-into`オプションなど、現場の痒いところに手が届く改善が揃っています。

OpenTofu 1.12がもたらす3つの実務改善

OpenTofu 1.12.0の主な変更点は以下の3つです。

  1. `prevent_destroy`の動的化 — `lifecycle`ブロックで変数参照が可能に
  2. プロバイダーチェックサムの自動補完 — `tofu init`だけで`h1:`と`zh:`両形式のハッシュが揃う
  3. `-json-into=FILENAME`オプション — 人間向けターミナル出力とJSON出力の同時取得

順を追って詳しく見ていきましょう。

なぜ「static prevent_destroy」は現場の邪魔者だったのか

従来の痛み:環境ごとにモジュールをコピペするしかなかった

`prevent_destroy`は、IaCで管理しているリソースを誤って削除しないための安全弁です。
RDSインスタンスや重要なS3バケットに設定しておくことで、`tofu destroy`や意図しないリソース削除を防いでくれます。

# 以前はこう書くしかなかった(ハードコーディング)
resource "aws_db_instance" "main" {
  # ...
  lifecycle {
    prevent_destroy = true  # 固定値しか書けない
  }
}

問題は、`prevent_destroy = true`はモジュール内にハードコードされるため、開発環境でも同じ制約が適用されてしまうことでした。
開発環境では頻繁にリソースを作り直したいのに、`prevent_destroy`のせいでエラーが出ます。
かといって`false`にすれば本番が危険になります。

結果として多くのチームが取ってきた解決策は:

  • 本番用モジュールと開発用モジュールを別々に管理(コピペ地獄)
  • `prevent_destroy`を全部外して手順書で運用カバー(ヒューマンエラーの温床)

この問題はTerraform 0.7(2016年)のIssueとして報告されたが、HashiCorpは約10年間対応せず放置してきました。
OpenTofu 1.12でようやく解決しました。

実際に使ってみる:dynamic prevent_destroyの実装例

基本的な使い方

# variables.tf
variable "prevent_destroy_database" {
  type        = bool
  description = "本番環境ではtrue、開発環境ではfalseを指定"
  default     = true
}
# main.tf
resource "aws_db_instance" "main" {
  identifier        = "myapp-db"
  engine            = "mysql"
  engine_version    = "8.4"
  instance_class    = "db.t3.medium"
  allocated_storage = 20

  lifecycle {
    prevent_destroy = var.prevent_destroy_database  # 変数参照OK!
  }
}
# environments/prod/terraform.tfvars
prevent_destroy_database = true

# environments/dev/terraform.tfvars
prevent_destroy_database = false

これだけで、同一モジュールを本番と開発で使い回しながら、環境ごとに削除保護のオン/オフを切り替えられます。

モジュール呼び出し側からの制御

# 呼び出し側(ルートモジュール)
module "database" {
  source = "./modules/database"

  prevent_destroy_database = terraform.workspace == "production" ? true : false
}

`terraform.workspace`を使えば、ワークスペース名に応じて動的に切り替えることもできます。

制約事項に注意

現時点では「同一モジュール内の値」しか参照できません。
他モジュールの出力値や`data`ソースを直接参照することはまだできません。
複雑な依存関係が必要な場合はワークスペースや`tfvars`で渡す設計にしておきましょう。

`tofu init`のチェックサム問題が解消

地味に現場を悩ませてきたのが、`.terraform.lock.hcl`のチェックサム問題です。

# 以前のlock.hcl — zh: ハッシュしか入っていない
provider "registry.opentofu.org/hashicorp/aws" {
  version     = "5.90.0"
  constraints = "~> 5.0"
  hashes = [
    "zh:abc123...",
    # h1: ハッシュが不足しているためキャッシュ利用時にエラー
  ]
}

共有プロバイダーキャッシュやローカルミラーを使っているチームでは、`h1:`ハッシュが不足しているために`tofu init`が失敗するケースが多発していました。
回避策として`tofu providers lock -platform=linux_amd64 -platform=darwin_arm64`のような複数プラットフォーム向けコマンドを毎回手動実行する必要がありました。

OpenTofu 1.12以降は、`tofu init`が`zh:`と`h1:`両形式のハッシュを自動補完するようになりました。

# 初回 tofu init 実行後に自動的に両形式のハッシュが記録される
$ tofu init

# 生成されたlock.hcl
provider "registry.opentofu.org/hashicorp/aws" {
  version = "5.90.0"
  hashes = [
    "h1:xyz789...",  # 自動追加!
    "zh:abc123...",
  ]
}

CI/CDパイプラインでキャッシュを活用しているチームにとっては、かなりの手間削減になります。

`-json-into`オプション:CI/CDとターミナル表示の両立

# ターミナルには人間向け出力、CIシステムにはJSON出力を同時送信
$ tofu plan -json-into=/tmp/plan_output.json

# ターミナルではいつも通りの見やすい差分表示
# /tmp/plan_output.json にはパース可能なJSON形式で同じ情報が保存される

Terraform/OpenTofuの出力をSlackに通知したり、独自のCI UIに統合したりするとき、`-json`フラグだと人間向け出力が消えてしまうのが悩みどころでした。
`-json-into`を使えば両方が取れます。
名前付きパイプ(`mkfifo`)経由でストリーム処理することも可能です。

Terraformから移行すべき今がその理由

OpenTofuはTerraformを「機能面で追い越した」

2023年8月のHashiCorp BSL移行以降、OpenTofuはLinux Foundation傘下でOSSとして開発を続けてきました。
2026年時点でのポイントは:

  • dynamic prevent_destroy(OpenTofu 1.12)— Terraform未実装
  • State暗号化(OpenTofu 1.7〜)— S3/GCS/Azure Blobのstateをクライアント側で暗号化
  • プロバイダーとモジュールは後方互換 — 既存のTerraformコードがそのまま動く(大半のケース)
  • BSL制約なし — 商用製品への組み込みや内部ツールへの利用が自由

移行コストは環境によって異なるが、`tofu`コマンドに置き換えるだけで動くケースが多くなっています。

# インストール(Homebrew)
brew install opentofu

# 既存のTerraformプロジェクトでそのまま実行
cd your-terraform-project
tofu init
tofu plan

移行の注意点

  • Terraform Cloudはそのまま使えない — 代替はenv0、Spacelift、Scalr等
  • provider `source`がOpenTofu Registryに変わる — `registry.terraform.io` → `registry.opentofu.org`(自動移行される)
  • WinRMプロビジョナーが1.12で非推奨に — 1.13で削除予定、OpenSSH for Windowsへ移行が必要

まとめ

OpenTofu 1.12のキーポイントをまとめると:

  • `prevent_destroy`に変数参照が使えるようになり、環境ごとのモジュールコピペが不要に
  • `tofu init`がチェックサムを自動補完するため、CIでのlock.hcl管理が楽になる
  • `-json-into`でターミナル出力とJSON出力の同時取得が可能に

10年越しの要望を実現したdynamic `prevent_destroy`は、IaCモジュールの設計を根本から改善する機能です。
Terraformをまだ使い続けているチームにとって、OpenTofu移行を検討する十分な理由になります。
まずは開発環境の1プロジェクトから試してみてください。

参考リンク

コメント