Terraform preserves state serials when applying refreshed plans
Terraform 0.9.10 fixes state serial handling that can fail Atlas consistency checks. Refresh-only changes reveal why a plan's state snapshot and the backend's current metadata need separate treatment.
An apply can change Terraform's state without changing a remote resource. Refresh can discover an attribute changed outside Terraform, and a data source can return updated information. Those observations belong in state even when the resource-change portion of a plan has no work to perform. State version metadata must still remain consistent when Terraform persists them.
Terraform 0.9.10 addresses that case. Its tagged changelog describes a hotfix for state index handling when changes consist only of data-source updates or resource drift discovered during refresh. The release record dates the release to June 29, despite the changelog's unrevised "not yet released" heading.
I value Terraform's common description of infrastructure APIs, but that description also needs a dependable record of what those APIs returned. Refresh-only changes are real state changes even when they do not correspond to a create or update request.
Persisting a plan can advance its state metadata
The implementation explanation in pull request 15423 identifies a more specific sequence than the changelog's short summary. When applying a plan, Terraform initially persists a copy of the plan's state. Persisting it can increment the serial number. The apply operation then used the state embedded in the plan, which could still have the earlier serial.
The problem is therefore not simply that a counter never increments. The stored state can already have a newer counter, and constructing the apply context can select the older copy. That puts execution metadata behind the persistence step that has just occurred.
The fix changes how a plan builds its execution context. If the caller supplies state, Terraform uses that state after checking its logical equivalence with the plan's state. If the caller does not supply state, the plan's embedded state remains the fallback. The code explicitly permits the supplied state to have a newer serial.
This preserves information from backend persistence without allowing an unrelated state payload to replace the plan's expected input. The check and the selection address different concerns. Equivalence protects the relationship to the plan, while retaining the supplied state avoids regressing its serial metadata.
Serial and content answer different questions
The state implementation in this release documents the serial as a number incremented when an operation modifies the state file. Terraform uses it to detect potentially conflicting updates. The same structure contains a lineage value, assigned when a new blank state is created, that determines whether two states' serials can meaningfully be compared.
A serial is consequently not a count of cloud resources changed by the current apply. An operation can advance the persisted state without making such a change. Equating those two quantities would miss the refresh-only case described in the release notes.
Logical equality is also different from identical metadata. The state equality implementation compares the format version and module contents. Its comparison does not require serial numbers to match. That makes it possible for the apply-context fix to accept logically equivalent state with a serial updated by persistence.
For example, a plan can hold a state payload with serial N, and persisting that payload can produce an equivalent state with serial N+1. Using the latter preserves the backend's bookkeeping. Falling back to N after that write loses information about the version already stored.
Why Atlas makes the error visible
The changelog says the issue is significant only for the Atlas backend because Atlas verifies consistent use of state serials on the server. That is a statement about where this defect produces the reported consistency failure. It does not establish that serial metadata has no meaning elsewhere.
A backend that checks incoming serials can refuse a write whose metadata conflicts with the state it has already accepted. In this case, the client can create that conflict through its own sequence of operations. No second operator is required to explain why the plan's embedded serial became older than the persisted copy.
The repair keeps those client operations consistent. It does not disable the server's validation or treat every saved snapshot as current. Terraform still checks equivalence before it uses the caller-provided state in the plan context.
I prefer preserving this distinction because a successful apply should remain meaningful even when its only durable result is a corrected observation. The absence of resource mutations cannot justify discarding state metadata.
The saved plan contains more than a diff
The versioned plan reference says planning performs refresh unless disabled and can save the result using -out. It also explains that a saved plan encodes configuration, state, the diff and variables. That embedded state is why the selection error can arise when constructing an apply context.
A saved plan is therefore a specific execution artifact with its own snapshot. Backend persistence can update metadata around that snapshot before execution begins. Terraform 0.9.10 accounts for that sequence by retaining equivalent state supplied by the backend path. The result preserves the plan's intended contents while carrying forward the serial associated with the state already persisted.