What does "Terraform will promise backwards compatibility with the state file" mean in practice?

I’m opening this for discussion here, because–as a GitHub issue–hashibot would probably auto-close it.

Background

I’m reading through the terraform docs here: https://www.terraform.io/docs/state/index.html#inspection-and-modification

And they state the following:

The CLI usage and output of the state commands is structured to be friendly for Unix tools such as grep, awk, etc. Additionally, the CLI insulates users from any format changes within the state itself. The Terraform project will keep the CLI working while the state format underneath it may shift.

Also:

The state is in JSON format and Terraform will promise backwards compatibility with the state file. The JSON format makes it easy to write tools around the state if you want or to modify it by hand in the case of a Terraform bug. The “version” field on the state contents allows us to transparently move the format forward if we make modifications.

I understand these docs were written a long time ago by Mitchell, but are they still an accurate reflection of the terraform ethos?

What do these docs mean in practice?

For example, if I’ve built automation around the terraform state that is stored remotely, can I count on it to maintain reverse compatibility when I upgrade to terraform 0.12 from 0.11.13? This is mostly a rhetorical question, because I know the format changed in some ways that weren’t reverse compatible.

For example, in https://github.com/hashicorp/terraform/pull/18223, this occurred:

Only the root module outputs are persisted, with child module outputs discarded.

Should the docs be updated?

It means that you can upgrade Terraform from 0.11 to 0.12 and it will upgrade the state for you. But once you’ve done that, you can’t go back.

So, you won’t need to destroy and re-create your entire environment with every minor release. Hopefully this will also work for the upgrade to 1.0 ( should we live long enough to actually see it :wink: ), provided that we’re upgrading from the latest 0.x

Thanks, I understand terraform doesn’t break its own access to terraform state with each version upgrade, but the docs seem to indicate I should also be able to build automation around consuming the raw terraform state without worrying about breaking changes. However, this isn’t true in practice.

I’m curious what someone like @apparentlymart has to say about it.

As you noticed, these are some old docs. I think what they are saying is broadly still accurate, but I can see that they are not really clear about what guarantees are being made here.

When it comes to Terraform’s own handling of the state, we do indeed maintain upgrade code that allows Terraform to read state snapshots created by older versions. Currently (in Terraform 0.12) that goes back to the very first JSON-based state snapshot format, but excludes the binary state snapshot format that very early versions of Terraform would produce.

Since our upgrade procedures call for moving forward one major version at a time and not skipping, these upgrade mechanisms will remain around long enough that anyone following that procedure should be able to walk forward from where they were to the latest version by jumping between the latest releases of each major version in between. In practice though, so far unless you are coming from a very ancient Terraform version that produced the binary format, current Terraform can recognize all of the historical JSON-based formats.

For external tools, the situation is a little trickier because naturally we can’t automatically upgrade such tools when a new release of Terraform comes out. Our current stance is that if you build such a tool then you should expect to need to make modifications to it over time to remain aligned with whatever is the current version of Terraform you are using, but that we won’t make gratuitous changes to the format.

We reserve the right to make new versions of the format because otherwise it would be largely impossible to add new features and capabilities to Terraform Core. However, we use the version number to communicate that so that tools consuming the format can reliably recognize when they receive data they are not equipped to process.

One thing not reflected in that documentation is that as of Terraform 0.12 there is a -json argument to terraform show which produces a different JSON representation of state that’s intended to be easier for outside consumers to parse. Since Terraform only produces this and never consumes it, there is more freedom in this separate format to make shims for backward-compatibility, to present data in a manner more consistent with the user model of Terraform rather than its internals, etc. If you want to build tooling around Terraform state and you don’t need to support Terraform v0.11 and earlier then I’d suggest using terraform state -json as the main integration point.

2 Likes

This was a super helpful reply. If I made a PR to add a reference to the terraform show documentation from the state documentation, would that be a welcome change?

Having read the content of the page you linked it seems like it really needs some general editing and attention because it’s got quite stale and isn’t reflective of the latest details. However, in the short term I’d be happy to add an explicit reference to terraform show somewhere in there, and then we can attack a more general rework of that page as a separate change. Thanks!

1 Like

Hello @apparentlymart,

We faced several times in the last month the situation that the provisioning failed because someone updated the backend state by using a newer 0.12 version. I would expect that Terraform could at least handle the state file in the same minor version. What are your recommendations here? I am working in an enterprise and sometimes new people are joining the team and do mistakes. Somehow it would be great to recover a state file. So lets assume the official CD environment is using version 0.12.12 but someone accidentially updated the remote state file by using a newer version. How can we handle this situation? I had always to delete the state file and all resources to recreate the environment. For now it is not that critical since we are not productive but if so the only chance is to use the same cli version.
It would be great to get some recommendations and maybe the Terraform team can think about to handle state files that have been created with the same minor version.
(By the way: Normally I am a friend of adding small features that do not break anything even in a patch version but in this case it is really hard for me to convince other colleagues not to stay month on an older patch version instead of frequently updating the environment.)

BR

MT

Hi @boebeng,

You can prevent the situation you described by including an exact Terraform version requirement as part of your configuration:

terraform {
  required_version = "0.12.12"
}

If you add this then a terraform plan or terraform apply will fail for anyone who doesn’t use exactly that version of Terraform. Upgrading to a newer Terraform version is then handled the same way as any other change to your configuration, such as via a pull request so the team can agree that it’s time to upgrade together.

In a collaborative enterprise environment, folks often run Terraform in automation to ensure not only that a single Terraform version is used in all cases but that everything else about the execution environment is consistent, thus avoiding the need for everyone to keep their local Terraform installations synchronized.

If you already have an upgraded state and do not want to continue using the newer version, you can try to tell Terraform that the state was created by an earlier version to see if it will be accepted. This will not always work, but if your alternative was to delete the state and start from new anyway then it doesn’t hurt to try:

  • Inform the rest of your team that you will be doing maintenence on Terraform and that nobody else should run it for the duration of these steps.
  • Run terraform state pull >tmp.tfstate to obtain the latest state snapshot to the tmp.tfstate file.
  • Edit tmp.tfstate in a text editor and change the "terraform_version" property to the earlier version you want to use.
  • Find the "serial" property and increment it by at least one to indicate that this is a new state snapshot.
  • Run terraform state push tmp.tfstate to submit the edited snapshot as a new snapshot.
  • Run terraform plan to see if it succeeds.

At the time of writing there have not been any significant changes to the state snapshot format during the 0.12.x line, so if you are switching from 0.12.21 or earlier to any earlier 0.12.x version then the above procedure should work, as long as provider versions have not also changed. (The data about individual resources is decided by the providers, so downgrading to a prior provider version may make the state data for a resource from that provider unreadable to the older version.)