hashicorp / terraform

Terraform enables you to safely and predictably create, change, and improve infrastructure. It is a source-available tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.
https://www.terraform.io/
Other
42.29k stars 9.48k forks source link

prevent_destroy should let you succeed #3874

Open ketzacoatl opened 8 years ago

ketzacoatl commented 8 years ago

Call me crazy, but I'm willing to call the current implementation of prevent_destroy a bug. Here is why: The current implementation of this flag prevents you from using it for 1/2 the use case.

The net result is more frustration when trying to get Terraform to succeed instead of destroying your resources.. prevent_destroy adds to the frustration more than alleviating it.

prevent_destroy is for these two primary use cases, right? 1) you don't want this resource to be deleted, and you want to see errors when TF tries to do that 2) you don't want this resource to be deleted, and you don't want to hear a peep out of TF - TF should skip over its usual prerogative to rm -rf on change.

I see no reason why TF must return an error when using prevent_destroy for the second use case, and in doing so, TF is completely ignoring my utterly clear directive to let me get work done. As a user, I end up feeling as though TF is wasting my time because I am focused on simple end goals which I am unable to attain while I spin my wheels begging TF to create more resources without destroying what exists.

You might say the user should update their plan to not be in conflict, and I would agree that is what you want to do in most cases.. but, honestly, that is not always the right solution for the situation at hand when using a tool like TF for the real-world. I believe in empowering users, and the current implementation of this flag prevents sensible use of the tool.

crw commented 2 years ago

Mentioning https://github.com/hashicorp/terraform/issues/18367 as related to this issue, per discussion with @apparentlymart.

CKarlslund commented 2 years ago

I ran into this problem just like everybody else. Is there a solution yet?

mercuriete commented 2 years ago

@CKarlslund I think It was answered before but that is what i came up in my company.

The important assets that we dont want to delete are in another project or folder with their own remote state. And you can use those objects with datasources.

In my case I have a virtual Network being shared for multiple environments. So in my global folder/repo I put the VPC and for example the EKS cluster shared with all environments. After that I reference those objects using data and I can create multiple environments and I can destroy them but keeping the VPC and the EKS cluster. I know maybe is not your use case but I hope you can came up with your solution. Cheers :)

XReyRobert-IBM commented 2 years ago

In case you run into this, you can also get rid of the state for the ressource your don't want to/can't destroy...
In my case I had dns record I could create/update but not remove (server would not allow it).

What I used to solve it:

tf state rm dns_a_record_set.dnslocal["tf-OC-SupportBind9"]

Tomasz-Kluczkowski commented 2 years ago

heh this also bit me today, ideally if we need to protect then fine, but I would then also like another flag -skip-destroy, which would work on individual resource. It would not be touched during destroy, full responsibility of the programmer to deal with the consequences of that (i.e. it remains in state and exists somewhere). as @ofbeaton mentions.

hadim commented 1 year ago

Any news here? At least an optional arg to tf destroy would be great. A legit use case is running tf in a CI where you obviously don't want the CI to fail while tf destroy has worked as excepted BUT you still want the CI to fail if tf destroy is failing for something else than not destroying a resource with a prevent_destroy flag.

tal-ayalon commented 1 year ago

What is the status of this PR?

francescomucio commented 1 year ago

another plus one, for a terraform destroy able to ignore the prevent_destroy = true. This has been requested many years ago.

While I appreciate the sentiment and the idea of keeping two separated stacks (one for the permanent and one for the more mutable resources), sometimes you need just a quick solution and a flag is good enough

FranksRobins commented 1 year ago

Any news on this request? TF is still throwing an error when using prevent_destroy. As for the comments about adding a flag, even if that would help when using TF in the command line, relying on flags or needing to do some extra work to inspect the plan and/or explicitly exclude a resource would make this complicated when using TF as a service in the case of Consul-Terraform-Sync.

TF should output that a resource was not destroyed because of the prevent_destroy configuration and continue its course without crashing with an exit code of 1 as not destroying a resource that was configured to not be destroyed is exactly the expected behaviour and not an error. There is no point in adding flags to tell TF to work as it is already explicitly configured. Flags should be added at best if you DO want TF to crash with an error if prevent_destroy is in its config or force destroy the protected resources. TF should run normally without any flags and simply not destroy those resources, that way CTS can run in an autonomous way.

mr-scripting commented 1 year ago

So I'm coming from another issue where terraform doesn't allow modules to use prevent_destroy (https://github.com/hashicorp/terraform/issues/18367 , https://github.com/hashicorp/terraform/issues/28913, https://github.com/hashicorp/terraform/issues/30937, https://github.com/hashicorp/terraform/issues/3116). So I've duplicated the resource in the module, added the lifecycle block and conditional deployment using count. When I finished and executed terraform destroy I hit another road-block. It's frustrating to say the least.

halradaideh commented 1 year ago

any updates here?

tonyszhang commented 1 year ago

This is related to https://github.com/hashicorp/terraform/issues/15485, any updates? Even AWS CloudFormation allows the "abandon" behaviour.

ArbitraryCritter commented 1 year ago

I think discussing this thing in the context of the prevent_destroy feature is a complete misnomer.

"prevent_destroy" is essentially a defensive coding tool. You're hinting terraform that if a plan ever results in the replacement of that specific resource, that is an error.

This allows a user to relax a bit more when verifying terraform plans (and makes full automation safer).

What everyone including myself is requesting is something completely different. It's for the cases where you want terraform to just forget a resource instead of deleting it. For resources that are fine to leak and/or resources that may be used in multiple places, but cannot easily be de-duplicated, but are easily re-created.

My specific use-case for this are dns certificate validation dns records. I have multiple pieces of code, requesting separate certificates that are using the same dns records for validation. (This is for AWS ACM). Currently terraform will delete my validation dns records if I delete one of the certificates, which will cause the other certificates to eventually expire (after lots of event/email notices) because they cannot auto-renew without these records. For these dns records I would prefer that terraform create them if they're missing (which is supported) and just forget the state when it deletes them. Yes, it causes a bit of a mess, but it's better than the alternative.

By the way, I'm very much against all the suggested terraform flags being thrown around for overriding prevent_destroy behavior per-run and I think you should clearly separate this feature from the otherwise excellent "prevent_destroy = true" feature.

I would suggest: lifecycle { on_delete: forget }

Or if you want to extend the prevent_destroy naming, then: lifecycle { prevent_destroy: forget_state }

lorengordon commented 1 year ago

There is also the config-driven state rm feature request, which may address some of this use case: https://github.com/hashicorp/terraform/issues/33226

For these dns records I would prefer that terraform create them if they're missing (which is supported) and just forget the state when it deletes them.

coogle commented 1 year ago

Just wanted to chime in with another example here...

Creating an RDS server in AWS, then creating various logical databases within that server along with users/roles/permissions/etc... The way I am doing this at the moment is via an SSH tunnel as the RDS server is in a VPC with no public access.

When you delete the RDS you have deleted all the logical databases within it. That being said, I've found destroying doesn't necessarily work well and I have to state rm a bunch of these logical operations before a destroy works.

alethenorio commented 1 year ago

I've been thinking about a use case which I think would need this to solve so I thought I'd bring this up.

Google Cloud has a project shutdown API.

If one wanted to build a Terraform module for defining a Google Cloud project along with a few resources inside, it would be nice to be able to remove an instance of that module from a Terraform configuration and rather than have all resources immediately deleted, just remove them from the Terraform state and call the Shutdown API (through either a local_exec provisioner for example). This would allow the project and its resources to be safely restored if a mistaken was detected (for example, a database inside that project contains data that was not properly backed up).

Very much look forward to something like this.

evbo commented 1 year ago

Adding to @ArbitraryCritter use-case:

Some resources are a pain to destroy. For instance, AWS secrets_manager has a 7 day retention policy. So I just want to create these resources and let everything but them get destroyed as needed.

Could we add something like ignore_destroy? It simply skips destroying it, maybe with only a warning?

Example:

resource "aws_secretsmanager_secret_version" "managed" {
  secret_id = aws_secretsmanager_secret.managed.id
  secret_string = "I manually update this"

  lifecycle {
    ignore_changes = [
      secret_string
    ]
    # prevent_destroy = true 
    # just simply ignore this resource during terraform destroy phase...
    ignore_destroy = true # no error will be emitted by this
  }
}

side note: manually removing these from state (so that destroy no longer applies to them) is not desirable as I may want to later on configure alerts or adjust configuration, requiring they be in state. Plus there are a ton of them, it would become a chore and they are deeply nested in modules...

jorgetolentinog commented 1 year ago

For S3 based on the idea of ignore_destroy of @evbo. One approach would be to create the resource with null_resource and retrieve it with data.

resource "random_id" "bucket_suffix" {
  byte_length = 4
}

locals {
  bucket_name = "${var.name}-storage-${random_id.bucket_suffix.hex}"
}

resource "null_resource" "create_s3_bucket" {
  provisioner "local-exec" {
    command = <<EOT
      aws s3api create-bucket --bucket ${local.bucket_name} --region us-east-1
    EOT
  }
}

data "aws_s3_bucket" "storage" {
  bucket = local.bucket_name
}

resource "aws_s3_bucket_versioning" "storage" {
  bucket = data.aws_s3_bucket.storage.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_ownership_controls" "storage" {
  bucket = data.aws_s3_bucket.storage.id
  rule {
    object_ownership = "BucketOwnerPreferred"
  }
}

resource "aws_s3_bucket_acl" "storage" {
  depends_on = [aws_s3_bucket_ownership_controls.storage]
  bucket     = data.aws_s3_bucket.storage.id
  acl        = "private"
}

My use case is: Prevent terraform from destroying my s3 bucket and allow me to manually delete it to avoid accidentally losing files.

duxbuse commented 12 months ago

My use case is on GCP.

We create a project_metadata_item enable-oslogin=true We also have an org policy enforcing this value to be true. However if we ever try to destroy the whole project terraform attempts to first destroy the metadata_item which is caught by the org policy constraint and prevented essentially blocking me from deleting the project.

Ideally I would ignore trying to delete this specific resource. And once the whole project is deleted the metadata item would be gone too so there is no need to manually delete this item.

I also am unable to state rm the resource as then terraform will attempt to recreate the item which will also fail since the value is already present

voycey commented 10 months ago

How are we still here 8 years later? GCP is a prime example - it makes resource "google_project_service" completely useless

evbo commented 10 months ago

For what it's worth, here's ultimately the work around I've settled on:

Infrastructure that is created once and seldom/never destroyed I put inside a separate module. Then, it gets referenced in other non-permanent modules using a data.terraform_remote_state resource.

In this way, it stays permanently created while the rest of the infrastructure can be destroyed freely. It alleviates the pain I was having but causes a code stink and awkward layout of my infrastructure based on how likely something is to ever be destroyed rather than its functionality.

pascalro-croesus commented 10 months ago

Another use case: AWS Backup resource. You may want to create the vault and associated resources with Terraform, but you really don't want to delete a vault and its dependencies when you call a destroy. Some of those dependencies, like IAM roles and DB option groups (for RDS backups) don't have the deletion protection flag in AWS, so the lifecycle is the only option to make sure they are not deleted in a destroy.

obouchta commented 9 months ago

Another use case: Creating Cloud9 membership is allowed by AWS. But removing the user from the membership list is only allowed if it's done by the owner. The affected resource is : aws_cloud9_environment_membership.

We need to skip the destroy if the user is no longer allowed (not the best practice but useful for my case)

Multox commented 7 months ago

+1 Would like to skip destroy on a particular module because of the AWS 7 day retention policy on keys. The intent being that the resource is created if it's needed, but never destroyed.

sbocinec commented 7 months ago

Terraform 1.7 introduced "Config-driven remove" https://www.hashicorp.com/blog/terraform-1-7-adds-test-mocking-and-config-driven-remove that allows to remove a resource from state without actually destroying it. I think this can address majority of the use cases mentioned here.

evbo commented 7 months ago

@sbocinec removing a resource from state means you can no longer modify it.

Having some kind of ignore_destroy lifecycle would still allow all stateful operations to be performed except destroy, which is what the majority of use cases in this issue are asking for. For instance, see top voted suggestions: https://github.com/hashicorp/terraform/issues/3874#issuecomment-161715361 https://github.com/hashicorp/terraform/issues/3874#issuecomment-270296400

sbocinec commented 7 months ago

@evbo you are right, that's perfectly valid case . I saw a bunch of people mentioning doing state rm manually, and I was quick to make conclusion about the "majority", I should have used "some" :bow:

alethenorio commented 7 months ago

Is it possible to define a new resource alongside a remove block so the resource is created on apply and immediately removed from the state (but not destroyed)?

anthosz commented 7 months ago

Is it possible to define a new resource alongside a remove block so the resource is created on apply and immediately removed from the state (but not destroyed)?

If it's possible, the issue is that it will retry to recreate it indefinitely until you remove it from tf configuration :/

alethenorio commented 7 months ago

@anthosz How so? If I understand the docs, the removed block is meant to be added alongside the definition of a resource so as long as the order of applying them is done correctly (create resource first followed by remove) it should make no difference whether they are both applied from the plan or different ones. Or did I miss something? If terraform attempts to create the resource on the next apply because it does not exist in its state even though a removed block exists for that resource then I am not sure I understand what kind of use cases removed is meant to solve.

g13013 commented 7 months ago

At least introduce a new setting skip_destroy!

camilo-s commented 7 months ago

Here's a use case this feature would be great for We're using Terraform to deploy data product infrastructure on-demand (data mesh). This includes a repository for data products to source control their code (with azuredevops_git_repository). Now, we intend to manage the full data product lifecycle with Terraform, from onboarding to decommissioning. In the latter case though, we'd like to keep the repository for auditing purposes, so Terraform should just forget it rather than destroying it.

BalintGeri commented 6 months ago

HOW is this not implemented yet? WHY is this feature missing from terraform? Please implement it, there are many use case for it, just like @camilo-s mentioned and I am now struggling because of this vital feature missing. :( Very disappointing. :(

hadim commented 6 months ago

Maybe devs at https://github.com/opentofu/opentofu will be keen to support that feature?

evbo commented 6 months ago

@hadim Thank you, I completely missed the news that Hashicorp changed their license this past August and that Terraform is no longer open source.

Anyone reading this: Definitely look towards OpenTF (Open Tofu) for any new features as they are promising complete compatibility and to remain open source forever:

https://opentofu.org/

unknownconstant commented 5 months ago

This issue is blocking me trying to use bind9 for DNS.

I want terraform to manage the NS records for the zone, but when I come to delete the zone, bind9 can't delete the last NS record as it'd leave an invalid config. I need terraform to not delete this record, so that the zone itself can be removed.

Not sure how to do this at present but I've already wasted hours trying to find a workaround.

unknownconstant commented 5 months ago

I've tried the removed block that came out in January but it's pretty weak in this context.

The NS record is defined in a module. I can't use the remove block because it errors saying the the NS record is still defined (which it should be for other configs using the module).

francescomucio commented 5 months ago

The solution we came up with is to build our Terraform setup in layers, each with his own state.

The outer layer stays when the inner is destroyed. A few example:

Outputs and loops are your friends in this setup.

Happy to add more details if anyone is interested.

On Sun, Mar 17, 2024, 13:14 unknownconstant @.***> wrote:

I've tried the removed block that came out in January but it's pretty weak in this context.

The NS record is defined in a module. I can't use the remove block because it errors saying the the NS record is still defined (which it should be for other configs using the module).

— Reply to this email directly, view it on GitHub https://github.com/hashicorp/terraform/issues/3874#issuecomment-2002435036, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXKTXZJLFQM3XERTJRAVCTYYWCKHAVCNFSM4BUDL332U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TEMBQGI2DGNJQGM3A . You are receiving this because you commented.Message ID: @.***>

mbklein commented 5 months ago

All the solutions in this thread so far violate one of my long-held principles of development: “Never let the workaround become the work.”

alethenorio commented 5 months ago

Is it possible to define a new resource alongside a remove block so the resource is created on apply and immediately removed from the state (but not destroyed)?

Just as an FYI, I tried this out and one cannot add a config-driven remove resource without first removing the Terraform resource it refers to from the config. A bit unfortunate :/

willianferrari commented 4 months ago

The solution we came up with is to build our Terraform setup in layers, each with his own state. The outer layer stays when the inner is destroyed. A few example: - with Snowflake we have multiple environments in the same account. Account level object are created in the Account Layer (for example human users or spending monitors), env layers (dev/test/prod) take care of the env specific objects. - for the Data Product repos. Handle the repo creation/deletion at the external layer, other operations in the specific Data Product layer. Outputs and loops are your friends in this setup. Happy to add more details if anyone is interested. On Sun, Mar 17, 2024, 13:14 unknownconstant @.> wrote: I've tried the removed block that came out in January but it's pretty weak in this context. The NS record is defined in a module. I can't use the remove block because it errors saying the the NS record is still defined (which it should be for other configs using the module). — Reply to this email directly, view it on GitHub <#3874 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXKTXZJLFQM3XERTJRAVCTYYWCKHAVCNFSM4BUDL332U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TEMBQGI2DGNJQGM3A . You are receiving this because you commented.Message ID: @.>

Hello,

I need to use the same volume for several workspaces.

Can I use this same approach, my problem is that when I delete a workspace I need to keep the volume.

simaotwx commented 3 months ago

It's quite frustrating to see the state this is in. The more I use terraform the more frustrating it is to use, especially in conjunction with Azure.

arnaudfroidmont commented 3 months ago

I concur with almost everything that was said above. In our case, we would like to not delete a FileSystem that was created by terraform so that we make sure we don't erase any data. I would love for the destroy to let the rest of the ressource be destroyed and only the ones that were tagged prevent_destroy to be left alone.

gxo13 commented 2 months ago

I support this feature. We have permissions to create a resource but not to destroy, so we need a way to prevent_destroy but without the pipeline failing.

I can't believe this has been open since 2015.

barnuri-cp commented 2 months ago

any update about this feature ?

shad-zam commented 3 weeks ago

any update about this feature ?

BenJackGill commented 2 weeks ago

This was raised in 2015 with 100+ comments and multiple concrete use cases provided, but still no resolution.

Do we give up hope?

voycey commented 2 weeks ago

The removed block covers some eventualities but not all of them unfortunately