Refactoring: import, moved & state ops
Adopt and reshape without destroying.
Two hard problems come up constantly in real Terraform: adopting infrastructure that already exists (created by hand or another tool) without recreating it, and reshaping your code (renaming, splitting modules) without Terraform seeing the new addresses as destroy-and-recreate. Both are solved by manipulating the mapping between code and state — import to bind existing reality into state, and moved to re-map addresses safely.
import: adopt existing resources
import binds a real resource to a config address in state, so Terraform starts managing it instead of trying to create a duplicate. Modern Terraform uses an import block (declarative, planned, reviewable) rather than only the older terraform import CLI command. You write the resource block to match, add an import block pointing at the real ID, run plan to confirm it lines up (no changes), and apply — now it is managed.
import {to = aws_instance.web # the address in your configid = "i-0abc123def456" # the real, already-existing resource}resource "aws_instance" "web" { # must match reality closely enough to plan cleanami = "ami-0abc123"instance_type = "t3.micro"}# terraform plan → should show 1 to import, 0 to change
moved: refactor without destroying
When you rename a resource or pull it into a module, its state address changes — and by default Terraform reads that as “old address destroyed, new address created,” which for real infrastructure is an outage. A moved block tells Terraform the resource simply has a new address, so it updates state in place with no destroy. This is how you refactor safely, including the count→for_each switch from the last lesson.
moved {from = aws_instance.web # old addressto = module.web.aws_instance.this # new address after refactor}# plan now shows a move, not a destroy/create. (state mv is the imperative equivalent.)