Terraform 1.6.6 skips cleanup for tests that only read data
Verification modules can retain data-source state without owning infrastructure. Terraform 1.6.6 checks for managed resources before starting test destruction.
A test can retain state for something it observed without owning anything it should destroy. Terraform's test runner had treated those situations too similarly. A verification module containing only data sources could enter destruction after the infrastructure it read had already gone away, causing cleanup to fail. Terraform 1.6.6, released on December 13, skips that unnecessary work by checking whether a test state contains managed resources.
The distinction follows directly from the testing model introduced in Terraform 1.6. A test file can run the main configuration and alternate helper modules, with separate state for each configuration. A helper that reads the main configuration's objects has a reason to retain data-source results during the test. It has no corresponding remote objects to delete afterward.
I like verification modules because they can examine results through a provider's data sources without changing a production module solely to expose testing details. Their usefulness depends on keeping observation separate from ownership. The cleanup fix makes the runner respect that difference when a verification run has finished.
Separate test states have a destruction order
The Terraform 1.6 testing documentation explains that runs against the main configuration share one in-memory state. Each alternate module has its own state, shared by runs that execute that module. This lets setup and verification configurations participate in one test sequence while retaining distinct resource ownership.
At cleanup, Terraform destroys managed resources in the main configuration first. It then processes alternate modules in reverse run order. That order supports a setup module that creates a dependency needed by the main configuration. The dependency remains until the main resources have been removed.
A verification module can instead contain data sources reading the main configuration's objects. The documentation explicitly identifies that arrangement as acceptable because data sources do not need destruction. The old cleanup behavior nevertheless initiated work for the verification state even when there were no managed objects in it.
If that work tried to read an object already destroyed through the main state, the read could fail. The test's verification phase might have completed successfully while the later cleanup operation failed for an observation that required no deletion. This makes the failure confusing unless the reader distinguishes the two states and the order in which the runner visits them.
The runner checks managed-resource mode
The implementation patch replaces an insufficient emptiness test with an examination of resources across the modules in each relevant state. It looks for ManagedResourceMode. If it finds no managed resource, it skips cleanup for that state.
This test is more precise than asking whether the state contains anything. Data-source state can be nonempty while containing no managed infrastructure. A plan-only run can also leave no managed resources to destroy. Both cases now avoid the destruction path under the same criterion.
The patch retains an error path for genuinely inconsistent state. If managed resources remain but the runner cannot associate them with a run block, it reports an internal inconsistency. Skipping observational states therefore does not mean silently discarding every state the runner cannot process. The criterion is the absence of managed resources.
That implementation choice also explains the scope of the fix. A helper module that creates managed resources still needs cleanup. Adding one managed resource to an otherwise observational helper changes what that state owns. The runner must then execute destruction for the managed object, and the module's dependencies still need to respect cleanup order.
A regression test covers the full command outcome
The new fixture first applies the main configuration, then calls a verification module using an output from that run. A third run returns to the main configuration. The verification module declares an input ID and a data source that reads the corresponding test resource. It creates no managed object of its own.
The command-level regression expects all three runs to pass and a successful exit code. The surrounding test also checks the provider's remaining resource count. This is useful coverage because the defect belongs to cleanup after execution, not to parsing a test file or evaluating a single assertion.
The fixture uses Terraform's internal test provider. It demonstrates the lifecycle relationship without claiming that every real provider reports a missing object in the same way. The corrected runner avoids entering the unnecessary path, so the behavior no longer depends on how an observational data source responds after its target has disappeared.
I consider cleanup part of a test's result when the runner can create infrastructure. A successful assertion does not establish that all created objects have been removed. This patch improves that result by removing a destruction attempt where the state proves that the test owns no managed resources.
The test command documentation still describes cleanup as an attempt and reports resources that could not be destroyed. Those diagnostics remain relevant for states that contain managed objects. For data-source-only verification states, Terraform 1.6.6 can now finish without trying to clean up remote infrastructure that the verification module never created.