Not everything starts in Terraform. A container someone started by hand, a bucket from before the project, a resource you split out of another state: import binds an existing object to an address without recreating it.
flowchart LR REAL["Existing container shop-cache"] --> IB["import block: to + id"] IB --> GEN["plan -generate-config-out=generated.tf"] GEN --> EDIT["Review and clean generated.tf"] EDIT --> AP["apply: 1 imported, 0 added"] AP --> ST["State now tracks it"] %% step REAL: The object already exists and has an ID the provider understands. %% step IB: An import block says which address the object should get and what its ID is. %% step GEN: Terraform can write a first version of the matching resource block for you. %% step EDIT: You tidy the generated code: remove defaults, use variables, keep secrets out. %% step AP: Apply records the object in state; nothing is created or destroyed.
import blocks (Terraform 1.5+)
import {
to = docker_container.cache
id = "3c9d1f2e8a7b" # the provider's import ID (here a container ID)
}
terraform plan -generate-config-out=generated.tf
# docker_container.cache: Preparing import... [id=3c9d1f2e8a7b]
# Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.
terraform apply
If you already wrote the resource block, leave out -generate-config-out; plan shows any differences between your code and the real object, and you adjust the code until the plan says 1 to import, 0 to change.
Import blocks are code: reviewed in a pull request, run by CI, and repeatable. After the apply you can delete them, or keep them as documentation (they become no-ops).
Importing many objects
import {
for_each = var.legacy_containers # map: key => container ID
to = docker_container.legacy[each.key]
id = each.value
}
The old CLI command
terraform import 'docker_container.cache' 3c9d1f2e8a7b
It writes to state immediately, without a plan, and you must write the resource block first. Prefer import blocks.
Finding the import ID
Each resource page in the provider docs ends with an Import section that shows the ID format: a container ID for Docker, a bucket name for aws_s3_bucket, a full resource ID for Azure, a projects/.../instances/... path for Google.
Clean up generated code
Generated configuration lists every argument the provider returned, including defaults and computed noise. Remove what you would not write by hand, replace literals with variables, and make sure no secret values are written into generated.tf before you commit.
Traps: importing the same object into two addresses or two states makes both think they own it, and one of them will eventually delete it; and an import with a wrong ID fails at plan, which is exactly why import blocks are safer than the CLI.