Skip to content

Intermediate · Infrastructure as code with Terraform · topic 32 of 53 · 30 min

Terraform state, backends and drift

Explanation

Terraform keeps a state file that maps each resource in your code to the real object in the cloud. State can contain secrets in plain text, so it never goes into Git; teams store it in a remote backend such as S3, Azure Storage, Google Cloud Storage or HCP Terraform, with locking so two people cannot apply at once. Drift is when reality changes outside Terraform; plan -refresh-only shows it.

What state is and why Terraform needs it

Your code says "there is a bucket called aws_s3_bucket.logs". The cloud has a bucket with the real name college-logs-prod. Terraform needs a record that links the two. That record is the state, stored by default in a file called terraform.tfstate (JSON). It holds the resource address, the real ID and the last known attributes of everything Terraform manages.

At every plan, Terraform reads the state, refreshes it by asking the cloud for the current values, and compares the result with your code. Without state, Terraform would not know which real bucket is "its" bucket, and it would try to create everything again.

Think of it like this: State is like the register in a library. The shelves hold the books (real resources); your code is the list of books the library should own. The register says which shelf copy is which catalogue entry. Lose the register and you cannot tell which books are yours.

Why state is sensitive

State stores attributes in plain text. That includes database passwords, private keys created by Terraform, and connection strings, even for values marked sensitive. Anyone who can read the state file can read those secrets. Treat state like a password vault.

output
# .gitignore for a Terraform project
.terraform/
*.tfstate
*.tfstate.*
crash.log
*.tfvars          # only if your tfvars files hold secrets; commit safe example files instead
override.tf
override.tf.json
Never commit state. Do commit .terraform.lock.hcl (the provider lock file).

Local versus remote backends

A backend is where state lives. The default local backend writes terraform.tfstate on your laptop. That breaks in a team: your colleague does not have your file, and if your laptop dies the state is gone. A remote backend stores state in shared, durable storage with access control and encryption.

BackendWhere state lives
s3An Amazon S3 bucket (turn on versioning and encryption)
azurermAn Azure Storage account blob container
gcsA Google Cloud Storage bucket
HCP Terraform (formerly Terraform Cloud)HashiCorp's hosted service, configured with a cloud block
output
terraform {
  backend "s3" {
    bucket       = "acme-terraform-state"
    key          = "network/prod/terraform.tfstate"
    region       = "ap-south-1"
    encrypt      = true
    use_lockfile = true   # native S3 locking in newer Terraform versions; check the docs for yours
  }
}
Terraform (HCL): after adding or changing a backend, run terraform init again (it offers to migrate state).

State locking

If two engineers run apply at the same moment, both read the same old state and both write a new one. One change is lost and the state may be corrupted. Locking prevents this: the first apply takes a lock, and the second waits or fails with "Error acquiring the state lock". Newer Terraform versions can lock with a lock file in the S3 bucket itself (use_lockfile = true). The older way used a DynamoDB table, which is now deprecated. azurerm and gcs backends lock natively, and HCP Terraform handles it for you.

Working with state safely

bash
terraform state list                                  # every address in state
terraform state show aws_s3_bucket.logs               # stored attributes of one resource
terraform state mv aws_s3_bucket.logs aws_s3_bucket.app_logs   # you renamed it in code
terraform state rm aws_s3_bucket.legacy               # forget it; the real bucket is NOT deleted
state mv and state rm change the record, not the real infrastructure.

Newer Terraform also lets you do these moves in code: a moved block records a rename, and a removed block forgets a resource. They are reviewed in a pull request like any other change, which is safer than running commands by hand.

Importing existing resources

Often a resource was created by hand before IaC. An import block (Terraform 1.5 and later) adopts it into state, so Terraform manages it from now on. terraform plan -generate-config-out=generated.tf can even write a first draft of the resource block for you to clean up.

output
import {
  to = aws_s3_bucket.logs
  id = "college-logs-prod"
}
Terraform (HCL): plan shows the import; apply records it in state.

Drift

Drift means the real infrastructure no longer matches the state, usually because someone changed it in the console. A normal plan detects drift and proposes to change it back to match the code. terraform plan -refresh-only shows only what changed outside Terraform, without proposing to fix it, and terraform apply -refresh-only updates state to accept reality. Then decide: either update the code to match, or let the next apply restore the code's version.

Common mistakes

Careful: Editing terraform.tfstate by hand. One wrong comma or ID can make Terraform delete or duplicate real resources. Use terraform state commands, moved, removed and import blocks instead, and keep versioning on the state bucket so you can roll back.
Careful: Committing state to Git, or sharing it on chat. State can hold passwords in plain text. If it was ever pushed, remove it, rotate every secret inside it, and move to an encrypted remote backend.
Careful: Using terraform state rm and expecting the cloud resource to disappear. It only forgets the record; the real resource keeps running and keeps costing money.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Terraform state, backends and drift · 1/7

Terraform state

In this lesson you will learn what Terraform state is, where to keep it, and how to handle drift.

Prefer a video by a teacher? Find free videos on “terraform state remote backend s3 locking drift explained” on YouTube ↗

In real life: Drift at a hospital chain

A hospital chain in Chennai manages its patient-portal security groups with Terraform. During a night incident, an engineer opens port 22 to 0.0.0.0/0 in the console and forgets to close it. The nightly CI job runs terraform plan -refresh-only, sees the changed ingress rule and posts an alert to the team channel. The next morning the team runs a normal apply, which puts the rule back to the reviewed version in code.

bash
terraform plan -refresh-only -detailed-exitcode
# exit code 0 = no drift, 2 = drift found, 1 = error
A drift check that a CI job can turn into an alert.

Practice (0/3 done)

Task 1 · Question

Which file should NOT be committed to Git?

Task 2 · Code · Python

Detect drift. state and real are dicts of attributes for one resource (what Terraform recorded, and what the cloud reports now). Write find_drift(state, real) that returns a sorted list of keys whose values differ, including keys that exist in only one of the two dicts.

Task 3 · Question

You renamed resource aws_instance.web to aws_instance.frontend in code. Plan wants to destroy one and create one. What is the right fix?

Quick quiz

Question 1

1.What does state locking protect against?

Question 2

2.What does terraform state rm aws_s3_bucket.legacy do to the real bucket?

Question 3

3.Which command shows changes made outside Terraform without proposing to undo them?

Question 4

4.A bucket was created by hand last year. How do you bring it under Terraform without recreating it?

Question 5

5.Why turn on versioning for the S3 bucket that stores state?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What does state locking protect against?

Answer: Two applies writing the same state at the same time

Locking makes the second apply wait or fail, so changes are not lost and state is not corrupted.

What does terraform state rm aws_s3_bucket.legacy do to the real bucket?

Answer: Nothing: Terraform just stops tracking it

state rm only removes the record. The real resource keeps existing.

Which command shows changes made outside Terraform without proposing to undo them?

Answer: terraform plan -refresh-only

A refresh-only plan compares state with reality and reports drift only.

A bucket was created by hand last year. How do you bring it under Terraform without recreating it?

Answer: Use an import block (or terraform import), then plan and apply

Import adopts an existing object into state. A plain resource block would try to create a new one.

Why turn on versioning for the S3 bucket that stores state?

Answer: You can roll back to an older state if one gets corrupted

Versioning keeps earlier copies of the state file, a safety net for mistakes.

Why should terraform.tfstate not be committed to Git?Related

Answer: It can contain secrets in plain text and it must be shared with locking, which Git cannot do

State can hold passwords and keys, and it needs locking to stop two applies at once. Use an encrypted remote backend.

From Terraform for the cloud

Your company has 200 resources that were created in the console. What is the usual way to bring them under Terraform?Related

Answer: Import them (import blocks or terraform import), then plan until it shows no changes

Importing adds existing resources to the state; a clean plan proves the code matches them.

From IaC in a team: reviews, pipelines and policy

Which statement about Terraform and OpenTofu is correct?Related

Answer: Terraform moved to the Business Source License in 2023, and OpenTofu is an open-source fork under the Linux Foundation

HashiCorp changed Terraform to BSL 1.1 in August 2023. OpenTofu forked the last open-source version.

From Why infrastructure as code

How do you refer to the id of resource "aws_s3_bucket" "logs"?Related

Answer: aws_s3_bucket.logs.id

A resource address is TYPE.NAME, and you add the attribute after another dot.

From Terraform basics: HCL, providers, resources and the workflow

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 1 more topic in “Infrastructure as code with Terraform” before its exam: Modules, environments and Terraform in CI.
  2. Module 7 exam: Infrastructure as code with Terraform · 13 questions · 20 min, without help. 4 topics of this module still to finish first.
  3. Then the next module, Configuration management with Ansible, and YAML. 53 topics left in the course before the final exam.

← Previous: Terraform basics: HCL, providers, resources and the workflow