September 18, 2024·4 min read·engineeringterraformplanscorrectnessproviders

Terraform 1.9.6 keeps known changes visible around unknown blocks

An unknown future block still has a known previous value. Terraform 1.9.6 preserves that distinction in plan output and repairs crashes involving replacement and null-to-unknown transitions.

An unknown value in a plan does not mean that nothing useful can be said about the change. Terraform may know the existing contents of a nested block while the provider cannot yet determine its replacement contents. A useful plan must show both facts. Terraform 1.9.6 repairs rendering that could lose the known portion of those transitions or crash while explaining them.

The 1.9.6 release, dated 18 September, contains three plan-renderer fixes. They cover complete changes inside unknown nested blocks, replacement-causing attributes within those blocks, and attributes changing from null to unknown. The tagged changelog groups all three under rendering, which is the correct scope for interpreting the release.

I rely on Terraform's declarative model because it makes infrastructure changes available for inspection before execution. That inspection depends on retaining partial information. A provider being unable to predict a future value should not suppress the values already known about the object being changed.

Unknown is a property of one side of the change

The main correction in pull request 35644 changes how nested block differences retain unknown status. Previously, a special-case unknown renderer could replace the normal treatment of the nested block. That simplified the representation but discarded detail that remained available about its previous contents.

The patch adds an unknown-status flag alongside the existing block collections. List, set and map blocks can retain their ordinary differences while also recording that their future value is unknown. Rendering can then display an unknown future block and the known previous blocks that are leaving the represented value.

This is useful because the two sides answer different review questions. The previous contents tell a reader what the current configuration has represented. The unknown marker tells the reader that the plan cannot enumerate the eventual result. Neither fact is an adequate substitute for the other.

The regression tests exercise a nested block called root_block_device in a test resource. Existing blocks contain known attributes, while the after-value is an unknown collection of blocks. Expected output retains the old attribute details and adds the future unknown marker. The test resource is an implementation fixture, so its field names should not be taken as evidence of a specific cloud provider defect.

The implementation also stops treating an unknown block with otherwise absent before and after values as a block that can be omitted. Missing concrete values and an explicitly unknown value are different states. Preserving that distinction prevents the renderer from silently removing information that the plan carries.

Replacement annotations need the right level

A nested attribute can be the reason a resource must be replaced. The renderer prints a replacement annotation to explain that relationship. Combining this information with an unknown block creates another choice about where the annotation belongs.

Pull request 35644 adds explicit controls that allow a renderer to require or suppress replacement annotations in its immediate children. The unknown renderer can display the replacement information at its own level and prevent the previous-value renderer from repeating it below. The patch's comments explain that these controls do not automatically cascade through every descendant.

This detail matters because rendering is a structured interpretation of the change, not a sequence of independent strings. A parent may know that its whole value forces replacement even when a child only knows how to display an old value. The output needs to associate the explanation with the relevant change while retaining the child's available detail.

The release notes also identify a crash in this combination. The correction therefore addresses both the completeness of a successfully rendered plan and the ability to render a valid change at all. It does not announce a different rule for deciding which provider changes require replacement.

An update may have no previous renderer

The second patch in pull request 35709 handles a related assumption. The unknown renderer previously treated a create action as the case with no previous value to print. An update can also lack a previous renderer when a provider had returned null for a computed attribute and now intends to recompute it.

The patch tests for the missing previous renderer as well as for creation. In either case, it can print the unknown marker directly. An update action alone is insufficient evidence that a printable prior value exists.

That same patch changes the action used when rendering unknown nested collections. The earlier implementation supplied a create action for the unknown marker. The correction uses the surrounding difference's action, so an update retains its update indication. This is a display distinction that helps a reader connect the unknown marker to the operation already described for the resource.

I consider these repairs meaningful even when the infrastructure operations themselves are unchanged. A reviewer needs the renderer to preserve what is known and explain what remains unresolved. Terraform 1.9.6 carries the old nested values through that explanation, keeps replacement annotations attached to the appropriate change and accepts an update whose previous computed value was null.