# Terraform import, moved and removed Blocks: Refactor Without Destroying

> How to import existing AWS resources into Terraform or OpenTofu, rename them with moved blocks and stop managing them with removed blocks, without downtime.

- Source: https://www.itwonderlab.com/terraform-import-moved-removed/
- Published: 2026-09-07
- Updated: 2026-09-07
- Author: Javier Ruiz Jiménez (https://www.javierruizjimenez.com/)
- Site: IT Wonder Lab (https://www.itwonderlab.com/)

---

## Change what Terraform manages without touching the infrastructure

Sooner or later you need to bring existing infrastructure under Terraform, rename a resource, move it into a [module](https://www.itwonderlab.com/terraform-module/) or stop managing it. All of these change the [state](https://www.itwonderlab.com/terraform-state/) and the code, but must **not** change the real infrastructure. Terraform (1.5 and later) and OpenTofu (1.6 and later) do it with configuration blocks that go through `plan` and code review, instead of the old imperative commands.

### import blocks: adopt existing resources

Suppose an S3 bucket was created by hand in the console:

```hcl title="import.tf"
import {
  to = aws_s3_bucket.legacy
  id = "ditwl-legacy-bucket"
}

resource "aws_s3_bucket" "legacy" {
  bucket = "ditwl-legacy-bucket"
}
```

`terraform plan` shows that the resource will be imported, and `apply` adds it to the state. The `id` format depends on the resource and is documented at the end of each resource page in the provider documentation.

You can ask Terraform to write the resource code for you:

```shell
$ terraform plan -generate-config-out=generated.tf
```

Review the generated file before using it: it contains every attribute, including some computed ones that you should remove.

After the import is applied you can delete the `import` block. With `for_each` in the `import` block (supported in current versions) you can import many resources at once.

> [!TIP]
> After importing, run `plan` again. A clean plan with no changes confirms that the code matches reality. If the plan wants to change something, adjust the code until it does not.

### moved blocks: rename or relocate a resource

Renaming `aws_instance.web` to `aws_instance.app` makes Terraform destroy one and create the other. A `moved` block tells it that it is the same object:

```hcl title="moved.tf"
moved {
  from = aws_instance.web
  to   = aws_instance.app
}
```

It also works to move a resource into a module, or between modules:

```hcl title="moved-module.tf"
moved {
  from = aws_vpc.main
  to   = module.network.aws_vpc.main
}
```

Keep the `moved` blocks while there may be other users of the module with the old state, then you can remove them. Modules published for others should keep them.

### removed blocks: stop managing without destroying

To remove a resource from the code **without** deleting the real infrastructure, use a `removed` block (Terraform 1.7 and OpenTofu 1.7 and later):

```hcl title="removed.tf"
removed {
  from = aws_s3_bucket.legacy

  lifecycle {
    destroy = false
  }
}
```

The resource is forgotten by the state, and the bucket keeps existing. Without `destroy = false`, removing the block from the code means `destroy`.

### The command line equivalents

Before these blocks existed the same was done with commands. You may still find them in older tutorials:

```shell
$ terraform import aws_s3_bucket.legacy ditwl-legacy-bucket
$ terraform state mv aws_instance.web aws_instance.app
$ terraform state rm aws_s3_bucket.legacy
```

They change the state immediately and without a plan, so they are easy to get wrong and leave nothing in the code history. Prefer the blocks, especially in a team or in a CI/CD pipeline.

### Checklist

1. Back up the state (or use a versioned [backend](https://www.itwonderlab.com/terraform-backend/)).
2. Write the block and run `plan`.
3. Confirm the plan says import or move, with **no** destroy.
4. Apply, then run `plan` again to confirm no changes remain.

Related: [lifecycle](https://www.itwonderlab.com/terraform-lifecycle-meta-argument/), [for_each vs count](https://www.itwonderlab.com/terraform-for-each-vs-count/) and [migrating from Terraform to OpenTofu](https://www.itwonderlab.com/terraform-to-opentofu/).
