Authors – All

If you have a few years of experience in the DevOps ecosystem, and you're interested in sharing that experience with the community, have a look at our Contribution Guidelines.

1. Introduction

Terraform state files store tracking information about our infrastructure’s configuration, making infrastructure management seamless. However, if we need to start things afresh, we can reset the state file.

In this tutorial, we discuss how to reset to terraform default tfstate.

2. Delete the State File

Deleting the state file and reinitializing the working directory is one way to reset our Terraform state. However, it isn’t the best option in most cases, because when we delete the state file, Terraform loses track of the existing infrastructure. Of course, this means that we can no longer manage said infrastructure with Terraform and will have to manage the infrastructure manually.

We can simply delete the state file using rm:

$ rm -rf terraform.tfstate

After deleting the state file, we’ll run terraform init to reinitialize the working directory with an empty state file:

$ terraform init

Initializing the backend...

...truncated...

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

3. Remove Bindings From the State File

This method involves removing resource instances from the infrastructure, and when this happens, Terraform will stop tracking them. Of course, that will leave us managing said resources instances manually.

To remove bindings from the state file, we’ll start by getting a list of the resources bound to the state using terraform state list:

$ terraform state list
module.ec2_provision.aws_default_route_table.triserver_rt
module.ec2_provision.aws_instance.triserver["1"]
module.ec2_provision.aws_instance.triserver["2"]
module.ec2_provision.aws_instance.triserver["3"]
module.ec2_provision.aws_internet_gateway.triserver_igw
...truncated...
module.ec2_provision.aws_security_group.triserver_sg
module.ec2_provision.aws_subnet.triserver_subnet["1"]
module.ec2_provision.aws_subnet.triserver_subnet["2"]
module.ec2_provision.aws_vpc.triserver_vpc

Now that we have a list of the resources, we can remove them from the state using terraform state rm [resource]. We’ll test this out by removing module.ec2_provision.aws_security_group.triserver_sg:

$ terraform state rm module.ec2_provision.aws_security_group.triserver_sg
Removed module.ec2_provision.aws_security_group.triserver_sg
Successfully removed 1 resource instance(s).

Normally, we use the terraform state rm command to remove bindings individually. But if we want to reset to Terraform default tfstate, removing the bindings one by one may be too much work. Instead, we could combine terraform state rm and terraform state list to remove all resources in a single command:

$ terraform state rm $(terraform state list)
Removed module.ec2_provision.aws_default_route_table.triserver_rt
Removed module.ec2_provision.aws_instance.triserver["1"]
Removed module.ec2_provision.aws_instance.triserver["2"]
Removed module.ec2_provision.aws_instance.triserver["3"]
Removed module.ec2_provision.aws_internet_gateway.triserver_igw
Removed module.ec2_provision.aws_key_pair.vagrant
Removed module.ec2_provision.aws_lb.triserver_alb
Removed module.ec2_provision.aws_lb_listener.triserver_lb_listener
Removed module.ec2_provision.aws_lb_target_group.triserver_tg
Removed module.ec2_provision.aws_lb_target_group_attachment.triserver_tg_attachment1["1"]
...truncated...
Removed module.ec2_provision.aws_subnet.triserver_subnet["1"]
Removed module.ec2_provision.aws_subnet.triserver_subnet["2"]
Removed module.ec2_provision.aws_vpc.triserver_vpc
Successfully removed 19 resource instance(s).

Once that’s done, we can reinitialize the working directory:

$ terraform init

Initializing the backend...

...truncated...

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

...truncated...

4. Revert to Previous Version

When we use remote state with versioning in Terraform, we can revert to previous states while retaining the option to return to the latest state. To do this, we’ll download our desired version of the state file to our local environment. Afterward, we’ll replace the current state file with the desired state file. Finally, we’ll apply the desired state, making it our current state.

Let’s demonstrate retrieving a state file using aws s3api:

$ aws s3api get-object --bucket baeldung-bucket --version-id [VERSION_ID] --key terraform.tfstate v1-terraform.tfstate
{
    "AcceptRanges": "bytes",
...truncated...
    "ContentType": "application/json",
    "ServerSideEncryption": "AES256",
    "Metadata": {}
}

The command above downloads a specific version of our state file from our S3 bucket to our local machine with the filename v1-terraform.tfstate.

Ordinarily, at this point, we can replace the current state with our desired state. However, if we do, we’ll get an error message:

$ terraform state push v1-terraform.tfstate
Failed to write state: cannot import state with serial 1 over newer state with serial 5

We received the error above because, in Terraform, we can’t write over the current state with a state that has a lower serial. Each version of a Terraform state has a serial number and the later versions always have higher serial numbers than earlier ones. Therefore, earlier versions cannot replace later versions without a change in their serial.

So, before replacing the current state, we’ll edit the serial in our desired state file version. Let’s cat the updated file:

$ cat v1-terraform.tfstate
{
  "version": 4,
  "terraform_version": "1.8.3",
  "serial": 5,
...truncated...
}

After that, we’ll update the current state with our edited state file using terraform state push:

$ terraform state push v1-terraform.tfstate

Before we apply the desired state file, we can confirm its differences with the current state using terraform plan:

$ terraform plan
module.ec2_provision.aws_key_pair.vagrant: Refreshing state... [id=vagrant]
module.ec2_provision.aws_vpc.triserver_vpc: Refreshing state... [id=vpc-028653dad25ca70cd]
module.ec2_provision.aws_subnet.triserver_subnet["1"]: Refreshing state... [id=subnet-099f24e1baa1d784b]
...truncated...
    }

Plan: 9 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + name_servers = (known after apply)

──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run
"terraform apply" now.

If satisfied with the incoming state, we’ll run terraform apply to revert to that state:

$ terraform apply -auto-approve

5. Conclusion

In this article, we discussed how to reset a Terraform state by deleting the state file, removing resource instances, and reverting to earlier state file versions.

When we reset to the default state by removing resource instances or deleting the state file, Terraform will lose track of the infrastructure. However, when we revert to an earlier version of a versioned remote state, we can always go back to a state where Terraform tracks the infrastructure.

Authors – All

If you have a few years of experience in the DevOps ecosystem, and you're interested in sharing that experience with the community, have a look at our Contribution Guidelines.