How to Destroy Resources from Terraform – step by step

Destroy resources terraform

In this post, we discuss how to manage the destruction of cloud infrastructure resources that are under Terraform management. By Terraform management we mean – resources that are created using Terraform IaC. The topics here cover what we mean by the destruction of such resources and how to overcome some common pitfalls.

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 who have adapted Terraform for infrastructure management usually have strict compliance with manual changes via the web console.

Under the hood

When Terraform configurations are applied (terraform apply), the relationship between real-world infrastructure objects and the IaC configuration is maintained in a state file. The state file is very crucial with respect to any terraform operations to be performed in the future – including destroy.

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.

Clearly, the state file is a source of truth for Terraform to perform any operation. If the state file is corrupted then Terraform can behave in unwarranted ways. If the state file has no mention of a certain resource – but the resource exists in the real world – then running terraform destroy will NOT destroy that resource.

Two ways to destroy

Continuing on the importance of the state file – there are essentially two ways to destroy resources using Terraform.

1) apply to destroy

Assuming we 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 the gaps in configuration and state files. It also checks if the real-world resources exist and generates a plan to remove any resources which are available in the real world, but not in the configuration.

This also means, if we manually delete a particular resource, the next destroy cycle would take the same into consideration and only attempt to delete the resource that exists.

Running the example code below (uncommented) will create 2 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 as well as in 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 to destroy

terraform destroy is a more common way to destroy resources managed by Terraform. The destroy command can be used to destroy a complete set of cloud infrastructure or a targeted resource.

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 below.

terraform destroy --target aws_instance.demo_vm_1

Terraform validates the state as well as its existence in 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.

However, 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.

Destroying protected resources

Let us recreate the infrastructure we had in 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 destory command again. Observe the output with error message.

As per the error message, we get 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 AWS console and disable the termination property for this EC2 instance, and run the destroy command again. This time it should succeed.

destroy terraform resources

At this moment, there is no way for Terraform to override this behavior. But this is for the good because it helps prevent the accidental deletion of critical resources.

Destroy all except a few

At times we face scenarios where we would like to recreate most of the infrastructure components, but keep 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 --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.

Running terraform state rm lets Terraform know that some of the resources are not managed by Terraform anymore. 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.

Conclusion

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

However, making use of this 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 on every stage – including the destroy phase – which can be very useful to keep track of things. It is FREE to get started with Spacelift.

destroy terraform resources

Share this post

twitter logo