Skip to content

Terraform: Desired State Engine

Mental model

Terraform is a vending machine. You insert a description of what you want (.tf files). It compares that to what it already dispensed (state file). Then it figures out the minimum set of actions to make reality match your description.

What it looks like

"Infrastructure as code" — you write files and run commands to create cloud resources.

What it really is

A desired-state reconciliation loop with three moving parts:

  1. Desired state — the dependency graph built from your .tf files.
  2. State file (terraform.tfstate) — a JSON cache of what Terraform believes currently exists.
  3. Reality — what actually exists in your cloud provider right now.

plan diffs desired vs state. apply executes the diff via provider API calls. refresh updates state to match reality.

Why it seems confusing

The state file is not reality. It is Terraform's last known snapshot of reality. Someone can change infrastructure outside Terraform and the state file won't know. This gap is called drift.

People also confuse the state file with the .tf files. The .tf files are what you want. The state file is what Terraform thinks you have.

What actually matters

  • Graph: Terraform resolves dependency order automatically. You declare resources; it figures out creation sequence.
  • Plan before apply: always read the plan. It tells you exactly what will be created, changed, or destroyed.
  • State is shared: use remote state (S3 + DynamoDB, Terraform Cloud) so your team shares one source of truth.
  • State locking: prevents two people from running apply at the same time, which would corrupt state.

Common mistakes

  • Editing the state file by hand instead of using terraform state commands.
  • Not using remote state — local state doesn't scale past one person.
  • Ignoring plan output and blindly running apply.
  • Making manual changes in the console, then wondering why Terraform wants to revert them (drift).
  • Storing secrets in plain text in .tf files or state.

Small examples

# plan output — read this before every apply
Plan: 1 to add, 0 to change, 0 to destroy.

# importing existing infrastructure into state
terraform import aws_instance.web i-1234567890abcdef0

# state file is JSON — but don't edit it directly
{
  "resources": [
    {
      "type": "aws_instance",
      "name": "web",
      "instances": [{ "attributes": { "id": "i-123..." } }]
    }
  ]
}

# refresh state to detect drift
terraform refresh
terraform plan   # now shows drift as pending changes

One-line summary

Terraform diffs what you declared against what it recorded, then makes API calls to close the gap.