Resource targetting in Terraform - getting rid of that pesky blocker

Picture the following scenario:

  • A software project has reached the end of its lifecycle and you want to take it down, destroying all of the AWS infrastructure that you have provisioned using Terraform.
  • This set of resources includes an RDS instance.
  • You run terraform destroy and, after double checking the mass destruction plan, you push the red button.
  • Destruction begins and you observe in ecstasy as the logs go by.
  • Your hysterical evil laugh is abruptly interrupted by the following message:
    Error: DB Instance FinalSnapshotIdentifier is required when a final snapshot is required

To make things worse, the instance has prevented the destruction of all of the resources upon which it depended: the VPC and its subnets, security groups, etc.

Two things can happen now:

a) In a mix of cold sweat, extreme relief and silly laughter, you realize you actually needed a final snapshot... In this case you would add this to your aws_db_instance resource:
final_snapshot_identifier = "nyse_stonks_final_snapshot"

b) You don't care about the darn thing or you already have a proper backup, so instead:
skip_final_snapshot = true

No matter which path you choose, here comes the paradox: you now need to apply these changes in order to be able to destroy the remaining resources. But, of course, if you simply run terraform apply, you are going to create again all the resources you have already destroyed...

Good news is: you can actually apply changes to a single resource by first targetting it.

We can first check what resources are still there and identify the RDS instance by running terraform state list. Your list might look similar to this one:

aws_db_instance.nyse_stonks_db
aws_db_subnet_group.nyse_stonks_db
aws_security_group.nyse_stonks_db
module.vpc.aws_subnet.private[0]
module.vpc.aws_subnet.private[1]
module.vpc.aws_subnet.private[2]
module.vpc.aws_vpc.this[0]

Terraform allows us not only to save our plan into a file, but to target specific resources as well, so let's do both:
terraform plan -target aws_db_instance.nyse_stonks_db -out evil.plan

As expected, there will be a single change in this plan, e.g.

~ resource "aws_db_instance" "nyse_stonks_db" {
    ...
  ~ skip_final_snapshot                   = false -> true
    ...
}

Let's proceed to apply the plan by running:
terraform apply evil.plan

Don't forget to remove the plan unless you have a pattern under .gitignore, or it might accidentally end up under version control (it happened to a friend of mine).

And finally, if you are still 148% sure that you want to destroy everything:
terraform destroy -auto-approve

Observe with immense joy as you have finally managed to destroy all the things.

Another typical case where this approach can be useful is in case you forget to remove the deletion protection on EC2 instances, ALBs, etc.