BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News OpenTofu 1.12 The Feature Terraform Never Shipped

OpenTofu 1.12 The Feature Terraform Never Shipped

Listen to this article -  0:00

The OpenTofu community released version 1.12.0 on May 14, 2026. This update isn’t a complete rewrite, but it does resolve some issues that infrastructure teams have faced for a while.

The most discussed feature is dynamic prevent_destroy. Until now, if you wanted to protect a resource from accidental deletion, you had to hard-code that decision in your configuration. That was fine when you owned a single environment, but it broke down immediately in shared module setups, the kind where the same module gets reused across dev, staging, and production. The workaround was always some variation of "duplicate the module" or "accept that dev databases can accidentally be protected too." Neither is great. OpenTofu 1.12 lets you wire prevent_destroy to a variable, so a production workspace can set it to true while a dev workspace leaves it false, without forking the module. It sounds like a small thing. For teams managing dozens of environments from shared module code, it genuinely isn't.

The contrast with Terraform on this particular point is worth noting. Requests to wire prevent_destroy to a variable date back to Terraform 0.7 in 2016. Over the years, the issue tracker gathered multiple threads on this topic. Teams faced errors like "Variables may not be used here" when trying the pattern now standard in OpenTofu 1.12. People tried workarounds like dynamic lifecycle blocks, only to hit "Blocks of type 'lifecycle' are not expected here". Others proposed environment variable overrides like TF_ALLOW_DESTROY as a workaround, which is the kind of solution you invent when the proper fix never arrives. HashiCorp never shipped the feature. Teams using shared modules in different environments faced a tough choice. They had to either duplicate module code or let development and production follow the same lifecycle rules. OpenTofu resolved it in 1.12, roughly a decade after the first request landed in the Terraform issue tracker. That gap says something about the pace of community-driven development versus a product roadmap that has other priorities.

OpenTofu also changed its OperProvider checksum handling. The dependency lock file has historically been a source of low-grade team friction — specifically in setups using a shared provider plugin cache or a local mirror. Running tofu init would populate the lock file with zh: hashes, but you'd then need a separate tofu providers lock run to get the h1: hashes required by the cache or mirror. OpenTofu 1.12 fixes this at the registry level: the OpenTofu Registry now returns a full set of checksums in both formats, so tofu init populates everything in one pass. The first time you run init after upgrading, you'll see new h1: entries appear in your lock file, that's expected, and it's the right behavior. The tofu providers lock command is still here for its original purpose: filling in checksums when you're not installing straight from the OpenTofu Registry.

The third notable addition is the -json-into=FILENAME flag. Now, if you want machine-readable output from OpenTofu commands, use -json. This replaces the human-readable terminal output completely. That forced anyone building tooling on top of OpenTofu's output to replicate the entire UI before they could ship anything useful. The new flag sends the JSON stream to a file (or named pipe, or /dev/fd/N for concurrent consumption) while leaving the normal terminal output intact. If you’re creating a UI for an internal platform that shows real-time application progress, this keeps the integration much cleaner. You won’t have to pick between the two output modes.

There's a new meta-argument: destroy = false. This removes a resource from state without destroying the object. It’s a simpler way to achieve what users used to do with terraform state rm workarounds.

On the deprecation side, WinRM provisioner support is being phased out in 1.12 with warnings, and will be removed in 1.13. The Go libraries backing it have gone unmaintained, and the recommendation is to migrate to OpenSSH for Windows. If you're still running provisioners over WinRM, now is a reasonable time to start that migration rather than wait. Official 32-bit architecture builds are also being phased out, with deprecation warnings expected in 1.13 before the packages stop shipping entirely.

About the Author

Rate this Article

Adoption
Style

BT