Terraform State Management · Topic 2 of 2
Remote Backends and Resource Drift
Terraform Associate 004: configure a remote backend, partial configuration, migrate state, detect drift with refresh-only, use moved and removed blocks.
- 5 min
- The backend block lives inside the
terraformblock, one per configuration, with no variables, locals or data source references. - Partial configuration supplies missing values with
terraform init -backend-config. - Run
terraform initafter any backend change;-migrate-statecopies state,-reconfigureignores it.
What it is
A remote backend stores state in a shared service such as an S3 bucket, Azure Storage or Google Cloud Storage instead of on one laptop. You configure it with a backend block inside the terraform block.
Resource drift is the difference between what state records and what really exists, usually because someone changed a resource outside Terraform. Managing state also covers refactoring: renaming resources or handing them off without destroying them.
How it works
The backend block
terraform {
backend "s3" {
bucket = "acme-terraform-state"
key = "network/terraform.tfstate"
region = "us-east-1"
use_lockfile = true
}
}
For the S3 backend, bucket, key and region are required, and locking is opt in with use_lockfile = true, which writes a .tflock file beside the state. The older dynamodb_table locking is deprecated.
| Backend rule | Detail |
|---|---|
| One per configuration | A configuration can have only one backend block |
| No named values | The block cannot refer to input variables, locals or data source attributes |
| Not with cloud | A configuration uses either a backend block or a cloud block, never both |
| Reinitialize | After any change to the backend, run terraform init again |
Partial configuration
Because variables are not allowed, you leave some arguments out and supply them at init time. The block still names the backend type.
terraform {
backend "s3" {}
}
terraform init -backend-config=prod.s3.tfbackend
terraform init -backend-config="key=prod/terraform.tfstate"
Values can come from a file, from KEY=VALUE pairs or from interactive prompts. Terraform writes the final backend settings in plain text to .terraform/terraform.tfstate and into saved plan files, so supply credentials through environment variables instead.
Changing or migrating a backend
When you add, change or remove a backend block, terraform init detects it and offers to copy existing state to the new location. terraform init -migrate-state asks for that migration explicitly, while terraform init -reconfigure uses the new settings and ignores the existing state. Removing the backend block and running init moves you back to the local backend.
Reading another configuration's state
The terraform_remote_state data source reads the root module outputs of another state, so one configuration can use values another one publishes.
data "terraform_remote_state" "network" {
backend = "s3"
config = {
bucket = "acme-terraform-state"
key = "network/terraform.tfstate"
region = "us-east-1"
}
}
You then refer to data.terraform_remote_state.network.outputs.subnet_id. Only outputs are available, not the other configuration's resources.
Detecting and handling drift
terraform plan and terraform apply refresh state against real infrastructure by default, so drift shows up as proposed changes. To see drift on its own, use refresh only mode.
| Command | Effect |
|---|---|
terraform plan -refresh-only | Shows how state would change to match real infrastructure; changes nothing |
terraform apply -refresh-only | Updates state to match real infrastructure, without changing any resource |
terraform plan -refresh=false | Skips the refresh and plans from state as recorded |
These replace the deprecated terraform refresh command, which updated state with no chance to review. After accepting drift into state, either update the configuration to match or run a normal apply to put the resource back as the configuration says.
Refactoring state with moved and removed blocks
moved {
from = aws_instance.web
to = aws_instance.app
}
removed {
from = aws_instance.legacy
lifecycle {
destroy = false
}
}
A moved block tells Terraform a resource has a new address, such as a rename or a move into a module, so it updates state instead of destroying and recreating it. A removed block with destroy = false takes a resource out of state while leaving the real object in place. The CLI equivalents are terraform state mv and terraform state rm; the blocks are preferred because they go through plan and review.
How the exam asks about this
- "The backend block must use var.environment for the key." Not allowed; use partial configuration with
-backend-configat init. - "Someone resized an instance in the console; you want state to match without changing anything." Run terraform apply -refresh-only.
- "You renamed a resource in code and plan shows destroy and create." Add a moved block from the old address to the new one.
What to remember for the exam
- The backend block lives inside the
terraformblock, one per configuration, with no variables, locals or data source references. - Partial configuration supplies missing values with
terraform init -backend-config. - Run
terraform initafter any backend change;-migrate-statecopies state,-reconfigureignores it. terraform_remote_statereads only the root module outputs of another state.-refresh-onlyon plan or apply replaces the deprecatedterraform refresh.movedrenames without recreation;removedwithdestroy = falsestops managing without destroying.
terraform apply -refresh-only changes state, not infrastructure. It accepts drift; it does not fix it. To undo a manual change, run a normal plan and apply from the configuration.
Common mistakes
Putting secrets in the backend block. Backend settings are saved in plain text under .terraform and in plan files. Use environment variables or the backend's own credential chain.
Forgetting to reinitialize. Editing the backend block does nothing until terraform init runs. Plan and apply report that the backend needs initializing.
Deleting a resource block to stop managing it. Deleting the block makes Terraform plan a destroy. Use a removed block with destroy = false or terraform state rm to keep the real object.
Check yourself
Can a backend block reference an input variable?
No. Backend blocks cannot use variables, locals or data source attributes, so supply changing values through partial configuration with
-backend-config.What does
terraform plan -refresh-onlydo?It shows how state would change to match real infrastructure after changes made outside Terraform, without changing state or resources.
How do you rename a resource without destroying it?
Add a
movedblock from the old address to the new one, or useterraform state mv, so Terraform updates state instead of recreating the object.
Topic 2 of 2 in Terraform State Management
Test yourself on Terraform State Management
The full HashiCorp Terraform Associate 004 question bank has 600 questions, with timed practice exams and an explanation for every answer.
This study guide is independent and is not affiliated with, endorsed by, or sponsored by HashiCorp. Vendor names, certification names and exam codes are trademarks of their respective owners and are used for identification only.