7.1.1. terraform import and Import Blocks
💡 First Principle: Import is fundamentally a state operation — it binds an existing real resource to a configuration address — so the address you import to must already have a matching resource block before the classic command will run, or Terraform will plan to destroy the unmanaged-looking entry.
The classic CLI command maps one real resource ID to one configuration address:
terraform import aws_instance.web i-1234567890abcdef0
The resource "aws_instance" "web" block must already exist before you run this — classic import errors out if the address is not in configuration. The command writes the resource into state but does not create or edit configuration; if the block's arguments don't match reality, the next plan shows changes. And if the resource block is later deleted or renamed so nothing in configuration matches the imported address, the state entry is left with no configuration and the next plan proposes to destroy the real resource.
Import blocks (Terraform 1.5+) make import declarative and reviewable:
import {
to = aws_instance.web
id = "i-1234567890abcdef0"
}
Because they live in configuration, import blocks are planned (you preview the import in terraform plan; state is only written when you apply), version-controlled, and can import many resources at once — a big improvement over the imperative command.
⚠️ Exam Trap: Classic terraform import imports into state only, one resource at a time, and never generates config. Import blocks are declarative, plannable, and support bulk import. Don't attribute config generation to the classic command.
Reflection Question: You write an empty resource block, run classic terraform import against it, and the next terraform plan is not a clean no-op. What does it propose, and why does that differ from what happens if the block is later deleted altogether?