FR
live

Terraform 1.16 allows import blocks inside modules and keeps secrets across plan and apply

On August 12, 2026 HashiCorp shipped Terraform 1.16 in release candidate, with two major changes: import blocks now work inside modules, and the new store block on terraform_data keeps ephemeral and sensitive values across plan and apply. Teams adopting Terraform over existing infrastructure gain the lever they were missing.

A row of identical steel server cabinets, one door left open with a single amber cable connector inside.

August 12, 2026. HashiCorp published Terraform 1.16.0-rc1 and, the same day, a 1.17.0-alpha. Two changes dominate the batch: import blocks now work inside modules, and the new store block on terraform_data keeps ephemeral and sensitive values across plan and apply.

For a team managing existing infrastructure, the first change is the one that has been missing for years: adopting Terraform without rewriting the whole history of your estate.

Import blocks enter the modules

Since Terraform 1.5, released in 2023, import blocks have let you bring an existing resource into state without destroying it. The syntax is simple:

hcl
import {
  to = aws_vpc.main
  id = "vpc-0abcd1234ef567890"
}

But they carried a structural limitation: they were only allowed at the root of the configuration. In practice, that forced an awkward dance — import the resource at root, then move it into a module by hand, migrating state along the way. For a modular codebase, it was the equivalent of moving house with your bare hands.

Terraform 1.16 lifts the restriction. An import block can now target a resource inside a module:

hcl
module "networking" {
  source = "./modules/networking"
}

import {
  to = module.networking.aws_vpc.main
  id = "vpc-0abcd1234ef567890"
}

The consequence for adoption is direct. Adopting Terraform over already-running infrastructure is always the same sequence: generate the configuration, then import every resource. Until now, root-only import forced you to break modularity at the exact moment you were trying to establish it. Now you write the configuration modular from the start, and the import blocks slot in where the resources actually live. That is a change of method, not a convenience.

To gauge how far this has come, remember the prior state. Before the import blocks of 1.5, importing was done by hand: the terraform import command on one side, writing the configuration block on the other, with a fragile identifier mapping and no automatic generation. The import blocks automated that generation, but confined it to the root. Moving a resource into a module afterward required a moved block and a risky, often manual state manipulation. 1.16 removes that last manual step — the one that produced the most mistakes.

terraform_data and the store block: secrets that survive the plan

The second major change fits in one block. terraform_data — the managed resource introduced in 1.5 to replace null_resource triggers — gains a store block that can hold ephemeral and sensitive values between plan and apply.

The problem it solves is concrete. Terraform splits into two phases: the plan, which computes a graph of actions, and the apply, which executes it. A value computed during the plan — a generated password, an ephemeral token, a derived secret — had to be recomputed at apply time, with all the divergence risk that implies. The store block lets you capture it at plan time and reuse it at apply time, without re-exposing it in plaintext in state.

It is the managed-resource counterpart of the new planned private data handling: Terraform 1.16 now preserves each provider’s private data between plan and apply, where it used to be lost between the two phases. For providers that rely on internal secrets or identifiers, that is a quiet but real hardening of the plan/apply cycle.

The concrete scenario that best illustrates the value is a generated password. A provider creates a database, and the administrator password is produced during the plan, stored in a secrets service, then referenced by another resource at apply time. Without a retention mechanism, that password had to be recomputed — and nothing guaranteed the two computations would agree. With the store block, the value captured at plan time is the one reused at apply time, which makes the cycle deterministic.

Finer triggers and computed nested blocks

Two further additions deserve a platform team’s attention, even if they are less flashy.

  • Resource action triggers now accept an on_failure mode — halt, taint, or continue. You can now decide precisely what happens to a resource whose trigger fails: stop the run, mark the resource for recreation, or keep going despite the failure. It is the level of control that was missing for sensitive pipelines.
  • Nested blocks can be used as computed values by providers. More flexibility for providers that model hierarchical structures, without forcing the user to declare them in full.

A concrete example of the trigger change: a pipeline that runs a health check after a resource is created can now taint that resource on failure, so the next run recreates it, instead of halting the entire apply or leaving a half-provisioned resource behind. That granularity used to require wrapping Terraform in external orchestration.

For completeness, Terraform is also now shipped as a pre-built binary for Linux s390x (zLinux), a platform still present in banking and insurance.

What remains experimental

The 1.17.0-alpha, published the same day, sketches the next direction with a feature to watch: deferred actions. Enabled by the -allow-deferral flag on plan, they allow count and for_each arguments whose value is unknown at plan time — for example, a list of instances supplied by another resource that has not been created yet.

It is experimental, restricted to alpha builds, and should not guide a migration. But it is a signal: HashiCorp is working toward a Terraform that can plan under uncertainty, where today an unknown value inside a count blocks the plan outright. Something to watch, not to adopt.

The adoption sequence, concretely

For a team moving existing infrastructure under Terraform with 1.16, the order of operations becomes markedly simpler.

  • Write the modular configuration the way it should be, without a temporary root structure. Resources live in the modules where they will belong long term.
  • Place an import block in each module, pointing at the real resource by its identifier. Configuration generation is no longer a manual prerequisite.
  • Run terraform plan to verify that each import matches the real state, then terraform apply to commit the import. No manual terraform state mv is required.
  • Remove the import blocks once state is in place, or keep them as explicit documentation of where each resource came from.

What disappears is the riskiest step: the manual state move between root and modules, a classic source of bad moved blocks and duplicated resources. 1.16 makes adoption as modular as the operations that follow it.

Verdict

Terraform 1.16 is not a rewrite, and that is precisely its strength. It closes two gaps that have annoyed teams daily for years, without breaking anything.

If you are adopting Terraform over existing infrastructure, lifting the restriction on import inside modules changes your method: write the modular configuration directly, and place the import blocks in the modules. You stop maintaining an artificial root structure just to import.

If your pipelines rely on secrets computed at plan time, the store block on terraform_data and planned private data are worth the upgrade on their own: they eliminate a class of plan/apply divergence on sensitive values.

For everything else, since 1.16.0-rc1 is a release candidate, caution applies: validate it on a non-critical environment before rolling it out, and keep an eye on deferred actions in 1.17 to plan ahead. The upgrade itself has no reason to wait for the stable release if modular import affects you.

References

The cyber brief, every Tuesday

The flaws that matter and the patches to apply, in a ten-minute read.

No spam. One-click unsubscribe.
read next

On the same topic

← Back to the feed

Type at least two characters.

navigate open esc dismiss