DevOps

OpenTofu 1.13 Makes Infrastructure Plans More Precise

Infrastructure plans are valuable only to the extent that they describe the change an automation system will actually make. OpenTofu 1.13 attacks one of the least glamorous but most consequential gaps in that promise: values that remain unknown until apply. Its new convert and assume... functions let module authors add information the engine cannot infer on its own, potentially making plans more useful to reviewers, policy engines, and downstream modules.

The release, published September 29, is deliberately smaller than some earlier OpenTofu updates. The project shortened its development cycle to align more closely with Go releases and extend the series’ security-support window through August 2027. That makes the planning changes easy to underestimate. They are not flashy syntax for its own sake. They recognize that infrastructure-as-code governance depends on the quality of information available before a change reaches production.

The central lesson for platform teams is straightforward: OpenTofu 1.13 can reduce avoidable uncertainty in plans, but it does so by asking module authors to take explicit responsibility for claims about future values. That trade is useful when guarantees are real and dangerous when assumptions are merely hopeful.

Why unknown values create operational friction

OpenTofu builds a dependency graph and predicts changes during planning, but providers often cannot know an attribute until an API creates or updates a remote object. The familiar (known after apply) marker is an honest representation of that limit. An ID allocated by a cloud service, for example, may not exist yet.

Unknown does not always mean that every property of a value is unknowable, however. A provider may not know the future VPC ID, while its API contract still guarantees that the ID will be a non-null string beginning with vpc-. Historically, that distinction could be lost. Expressions that compare the ID with null, validate its prefix, or use its presence to enable another component could themselves become unknown.

The consequences spread beyond untidy console output. OpenTofu currently requires some decisions, including those involving count, for_each, and enabled, to be resolvable during planning. Unknown data can therefore block a configuration even when a human understands the future value’s constraints. Machine-readable plans pose another problem: a policy engine encountering an unknown field must either permit a change it cannot fully evaluate or reject a change that might be compliant.

This is the boundary OpenTofu 1.13 tries to sharpen. The engine still will not invent remote API results. Instead, module authors can preserve facts they know about those results so that subsequent expressions have more information to work with.

convert carries type information through the plan

The new convert function allows an author to declare the expected type of an expression whose structure cannot otherwise be predicted. The project’s example starts with dynamic JSON. Passing an unknown string to jsondecode normally yields a result with an unknown type as well as an unknown value. If the producer’s contract guarantees a particular schema, convert can describe it as an object with fields such as a string ID and a map of tags.

The decoded value does not magically become known. OpenTofu still cannot display the future ID or tags. But it now knows that accessing the ID will produce a string and that the tags field is a string map. That distinction can allow type checking, expression evaluation, and module composition to progress further during planning.

For platform engineers, the practical use case is a shared module sitting between a resource provider and many application stacks. Instead of forcing every consumer to rediscover or work around an imprecise result type, the shared module can encode the producer’s documented contract once. Consumers then receive a more useful interface without coupling themselves directly to provider quirks.

Teams should treat such conversions as interface declarations, not convenient casts. The declared object type belongs in the same review category as a module variable schema or an API definition. If the upstream producer changes shape, the conversion can fail. That is preferable to silent corruption, but it still moves a possible error from plan time to apply time unless the contract is tested elsewhere.

The assume... family records value constraints

Where convert describes type, the assume... functions add facts about the value itself. OpenTofu documents examples including a value being non-null or a string having a known prefix. Wrapping a future VPC ID with assumenotnull and assumestringprefix can let downstream logic prove that comparisons with null or an empty string are false. Validation expecting vpc- may also run during planning rather than waiting for apply.

This feature shifts the location of knowledge. Providers remain the ideal place to expose accurate plan-time semantics because they sit closest to the remote API. Yet provider schemas do not always capture every guarantee, and fixes may arrive slowly. A carefully maintained module can now bridge that gap for its consumers.

The word assume is an important warning label. If an author promises a vpc- prefix and the service returns subnet-, OpenTofu will fail the assumption during apply. The result is not a loophole around reality; it is a runtime assertion. Platform teams gain a more precise plan only by accepting responsibility for the assertion’s accuracy.

A safe adoption playbook for module owners

The best first targets are established contracts that are already documented and stable. Do not scatter assumptions through root configurations just to eliminate every unknown marker. Centralize them at the narrowest reusable boundary where the team owns the abstraction.

1. Inventory uncertainty that changes decisions

Start with unknowns that prevent a plan, blind a policy check, or cause downstream module interfaces to lose useful type information. Cosmetic unknowns that do not affect review or execution are poor candidates. This keeps the feature focused on measurable developer-experience and governance problems.

2. Identify the source of truth

For every proposed conversion or assumption, cite the provider documentation, vendor API contract, or an internally controlled schema that makes the claim reliable. A value that merely usually has a prefix is not equivalent to a guaranteed prefix. If no durable source exists, leave the value unknown and pursue a provider improvement instead.

3. Encapsulate the assertion

Place the new function in the shared module that already abstracts the resource or data source. Expose the refined result as a typed output. This avoids duplicating assumptions across repositories and creates one place to update when an upstream contract changes.

4. Test both plan and apply paths

A regression suite should prove that the refined information produces the intended plan-time result and that a representative apply satisfies the assertion. Include negative tests where feasible. If a module claims a JSON object shape, test missing and changed fields; if it claims a prefix, test the failure behavior operators will see.

5. Measure the governance effect

Compare policy results before and after adoption. The goal is not a plan with zero unknowns. It is fewer indeterminate decisions, fewer unnecessary policy denials, and earlier feedback on genuinely invalid configurations. Record which policies still need explicit unknown-value handling because no safe assumption is available.

What else changes the upgrade calculation

Teams evaluating 1.13 should separate the planning opportunity from several compatibility changes. The release removes WinRM connections for remote-exec and file provisioners after deprecating them in 1.12; configurations using type = "winrm" must move to OpenSSH for Windows before upgrading. OpenTofu on macOS now requires macOS 13 Ventura or later, and 1.13 is the final series with official 32-bit builds.

The base64gzip function also produces compressed data that is equivalent after decompression but not byte-for-byte identical to earlier output because of an upstream Go implementation change. If that output feeds a managed resource argument, a post-upgrade plan may propose an update or replacement. Review those plans closely; the project suggests temporary ignore_changes usage when a team cannot immediately accept the new representation.

Windows on ARM64 gains official OpenTofu packages, although third-party provider availability still depends on each provider maintainer. The release also previews experimental built-in linting and Symbol Libraries. Those experiments signal a broader interest in bringing reusable logic and earlier feedback closer to the core language, but they should not be treated as stable production dependencies yet.

More precise plans require stronger module governance

OpenTofu 1.13 does not eliminate plan-time uncertainty, nor should it. Cloud APIs remain dynamic, and a plan that fabricates certainty would be worse than one that clearly marks what it cannot know. The release instead creates a vocabulary for facts that exist outside the engine’s current inference.

That makes module ownership more important. A central platform team can use these functions to publish sharper contracts and help policy systems decide earlier. The same team can also create brittle applies if it encodes undocumented behavior as guaranteed truth. Code review should therefore ask two questions for each assumption: Who guarantees this fact? and How will we detect when it stops being true?

Used with that discipline, the new functions can improve infrastructure delivery in a subtle but meaningful way. They move feedback left without pretending the future is fully knowable. For DevOps teams, that is the real value of OpenTofu 1.13: not fewer question marks at any cost, but better evidence behind the answers a plan is willing to give.

Sources