# Request for testing: \`removed\` block

**URL:** <https://discuss.hashicorp.com/t/request-for-testing-removed-block/60511>\
**Category:** Terraform\
**Created:** [December 1, 2023, 11:57am UTC](https://discuss.hashicorp.com/t/request-for-testing-removed-block/60511 "2023-12-01T11:57:21Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![kmoe](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/kmoe/32/774_2.png) [@kmoe](https://discuss.hashicorp.com/u/kmoe)\
**Post date:** [December 1, 2023, 11:57am UTC](https://discuss.hashicorp.com/t/request-for-testing-removed-block/60511/1 "2023-12-01T11:57:21Z")

</div>

The most recent alpha release of Terraform, v1.7.0-alpha20231130, contains a new feature we’d like your feedback on: configuration-driven remove with the `removed` block.

This new block follows in the footsteps of `moved`, providing an alternative to `terraform state rm` that can remove multiple resources and modules from state at once, as a safe, plannable operation.

```auto
# tells Terraform that the aws_instance.example resource, removed from configuration,
# should not be destroyed, only removed from state
removed {
  from = aws_instance.example

  lifecycle {
    destroy = false
  }
}

```

This feature preview in the most recent alpha release, so it **not** ready for production.

You can download it via the [releases website](https://releases.hashicorp.com/terraform/1.7.0-alpha20231130/) (see [CHANGELOG](https://github.com/hashicorp/terraform/releases/tag/v1.7.0-alpha20231130)).

An early draft can be found [here](https://github.com/hashicorp/terraform/pull/34339). I’d love your feedback on the docs as well!

Please respond to this topic with any feedback or questions. Potential bugs can also be filed within our Github [Issues](https://github.com/hashicorp/terraform/issues).

---

<div class="post-metadata">

**Author:** ![benjamin.m.lykins](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/benjamin.m.lykins/32/16334_2.png) [@benjamin.m.lykins](https://discuss.hashicorp.com/u/benjamin.m.lykins)\
**Post date:** [December 7, 2023, 2:21pm UTC](https://discuss.hashicorp.com/t/request-for-testing-removed-block/60511/2 "2023-12-07T14:21:42Z")

</div>

Looks good tested on my end with a simple local resource.

Created a simple module which creates one file.

```auto
terraform {
  required_providers {
    local = {
      source = "hashicorp/local"
      version = "2.4.0"
    }
  }
}

resource "local_file" "file" {
  filename = "${path.module}/destination"
  source = "${path.module}/source"
} 

```

Ran an init + apply.

```auto
local_file.swamp: Refreshing state... [id=5291767cb169477c0a03c53439ec85c8dc3aaf46]

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

Terraform will perform the following actions:

  # local_file.file will be created
  + resource "local_file" "file" {
      + content_base64sha256 = (known after apply)
      + content_base64sha512 = (known after apply)
      + content_md5 = (known after apply)
      + content_sha1 = (known after apply)
      + content_sha256 = (known after apply)
      + content_sha512 = (known after apply)
      + directory_permission = "0777"
      + file_permission = "0777"
      + filename = "./destination"
      + id = (known after apply)
      + source = "./source"
    }

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

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

local_file.file: Creating...
local_file.file: Creation complete after 0s [id=5291767cb169477c0a03c53439ec85c8dc3aaf46]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

```

Updated the file to remove the local\_file resource and added removed block. I’m guessing lifecycle + destroy is a new argument.

```auto
terraform {
  required_providers {
    local = {
      source = "hashicorp/local"
      version = "2.4.0"
    }
  }
}

# resource "local_file" "file" {
# filename = "${path.module}/destination"
# source = "${path.module}/source"
# }

removed {
  from = local_file.file
  lifecycle {
    destroy = false
  }
}

```

Ran an apply and got the proper feedback.

```auto
local_file.file: Refreshing state... [id=5291767cb169477c0a03c53439ec85c8dc3aaf46]

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

Terraform will perform the following actions:

 # local_file.file will no longer be managed by Terraform, but will not be destroyed
 # (destroy = false is set in the configuration)
 . resource "local_file" "file" {
      - content_base64sha256 = "N+FgMd5soTK/NPAhWpf9OwVevfnr6DOVhD8CTislQ5s=" -> null
      - content_base64sha512 = "/uaHo4s7YXkWFCLPOobf2izKhUbZtBKmlE3fnEfjM5kFzN4MAC4e8IK+Yl6QlbuqBPnpiI36dGCgtCX/7lVBCw==" -> null
      - content_md5 = "f352cac61c815fe9d44770e65345367d" -> null
      - content_sha1 = "5291767cb169477c0a03c53439ec85c8dc3aaf46" -> null
      - content_sha256 = "37e16031de6ca132bf34f0215a97fd3b055ebdf9ebe83395843f024e2b25439b" -> null
      - content_sha512 = "fee687a38b3b6179161422cf3a86dfda2cca8546d9b412a6944ddf9c47e3339905ccde0c002e1ef082be625e9095bbaa04f9e9888dfa7460a0b425ffee55410b" -> null
      - directory_permission = "0777" -> null
      - file_permission = "0777" -> null
      - filename = "./destination" -> null
      - id = "5291767cb169477c0a03c53439ec85c8dc3aaf46" -> null
      - source = "./source" -> null
    }

Plan: 0 to add, 0 to change, 0 to destroy.
╷
│ Warning: Some objects will no longer be managed by Terraform
│ 
│ If you apply this plan, Terraform will discard its tracking information for the following objects, but it will not delete them:
│ - local_file.file
│ 
│ After applying this plan, Terraform will no longer manage these objects. You will need to import them into Terraform to manage them again.
╵

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

```

Very simple test, but might try more robust workings. Do other meta arguments work?  
-provider, for\_each, etc?

Update :

Did something a bit funkier and got a good result.  
Tested creating an ECR resource via the AWS provider.  
But then added a deleted block and an import block to the code.  
Added a awscc\_ecr\_repository resource to see if I could migrate the resource from one provider to the next.

So config was :

```auto
terraform {
  required_providers {
    awscc = {
      source = "hashicorp/awscc"
      version = "0.66.0"
    }
    aws = {
      source = "hashicorp/aws"
      version = "5.29.0"
    }
  }
}

```

Had an initial ecr\_repository :

```auto
resource "aws_ecr_repository" "this" {
  name = "delete-test"
  image_tag_mutability = "MUTABLE"

  image_scanning_configuration {
    scan_on_push = true
  }
}

```

After creating, I commented out the old aws\_ecr\_repository, added a removed block, added an import block, and then added an awscc\_ecr\_repository resource.

```auto
# resource "aws_ecr_repository" "this" {
# name = "delete-test"
# image_tag_mutability = "MUTABLE"

# image_scanning_configuration {
# scan_on_push = true
# }
# }

removed {
  from = aws_ecr_repository.this
  lifecycle {
    destroy = false
  }
}

import {
  id = "delete-test"
  to = awscc_ecr_repository.this
}

resource "awscc_ecr_repository" "this" {
  repository_name = "delete-test"
  image_tag_mutability = "MUTABLE"
  image_scanning_configuration = {
    scan_on_push = true
  }
  lifecycle {
    ignore_changes = [empty_on_delete, lifecycle_policy, repository_policy_text, tags]
  }
}

```

Ran smoothly on the state removal and import.

```auto
awscc_ecr_repository.this: Preparing import... [id=delete-test]
aws_ecr_repository.this: Refreshing state... [id=delete-test]
awscc_ecr_repository.this: Refreshing state... [id=delete-test]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

 # aws_ecr_repository.this will no longer be managed by Terraform, but will not be destroyed
 # (destroy = false is set in the configuration)
 . resource "aws_ecr_repository" "this" {
      - arn = "arn:aws:ecr:us-east-1:634211456147:repository/delete-test" -> null
      - id = "delete-test" -> null
      - image_tag_mutability = "MUTABLE" -> null
      - name = "delete-test" -> null
      - registry_id = "634211456147" -> null
      - repository_url = "634211456147.dkr.ecr.us-east-1.amazonaws.com/delete-test" -> null
      - tags = {} -> null
      - tags_all = {} -> null

      - encryption_configuration {
          - encryption_type = "AES256" -> null
        }

      - image_scanning_configuration {
          - scan_on_push = true -> null
        }
    }

  # awscc_ecr_repository.this will be updated in-place
  # (imported from "delete-test")
  ~ resource "awscc_ecr_repository" "this" {
        arn = "arn:aws:ecr:us-east-1:634211456147:repository/delete-test"
      + empty_on_delete = (known after apply)
        encryption_configuration = {
            encryption_type = "AES256"
        }
        id = "delete-test"
        image_scanning_configuration = {
            scan_on_push = true
        }
        image_tag_mutability = "MUTABLE"
      + lifecycle_policy = (known after apply)
        repository_name = "delete-test"
      + repository_policy_text = (known after apply)
        repository_uri = "634211456147.dkr.ecr.us-east-1.amazonaws.com/delete-test"
      + tags = (known after apply)
    }

Plan: 1 to import, 0 to add, 1 to change, 0 to destroy.
╷
│ Warning: Some objects will no longer be managed by Terraform
│ 
│ If you apply this plan, Terraform will discard its tracking information for the following objects, but it will not delete them:
│ - aws_ecr_repository.this
│ 
│ After applying this plan, Terraform will no longer manage these objects. You will need to import them into Terraform to manage them again.
╵

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

awscc_ecr_repository.this: Importing... [id=delete-test]
awscc_ecr_repository.this: Import complete [id=delete-test]

```

Hope this all helps.

Wrote up a quick post on it as well :

> **[Terraform Removed Block](https://dev.to/lykins/terraform-removed-block-4j27)**
>
> Decided to test out the new state block with looks to be released in v1.7. At this point I can...

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [December 7, 2023, 4:25pm UTC](https://discuss.hashicorp.com/t/request-for-testing-removed-block/60511/3 "2023-12-07T16:25:18Z")

</div>

Thanks for testing, @benjamin.m.lykins!

One thing that occurs to me looking at the output you saw is that we might consider showing the inside of the block describing that it will “no longer be managed by Terraform” as if it were a no-op change, so that it would not suggest that all of the arguments of the resource instance are being set to `null`.

The thing we’re trying to communicate here is that Terraform is not going to change this _remote object_ in any way, but the current presentation is a little ambiguous about that, making it look perhaps a little too much like the rendering for an object being destroyed. 🤔

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [December 7, 2023, 4:31pm UTC](https://discuss.hashicorp.com/t/request-for-testing-removed-block/60511/4 "2023-12-07T16:31:44Z")

</div>

Another separate note:

The use of a pair of `removed` and `import` for migrating an object from one provider to another is neat, but I remember that we’ve also separately designed a way for providers to hand off ownership of objects between different resource types using a `moved` block and some special provider logic to translate between the two schemas:

```auto
# Not implemented yet; just an example
moved {
  from = aws_ecr_repository.this
  to = awscc_ecr_repository.this
}

```

Although it’s a neat workaround to use forgetting and importing together for this, I hope that a real solution like I showed above will follow in the near future, because that will both allow Terraform to better communicate what’s going on and avoid the lossiness that often arises through importing, because the remote system cannot preserve Terraform metadata about an object, only its actual data.

That’s a different feature from removing though, and I guess it doesn’t hurt to use removing and importing together as a workaround in the meantime.

---

<div class="post-metadata">

**Author:** ![benjamin.m.lykins](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/benjamin.m.lykins/32/16334_2.png) [@benjamin.m.lykins](https://discuss.hashicorp.com/u/benjamin.m.lykins)\
**Post date:** [December 8, 2023, 3:34pm UTC](https://discuss.hashicorp.com/t/request-for-testing-removed-block/60511/5 "2023-12-08T15:34:03Z")

</div>

You are welcome, @apparentlymart! I always enjoy seeing new features in Terraform/Terraform Cloud.

Here are a couple of additional items for feedback:

1. I agree that the use of null can be confusing, as removing a resource from the code looks very similar.

Perhaps something similar to how “moved” appears in the CLI could be implemented?

```auto
local_file.file: Refreshing state... [id=da39a3ee5e6b4b0d3255bfef95601890afd80709]

Terraform will perform the following actions:

// I updated the following two comments. 
  # local_file.file will no longer be managed by Terraform, but will not be destroyed
  # (destroy = false is set in the configuration)
    resource "local_file" "file" {
        id = "da39a3ee5e6b4b0d3255bfef95601890afd80709"
        # (10 unchanged attributes hidden)
    }

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

```

1. I did one more test this morning using `destroy = true`.

If sticking to the current output, there are differences between `destroy = true` and `destroy = false`.

When run with `destroy = true`:

```auto
removed {
  from = local_file.file
  lifecycle {
    destroy = true
  }
}

```

Here is what it currently says :

```auto
  # local_file.file will be destroyed
  # (because local_file.file is not in configuration)
  - resource "local_file" "file" {
      - content_base64sha256 = "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=" -> null
      - content_base64sha512 = "z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==" -> null
      - content_md5 = "d41d8cd98f00b204e9800998ecf8427e" -> null
      - content_sha1 = "da39a3ee5e6b4b0d3255bfef95601890afd80709" -> null
      - content_sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" -> null
      - content_sha512 = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" -> null
      - directory_permission = "0777" -> null
      - file_permission = "0777" -> null
      - filename = "./destination" -> null
      - id = "da39a3ee5e6b4b0d3255bfef95601890afd80709" -> null
      - source = "./source" -> null
    }

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

```

In comparison, this is what it looked like when it was removed, but not destroyed. (i.e. destroy = false).

```auto
 # local_file.file will no longer be managed by Terraform, but will not be destroyed
 # (destroy = false is set in the configuration)

```

Perhaps update it for resources that will be removed and destroyed, something like this:

```auto
//From
 # local_file.file will be destroyed
 # (because local_file.file is not in configuration)

//To 
# local_file.file will no longer be managed by Terraform, and will be destroyed. 
# (destroy = true is set in the configuration)

```

1. Since import, move, and soon-to-be-removed will be blocks, would it make sense to add a section in the plan and apply to describe these specific state changes?

For example :

```auto
Plan: 0 to add, 0 to change, 1 to destroy.
State: 1 to import, 1 to move, 1 to remove

```

Sorry if this is long winded, but hopefully it is decent feedback.

---

<div class="post-metadata">

**Author:** ![kmoe](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/kmoe/32/774_2.png) [@kmoe](https://discuss.hashicorp.com/u/kmoe)\
**Post date:** [December 12, 2023, 7:36pm UTC](https://discuss.hashicorp.com/t/request-for-testing-removed-block/60511/6 "2023-12-12T19:36:31Z")

</div>

Thanks for testing, @benjamin.m.lykins. I like how you put it in your blog article - “a holy trinity of state blocks”. This was exactly the intent of this feature.

I agree with you and @apparentlymart on rendering remove-only changes as no-ops. It also makes the plan significantly shorter, as the “unchanged” attributes are collapsed. This should be updated in the next beta (1.7.0-beta2 tomorrow).

As for explicitly reminding the user that `destroy = true is set in the configuration` and that is why their resource will be deleted, this is a nice detail that I’d like to include. As you note in your article, the `removed` block is currently present in a very basic form and more arguments will be added in a later iteration. At the moment, a `removed` block with `destroy = true` is functionally a no-op if you’ve already removed the resource from configuration, since that’s already enough for Terraform to know it needs to delete it.

Regarding the “resource change comment”:

> ` # local_file.file will be destroyed`

vs

> `# local_file.file will no longer be managed by Terraform, and will be destroyed.`

It’s always true that when Terraform destroys a resource, it also is no longer managing it. I do quite like the additional explicitness here, but I also wouldn’t want to imply that Terraform is doing anything out of the ordinary when it destroys a resource with `destroy = true`.

---

<div class="post-metadata">

**Author:** ![kmoe](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/kmoe/32/774_2.png) [@kmoe](https://discuss.hashicorp.com/u/kmoe)\
**Post date:** [December 12, 2023, 7:42pm UTC](https://discuss.hashicorp.com/t/request-for-testing-removed-block/60511/7 "2023-12-12T19:42:14Z")

</div>

> 1. Since import, move, and soon-to-be-removed will be blocks, would it make sense to add a section in the plan and apply to describe these specific state changes?
> 
> For example :
> 
> ```auto
> Plan: 0 to add, 0 to change, 1 to destroy.
> State: 1 to import, 1 to move, 1 to remove
> 
> ```

For legacy integration reasons changing this text is costly. We also try not to rely on users’ understanding of Terraform state and “state actions” unless they’re intentionally using advanced features such as `terraform state rm`. On the other hand I do think this nicely presents the distinction between planned actions that will result in provider requests during apply, and those which require no further requests and will just update the state. Interested to see if others have feedback on this output.

> hopefully it is decent feedback.

Absolutely, and thanks again!

---

<div class="post-metadata">

**Author:** ![jbeal\_ramp](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/jbeal_ramp/32/39700_2.png) [@jbeal\_ramp](https://discuss.hashicorp.com/u/jbeal_ramp)\
**Post date:** [January 9, 2024, 10:20pm UTC](https://discuss.hashicorp.com/t/request-for-testing-removed-block/60511/8 "2024-01-09T22:20:21Z")

</div>

Is the `lifecycle` block required when `destroy = "false"` ? This seems like the main reason most terraform authors would go through the extra work of adding a `removed` block in the first place. If I want to remove a resource from state and have terraform destroy the underlying resource, I’m just going to delete the resource block. I can see why public module developers may want the extra documentation provided by an explicit `removed{}` block with `destroy="true"` (“You’re not remembering incorrectly; there used to be a resource here”), but requiring the `lifecycle` block in every `removed` block feels like requiring terraform authors to answer Yes to “Are you sure you want to save this file?” every time they hit the Save button.

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [January 10, 2024, 9:20pm UTC](https://discuss.hashicorp.com/t/request-for-testing-removed-block/60511/9 "2024-01-10T21:20:11Z")

</div>

Hi @jbeal_ramp,

A `removed` block for a resource supports a subset of the arguments that are valid in a `resource` block.

For this round, the subset is limited only to the newly-added `destroy = false` lifecycle option, but in future it might also grow to include some or all of the following:

- `provider` arguments for specifying which provider configuration should destroy the object.
- `create_before_destroy` to specify that the deletion should be subject to the same create/destroy graph ordering inversion that is used when that argument appears in a `resource` block. (This is sometimes important if the object is being destroyed in the same plan as a related object is being updated, so that the destroy and the update will happen in the correct relative order.)
- `precondition` blocks defining conditions that must be true before the object can be destroyed.
- `provisioner` blocks with `when = destroy` set, and `connection` blocks to configure how they should connect to the object being destroyed.

The arguments that belong in `lifecycle` in a `resource` block also belong in `lifecycle` in a `removed` block, so that the two are symmetrical.

For this initial round that only supports one argument it does of course seem overly verbose, but it’s designed this way to allow future expansion so that anything new added to `resource` blocks can, if it’s useful to do so, also be added to `removed` blocks that refer to resources.

---

<div class="post-metadata">

**Author:** ![jbeal\_ramp](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/jbeal_ramp/32/39700_2.png) [@jbeal\_ramp](https://discuss.hashicorp.com/u/jbeal_ramp)\
**Post date:** [January 12, 2024, 12:47am UTC](https://discuss.hashicorp.com/t/request-for-testing-removed-block/60511/10 "2024-01-12T00:47:54Z")

</div>

I appreciate the explanation, and those all seem like good future extensions for a `removed` block. I guess the rationale for not defaulting `destroy` to `false` is that could conflict with some of these future use cases, so requiring it to be explicit helps to prevent some illogical configurations? (e.g. it would be illogical to specify – or default – `destroy = false` if also specifying a `provider` responsible for (not?) destroying the object.)

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [January 15, 2024, 5:28pm UTC](https://discuss.hashicorp.com/t/request-for-testing-removed-block/60511/11 "2024-01-15T17:28:01Z")

</div>

Indeed, the broad idea is that a `removed` block that refers to a resource is like a `resource` block with all of the desired state removed and only a subset of the metadata pieces left, but all of those metadata pieces should have equivalent behavior in `removed` as they did in `resource` so that an author doesn’t need to memorize a bunch of different defaults in this case.

Removing an item from the configuration without destroying it is a rare thing to do, and so it doesn’t seem justified to optimize the syntax used to declare it at the expense of inconsistency.

Subjectively, it also means that a module that is requesting that something ought not to be destroyed will always have `destroy = false` written in it, which is a much clearer statement (to an unfamiliar future reader) of “do not destroy this” than just an empty `removed` block would be. Explicit is better than implicit.

---

<div class="post-metadata">

**Author:** ![mikebranstein](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/mikebranstein/32/39788_2.png) [@mikebranstein](https://discuss.hashicorp.com/u/mikebranstein)\
**Post date:** [January 15, 2024, 7:48pm UTC](https://discuss.hashicorp.com/t/request-for-testing-removed-block/60511/12 "2024-01-15T19:48:16Z")

</div>

I tend to agree with @jbeal_ramp, and may go a bit further and suggest that a `removed` block can never delete a resource - similar to an `import` block can never create a resource.

Traditional `resource` blocks are the constructs for creating and deleting resources. `import` and `removed` blocks are the constructs for managing the state (creating and deleting) of a resource without the need of the CLI.

Looking at `removed` in this way would suggest the `destroy` option within the `lifecycle` block is unnecessary and should not be applicable.

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [January 16, 2024, 12:35am UTC](https://discuss.hashicorp.com/t/request-for-testing-removed-block/60511/13 "2024-01-16T00:35:24Z")

</div>

The language design of `removed` blocks is effectively frozen at this point, because we’re at the last planned release candidate for Terraform v1.7.0 and so bugs notwithstanding the final v1.7.0 will exactly match v1.7.0-rc2. (We probably should’ve wound down this topic a few weeks ago when we hit v1.7.0-beta1, since we’ve only really been fixing bugs since then, but alas… 😖)

However, even if we _could_ still change it I don’t feel convinced that we should. `removed` blocks as currently designed are intended to grow to be be parts of the solutions for a bunch of different issues, including but not limited to the following (which were the ones I was able to find quickly from memory while writing this comment):

- [on-destroy provisioners not being executed · Issue #13549 · hashicorp/terraform · GitHub](https://github.com/hashicorp/terraform/issues/13549)
- [Error: Provider configuration not present · Issue #25682 · hashicorp/terraform · GitHub](https://github.com/hashicorp/terraform/issues/25682)
- [Do not delete a resource but create a new resource when change is detected · Issue #15485 · hashicorp/terraform · GitHub](https://github.com/hashicorp/terraform/issues/15485)

I do acknowledge that because the folks who are gathered in this issue are primarily motivated by having a configuration-based alternative to `terraform state rm` it’s natural that this audience would prioritize the ergonomics of _that in particular_, but this capability is just the first of many things we’re intending to solve by fixing what is really a design flaw in today’s Terraform: that removing things from the configuration removes both the desired state _and_ all of the metadata Terraform needs to actually destroy the removed object.

The way I suggest thinking of a `removed` block is as a record of something that used to be present and is now removed, along with the instructions to Terraform on how it should react if that object still exists in the prior state. There are various different ways Terraform could potentially react to that situation; deleting it from the state without taking any other action is the first new possibility we’re going to support here, but by the time this is done I expect it will be close to the least interesting of the things that `removed` can achieve.

Deleting something from the state without destroying it is a rare, “advanced user” sort of thing, which means that:

1. It doesn’t really _need_ a concise representation in configuration. If this is something you’re using _a lot_ then you’re not really using Terraform in the way it’s intended to be used.

2. It’s relatively unlikely that an average Terraform maintainer will have encountered it before, and so when someone _does_ find it for the first time (e.g. in a Terraform module they’ve inherited from someone else who is long gone) we’d like for them to be able to make a confident guess as to what it represents.

(It also, as a nice consequence of treating _all_ of these options as additive, solves the less-significant-but-still-important problem of allowing a parent module to override the decision made by a child module or allowing an [override file](https://developer.hashicorp.com/terraform/language/files/override) to overrule a normal file, by explicitly setting `destroy = true`. That’s more just a nice consequence of the decision made for other reasons though; I expect we could’ve found other ways to solve it if we needed to.)

* * *

Another potential future plan in this area that will hopefully help to remove some friction are some new commands for manipulating the _desired state_ (i.e. the configuration) instead of the latest state snapshot directly. These are intended as modern replacements for the various `terraform state ...` subcommands, correcting the historical design error of exposing direct state manipulation instead of that being a hidden side-effect of `terraform apply`.

These are not designed fully and so are just early sketches that might change, but consider:

- `terraform add -import=i-12345 aws_instance.example` would ask the AWS provider to fetch EC2 instance `i-12345`, then generate a `resource "aws_instance" "example"` block based on that, _and_ add the necessary `import` block so that you can subsequently run `terraform apply` to complete the addition.

- `terraform rm aws_instance.example` would delete the `resource "aws_instance" "example"` block and replace it with a `removed` block that contains all of the relevant `provisioner`, `connection`, `precondition`, `postcondition`, etc settings from the `resource` block, so that a subsequent `terraform apply` will handle the removal as “delete from state” while still respecting all of the previously-configured meta-arguments.

- `terraform mv aws_instance.example aws_instance.example2` would change the existing `resource "aws_instance" "example"` block to be `resource "aws_instance" "example2"` instead, and add a `moved` block commemorating that change so that a subsequent `terraform apply` can update the state to match.

I’ve described the above as CLI commands because that’s a concise way to get the idea across, but we also imagine exposing similar commands as part of the text editor integrations so that those who prefer to work in their text editors can perform these development-time tasks without dropping to a shell, and then check the results directly in the text editor pane before committing.

Either way, it would become Terraform’s job to write the required `import`, `moved` or `removed` blocks, which the human author can then review and alter if they have a different idea of how Terraform ought to treat the situation. After the change is committed, `terraform apply` updates the remote system and the state to match the new desired state, as usual.

---

<div class="post-metadata">

**Author:** ![jbeal\_ramp](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/jbeal_ramp/32/39700_2.png) [@jbeal\_ramp](https://discuss.hashicorp.com/u/jbeal_ramp)\
**Post date:** [January 17, 2024, 4:25pm UTC](https://discuss.hashicorp.com/t/request-for-testing-removed-block/60511/14 "2024-01-17T16:25:59Z")

</div>

> [@apparentlymart](#):
>
> Removing an item from the configuration without destroying it is a rare thing to do, and so it doesn’t seem justified to optimize the syntax used to declare it at the expense of inconsistency.

This feels to me like a statement that is only true because the current state of affairs makes it difficult to do. I hope to use this feature as soon as it is released to try to break some of our larger terraform cloud workspaces into smaller state files. This sort of refactoring would be very cumbersome to do today. With the ability to use `removed` blocks in the current module and `import` blocks in a refactored new module, we can migrate state management for a resource without modifying the underlying resource.

Edited: To be clear – not intended to challenge the language design at this point; I see the rationale.
