Going to AWS re:Invent 2024?

➡️ Book a meeting with Spacelift

Terraform

Destroy Command – How to Destroy Resources from Terraform

Destroy resources terraform

Terraform at Scale

Manage your infrastructure as it grows with a flexible, robust workflow, drift detection and reconciliation, and policies for security and compliance.

Book a demo

Cloud resources created using Terraform are maintained using Infrastructure as Code. Any changes, commissioning, and decommissioning of resources are supposed to be handled using IaC. Teams that have adapted Terraform for infrastructure management usually have strict compliance with manual changes via the web console.

In this post, you will learn how to manage the destruction of cloud infrastructure resources under Terraform management (resources created using Terraform IaC) and how to overcome some common pitfalls.

What we will cover:

  1. What is the Terraform destroy command?
  2. Terraform destroy command options
  3. How to destroy resources from Terraform
  4. Why did Terraform destroy fail?
  5. Best practices for destroying Terraform resources

What is the Terraform destroy command?

Terraform destroy command is a common way to destroy resources managed by Terraform. It terminates a complete set of cloud infrastructure or a targeted resource by deleting infrastructure resources present in the state file. When the destroy command is executed, Terraform first validates the information contained in the state file by cross-checking with cloud provider APIs. Internally it builds a dependency graph to identify the sequence in which the resources are to be destroyed.

Before running terraform destroy you should review the changes and verify the execution plan using terraform plan -destroy. Once executed, terraform destroy permanently deletes the targeted resources.

The state file is Terraform’s source of truth when performing any operation. If the state file is corrupted, Terraform can behave in unwarranted ways. If the state file does not mention a certain resource—but the resource exists in the real world—then running terraform destroy will NOT destroy that resource.

When to use the destroy command?

The terraform destroy command should be used mostly when you want to tear down your infrastructure resources. However, there are cases where you will use the target option to destroy only what you no longer need, or that is not working properly.

Terraform destroy command options

You can use terraform destroy command with different options, for example:

1. terraform destroy -auto-approve

By default, whenever you run a terraform destroy, you will first see a destroy plan and have to approve it manually. Taking advantage of the “-auto-approve” option lets you destroy all resources without the need of any manual approval. This can be useful in CI/CD pipelines, especially for ephemeral environments that you want to destroy on a schedule, or whenever an event occurs.

2. terraform destroy -refresh=false

By using the -refresh=false option with terraform destroy, you ensure that resources are destroyed based on the information Terraform has in the state file, without refreshing the state prior to running this operation.

3. terraform destroy -lock and -lock-timeout

The -lock option controls whether or not Terraform will lock the state during the operation it provides (in our case destroy) and the -lock-timeout refers to the duration of how long Terraform should wait to acquire the lock which can be useful in automated environments where you may expect brief delays in acquiring a lock and do not want an immediate failure if the state is locked.

How to destroy resources from Terraform

Running terraform destroy is not the only way to delete the infrastructure resources. Continuing on the importance of the state file – there are essentially two ways to destroy resources using Terraform.

1. Destroy resources with Terraform plan

Assuming you have a set of resources deployed using Terraform configurations, to destroy one or all of the resources in this configuration, simply remove/comment out the resource blocks and run terraform plan to validate the desired action and then apply.

During its validation phase, Terraform identifies gaps in configuration and state files. It also checks if real-world resources exist and generates a plan to remove any resources that are available in the real world but not in the configuration.

This also means that if we manually delete a particular resource, the next destroy cycle will consider it and only attempt to delete the existing resource.

Example: How to destroy an AWS instance using Terraform

The example code below (uncommented) will create two EC2 instances – demo_vm_1 and demo_vm_2. To destroy one of the EC2 instances, comment out the corresponding resource block in the code – demo_vm_2.

resource "aws_instance" "demo_vm_1" {
 ami           = "ami-07df274a488ca9195"
 instance_type = "t2.micro"
 
 tags = {
   name = "Demo VM One"
 }
}
 
/*resource "aws_instance" "demo_vm_2" {
 ami                     = "ami-07df274a488ca9195"
 instance_type           = "t2.micro"
 
 tags = {
   name = "Demo VM Two"
 }
}*/

Run terraform plan and observe the output.

aws_instance.demo_vm_2: Refreshing state... [id=i-0b63433033e61d818]
aws_instance.demo_vm_1: Refreshing state... [id=i-0b46c3280f7d305df]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # aws_instance.demo_vm_2 will be destroyed
  - resource "aws_instance" "demo_vm_2" {
      - ami                                  = "ami-07df274a488ca9195" -> null
      - arn                                  = "arn:aws:ec2:eu-central-1:532199187081:instance/i-0b63433033e61d818" -> null
      - associate_public_ip_address          = true -> null
      - availability_zone                    = "eu-central-1b" -> null
.
.
.
.
}
    }

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

Terraform identifies the absence of demo_vm_2 in the configuration, but notices that it exists in the state file and the real world. Thus, it generates a plan to destroy that EC2 instance. Running terraform apply now would cause the destruction of this EC2 instance.

2. Destroy resources using terraform destroy command

The terraform destroy command is a more common option to terminate resources managed by your current Terraform project.

Example: Destroy a specific Terraform resource using the --target flag

To destroy a specific EC2 instance (demo_vm_1), the --target argument can be supplied to the destroy command with the resource path to identify the correct resource as shown below.

terraform destroy --target aws_instance.demo_vm_1

Terraform validates the state and its existence in the AWS EC2 console and triggers the deletion of the specified EC2 instance.

.
.
.
.
- throughput            = 0 -> null
          - volume_id             = "vol-040c21ea887ddd779" -> null
          - volume_size           = 8 -> null
          - volume_type           = "gp2" -> null
        }
    }

Plan: 0 to add, 0 to change, 1 to destroy.
│ Warning: Resource targeting is in effect
│ You are creating a plan with the -target option, which means that the result of this plan may not represent all of the changes requested by the current
│ configuration.
│ The -target option is not for routine use, and is provided only for exceptional situations such as recovering from errors or mistakes, or when
│ Terraform specifically suggests to use it as part of an error message.

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

aws_instance.demo_vm_1: Destroying... [id=i-0435cfff2020ce5fd]
aws_instance.demo_vm_1: Still destroying... [id=i-0435cfff2020ce5fd, 10s elapsed]
aws_instance.demo_vm_1: Still destroying... [id=i-0435cfff2020ce5fd, 20s elapsed]
aws_instance.demo_vm_1: Still destroying... [id=i-0435cfff2020ce5fd, 30s elapsed]
aws_instance.demo_vm_1: Still destroying... [id=i-0435cfff2020ce5fd, 40s elapsed]
aws_instance.demo_vm_1: Destruction complete after 40s
│ Warning: Applied changes may be incomplete
│ The plan was created with the -target option in effect, so some changes requested in the configuration may have been ignored and the output values may
│ not be fully updated. Run the following command to verify that no other changes are pending:
│     terraform plan
│ Note that the -target option is not suitable for routine use, and is provided only for exceptional situations such as recovering from errors or
│ mistakes, or when Terraform specifically suggests to use it as part of an error message.

Destroy complete! Resources: 1 destroyed.

Validate the same by logging into the AWS Console.

Example: Destroy all resources using terraform destroy command

To destroy both EC2 instances simply run terraform destroy without --target argument. Terraform validates the state and existence of the EC2 instances and triggers the resource deletion. Try running terraform destroy with our example now, and observe the output.

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

Changes to Outputs:
  - instance_id_1 = "i-0195745b98b21bec7" -> null

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

aws_instance.demo_vm_2: Destroying... [id=i-0cdf2d51800624a22]
aws_instance.demo_vm_1: Destroying... [id=i-0195745b98b21bec7]
aws_instance.demo_vm_2: Still destroying... [id=i-0cdf2d51800624a22, 10s elapsed]
aws_instance.demo_vm_1: Still destroying... [id=i-0195745b98b21bec7, 10s elapsed]
aws_instance.demo_vm_2: Still destroying... [id=i-0cdf2d51800624a22, 20s elapsed]
aws_instance.demo_vm_1: Still destroying... [id=i-0195745b98b21bec7, 20s elapsed]
aws_instance.demo_vm_1: Still destroying... [id=i-0195745b98b21bec7, 30s elapsed]
aws_instance.demo_vm_2: Still destroying... [id=i-0cdf2d51800624a22, 30s elapsed]
aws_instance.demo_vm_2: Still destroying... [id=i-0cdf2d51800624a22, 40s elapsed]
aws_instance.demo_vm_1: Still destroying... [id=i-0195745b98b21bec7, 40s elapsed]
aws_instance.demo_vm_1: Destruction complete after 41s
aws_instance.demo_vm_2: Still destroying... [id=i-0cdf2d51800624a22, 50s elapsed]
aws_instance.demo_vm_2: Still destroying... [id=i-0cdf2d51800624a22, 1m0s elapsed]
aws_instance.demo_vm_2: Destruction complete after 1m7s

Destroy complete! Resources: 2 destroyed.

3. Destroy protected resources

Let us recreate the infrastructure from the previous example, this time with a minor difference. Add disable_api_termination = true attribute to demo_vm_2. Try to destroy the same using terraform destroy command again. Observe the output with error message.

As per the error message, we got OperationNotPermitted error for one of the EC2 instances. The Terraform configuration responsible for creating this EC2 instance has disable_api_termination set to true. This is additional protection provided by the cloud provider to avoid accidental deletion of the EC2 instance. Because Terraform operations utilize cloud provider APIs to apply or destroy configurations, it cannot do anything disallowed by the provider’s API.

In this case it means that Terraform is not be able to delete demo_vm_2. However, it was able to delete demo_vm_1 for which this property was not set at all. This tells us that it defaults to false.

Log in to the AWS console, disable the termination property for this EC2 instance, and run the destroy command again. This time, it should succeed.

destroy terraform resources

At this time, Terraform cannot override this behavior. However, this is a positive because it helps prevent any accidental deletion of critical resources.

4. Destroy all except a few

Sometimes, you will face scenarios where you would like to recreate most of the infrastructure components but keep a few critical ones in place. Currently, there is no way to specify the resources NOT to be destroyed using Terraform.

However, since the state plays a major role in Terraform’s decision making – an apparent workaround would be as follows:

  1. Remove the resource from the state file using terraform state rm command.
  2. Run terraform destroy – without Terraform --target argument.
  3. After successful destruction, import the target resources back in the state file. This post describes how to import cloud resources under Terraform.

Terraform destroy vs. terraform state rm

Unlike terraform destroy, terraform state rm doesn’t actually delete resources from the infrastructure. Running terraform state rm lets Terraform know that some of the resources are no longer managed by Terraform. When destroy is executed, Terraform does not touch those resources. When the cleanup of other resources is done, and the remaining resources are imported back, Terraform then manages the resources again.

Why did Terraform destroy fail?

There are many reasons why a Terraform destroy operation may fail.

RBAC Terraform destroy will fail if the role/account you are using doesn’t have enough permissions to delete the resources.
Locked state When the state is locked, you won’t be able to run operations against your state, and you will need to wait for it to be available.
Network issues If you have any connectivity issues, your delete requests may not arrive properly.
Syntax errors If you have made changes to your code that have syntax errors, terraform destroy won’t run until you fix them.
Resource dependencies This was mostly happening in earlier versions of Terraform, but if you create explicit dependencies using “depends_on”, you would need to sometimes run terraform destroy multiple times to make it work.

Best practices for destroying Terraform resources

Terraform destroy is a very powerful command, but it will remove all infrastructure resources based on your Terraform configuration and state file, so it should be approached with caution.

Below is a list of best practices you should follow:

1. Review changes before destroying

You should first check the execution plan before destroying your resources to ensure that everything will happen exactly as you want.

2. Take advantage of version control

Ensure your code is kept in a VCS system because this will allow you to track changes to the codebase and roll back to previous configurations if the need arises.

3. Use apply to destroy

If you want to specifically remove a resource, it is easier to delete the resource from your configuration (if you won’t ever need it) and applying the code, rather than doing a targeted destroy. In any case, check the execution plan before making the change.

4. Use remote state and state locking

It is fundamental to use a remote state for collaboration, and state locking is helpful to ensure that no concurrent operations happen in the state. Without locking, multiple engineers can do operations at the same time on the state file, resulting in state corruption and errors overall.

5. Ensure least privileged access

To keep your infrastructure safe, you should always ensure that only authorized users have permissions to destroy resources.

6. Use deletion protection

In case of production critical resources such as production databases, enable deletion protection to prevent accidental removal.

Key Points

Terraform destroy is a command that should be used wisely. It is not something that would be used on a daily basis in managing the production environment. 

However, making use of it in sub-production environments would be quite frequent as a lot of try-outs for proofs-of-concept require cleanup activities.

Spacelift lets you customize the entire infrastructure life-cycle management by providing the ability to run pre and post commands at every stage – including the destroy phase – which can be very useful in keeping track of things.

Spacelift destroying phase

We encourage you also to explore how Spacelift makes it easy to work with Terraform. If you need any help managing your Terraform infrastructure, building more complex workflows based on Terraform, and managing AWS credentials per run, instead of using a static pair on your local machine, Spacelift is a great tool for this. It supports Git workflows, policy as code, programmatic configuration, context sharing, drift detection, and many more great features. Create your free trial account or book a demo with one of our engineers.

Note: New versions of Terraform will be placed under the BUSL license, but everything created before version 1.5.x stays open-source. OpenTofu is an open-source version of Terraform that will expand on Terraform’s existing concepts and offerings. It is a viable alternative to HashiCorp’s Terraform, being forked from Terraform version 1.5.6.

Manage Terraform Better and Faster

If you are struggling with Terraform automation and management, check out Spacelift. It helps you manage Terraform state, build more complex workflows, and adds several must-have capabilities for end-to-end infrastructure management.

Start free trial

The Practitioner’s Guide to Scaling Infrastructure as Code

Transform your IaC management to scale

securely, efficiently, and productively

into the future.

ebook global banner
Share your data and download the guide