49

I have a Terraform 0.11 project with 30-40 different resources. I would like to delete all of them except a few - and those few are logically related to each other.

I was looking for something close to terraform destroy --except=resource-id but that of course doesn't exist.

Is there a way to achieve that without too much scripting (Terraform admins have various OSs)? Would using modules make that process easier perhaps?

rath
  • 3,655
  • 1
  • 40
  • 53
  • 2
    The answer is to not structure your code base this way. Things in the same directory should all be creatable or destroyable at the same time. Part applying changes is an escape hatch, not something to rely on. – ydaetskcoR Mar 20 '19 at 17:17
  • @ydaetskcoR This makes sense. I would accept this answer, if you can write it up – rath Mar 20 '19 at 17:20
  • 1
    It feels like it might be a duplicate, let me have a dig around, otherwise I'll write it up as an answer here. – ydaetskcoR Mar 20 '19 at 17:42
  • 1
    what about eips? say for example you had these whitelisted and don't want to destroy them because you need a change control elsewhere, you set Terraform to create and mark wit prevent_destroy, all prevent_destroy does here is to fail your terraform destroy command. You could create the eip outside terraform and just manage the association, but both feel like this could be handled better – krystan honour Aug 19 '19 at 13:55
  • None of these answers is satisfying. I have a set of related infrastructure AND I want a couple items to remain after destroy AND they should remain in TF's state. Use cases: some S3 buckets (I recently "lost" an S3 bucket name for a transient test environment due to namesquatting); and ACM certs which require manual validation with non-AWS DNS provider. For environments that come and go (i.e. test environments taken down overnight to reduce cost). I don't think this is unreasonable. – Chuck Batson Feb 09 '23 at 07:20

7 Answers7

94

There is no --except feature in terraform destroy command currently. If you really want to do that, and you know what you are doing, here is the workaround.

# list all resources
terraform state list

# remove that resource you don't want to destroy
# you can add more to be excluded if required
terraform state rm <resource_to_be_deleted> 

# destroy the whole stack except above excluded resource(s)
terraform destroy 

So why do these commands work for your idea?

The state (*.tfstate) is used by Terraform to map real world resources to your configuration, keep track of metadata.

terraform state rm cleans a record (resource) from the state file (*.tfstate) only. It doesn't destroy the real resource.

Since you don't run terraform apply or terraform refresh, after terraform state rm, terraform doesn't know the excluded resource was created at all.

When you run terraform destroy, it has no detail about that excluded resource’s state and will not destroy it. It will destroy the rest.

By the way, later you still have chance to import the resource back with terraform import command if you want.

Patrick Decat
  • 618
  • 5
  • 11
BMW
  • 42,880
  • 12
  • 99
  • 116
  • 7
    This now orphans the state of the resource that's not deleted so Terraform won't manage it any more which isn't really what the OP wanted. You _could_ import it again after deleting everything else but in general partial apply and destroy is a bad idea. – ydaetskcoR Mar 21 '19 at 08:24
  • 5
    This is a decent workaround for my situation. Not ideal, as ydaetskcor says, but good enough. – rath Mar 21 '19 at 10:02
  • its working fine – Vinoth Ramamoorthy Feb 03 '22 at 12:20
  • what would the `import` be? `terraform import ` ? – Panda May 11 '23 at 11:51
  • Import command would be different to each resource, you need take reference from the terraform document. – BMW Jun 23 '23 at 05:53
12

Targetting each resource (while skipping over the data resources) except the one you want is probably the only way atm:

#! /bin/bash

while read -r resource; do
    terraform destroy -target="$resource"
done < <(terraform state list | grep -vE "^data\." | grep -vE "dont_remove|also_important")
Olli K
  • 1,720
  • 1
  • 16
  • 17
  • If you're using a module the "^data\." pattern needs to be "\.data\." – lost Mar 23 '23 at 11:19
  • And (obviously, but I missed it!) its not use TF's dependency graph, so this probably won't work except in simple cases. – lost Mar 23 '23 at 11:52
6

To delete all your stack except some resources, you need to:

  1. Create a backup of your current state : terraform state pull > bkup.json
  2. List all your resources : terraform state list
  3. "rm" the resources that you want to keep : terraform state rm "aws_myresource". It will not delete the resource in your cloud, but only from the terraform perspective.
  4. Destroy your stack : it will be delete from the cloud all your resources except the ones you "rm" just before. terraform destroy
  5. You now have an empty state (0 resources). Save it in order to be able to edit it : terraform state pull > state-to-edit.json
  6. Edit the state-to-edit.json by adding resources object {} from the bkup.json in the .resources[] block. You also need to increment by 1 the .serial value
  7. Push the modified state : terraform state push ./state-to-edit.json
  8. You can verify with terraform plan or terraform state list that you still have the wanted resources & deleted all the others.
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
Rémi F.
  • 61
  • 1
  • 3
  • 1
    This is the only answer that does what the OP asked. I'm just sad this is necessary. See also this nearly four year old [issue](https://github.com/hashicorp/terraform/issues/23547). – Chuck Batson Feb 09 '23 at 07:54
  • 1
    Thank you @ChuckBatson, I added it in the issue for visibility purpose. Hopes it helps people. – Rémi F. Feb 09 '23 at 16:15
  • I wonder why terraform was created with so much hate in one's soul. – Lethargos Aug 08 '23 at 13:26
1

I have a bit of a different work around. The resources I do not want to delete with "terraform destroy" I create as "null_resource" using a provisioner with CLI. You can still use your variables in terraform as well.

for example (Create a resource group, but it is persistent due to null_resource)

resource "null_resource" "backend-config" {
        provisioner "local-exec" {
        command     = <<EOT
    az group create --location ${var.Location} --name ${var.Resource_group_name} --tags 'LineOfBusiness=${var.Lob}' 'Region=${var.Region}' 'Purpose="Terraform-Primary-Resource-Group-${var.Lob}'
    EOT
        interpreter = ["Powershell", "-Command"]
      }
    }

Now if you destroy the resources using terraform destroy. Any null_resource will remain intact.

Pwd9000
  • 132
  • 8
1

BMW's answer is best if you just need to destroy things, and not change the code. That is, if you intend to bring those resources back up at some later time.

If you just want to remove the resources, the proper solution is to remove the Terraform definitions you wish to destroy, and then do a normal terraform apply. Just as you would for adding a resource.

(An old question, I know, but I was surprised to not see anyone mention alternative. The OP's use case seems to be covered by the other answers, so this answer is for others stumbling in here.)

Bendik
  • 1,097
  • 1
  • 8
  • 27
0
  1. List resources:

    terraform state list
    
      data.terraform_remote_state.rg
      azurerm_postgresql_database.postgresql_database
      azurerm_postgresql_server.postgresql_server
    
  2. Remove resource

    terraform destroy -target azurerm_postgresql_database.postgresql_database -auto-approve
    
-2

terraform destroy -target RESOURCE_TYPE.NAME -target RESOURCE_TYPE2.NAME

kensai
  • 943
  • 9
  • 16
  • can you comment whats wrong on my answer? – kensai Dec 30 '21 at 14:02
  • 6
    question is asking how to destroy all resources but one, you are showing (without explaining) how to destroy single resources. If someone follows blindly your answer he/she would destroy just the resource he/she wants to keep – Marco Necci May 13 '22 at 14:50