Skip to content

State Management


State is the piece of Terraform that lets configuration, previous decisions, and real infrastructure meet. Once Terraform is used by more than one person, that state stops being a local implementation detail and becomes shared operational data.

In this chapter, you’ll look closely at what Terraform state contains, how to inspect it safely, why concurrent access is dangerous, and why state storage and state locking solve different problems.

What Does Terraform State Represent?

Terraform has to answer a difficult question:

Given the configuration I have now, what infrastructure already belongs to this Terraform configuration?

The configuration tells Terraform what you want. The cloud provider tells Terraform what exists. State records Terraform’s knowledge about the relationship between those two things.

A useful mental model is:

Terraform Configuration
        β”‚
        β”‚ desired configuration
        β–Ό
   Terraform Engine
        β”‚
        β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ί Cloud Provider
        β”‚                    β”‚
        β”‚                    β–Ό
        β”‚              Real Infrastructure
        β”‚
        β–Ό
     State
        β”‚
        └── Terraform's recorded knowledge

State is therefore not simply a cache of the cloud provider.

It contains Terraform-specific information needed to manage resources over time, including resource instances, their addresses, provider-related information, and values returned while managing those resources.

Configuration, State, and Infrastructure Are Different

Consider a resource:

resource "aws_s3_bucket" "logs" {
  bucket = "devstuffs-example-logs"
}

There are three different things involved:

ThingMeaning
ConfigurationWhat your Terraform code declares
StateWhat Terraform currently records about the managed resource
InfrastructureWhat actually exists in AWS

These can temporarily disagree.

For example, somebody could delete the S3 bucket manually. Your configuration still declares it, and state may still contain it. During the next plan, Terraform can discover that the real infrastructure no longer matches Terraform’s expectations.

That distinction becomes especially important when diagnosing problems.

State Snapshots and Resource Instances

Terraform stores state as a snapshot representing the resources Terraform knows about at a particular point in time.

A resource can also have multiple instances.

For example:

resource "aws_instance" "server" {
  count = 2

  # ...
}

Terraform does not treat that as one undifferentiated object. It tracks instances at addresses such as:

aws_instance.server[0]
aws_instance.server[1]

Likewise, a resource using for_each has keyed instances:

aws_instance.server["web"]
aws_instance.server["worker"]

These addresses matter because Terraform needs to identify exactly which instance a state operation refers to.

The same idea becomes more important with modules:

module.network.aws_vpc.main
module.app.aws_instance.web
module.app.aws_instance.worker

An address tells you where an object lives in Terraform’s resource hierarchy.

Note

A resource address is a Terraform identity, not an AWS resource ID. aws_instance.server[0] is a Terraform address; an EC2 instance ID such as i-0123456789abcdef0 is an identifier assigned by AWS.

Inspecting State Safely

You normally should not open terraform.tfstate in an editor and start changing JSON manually.

Terraform provides commands for inspecting state.

List the resources Terraform currently knows about:

terraform state list

You might see:

aws_s3_bucket.logs
aws_vpc.main
aws_subnet.public[0]
aws_subnet.public[1]

To inspect one resource:

terraform state show aws_s3_bucket.logs

This is useful when you need to answer questions such as:

  • Does Terraform know about this resource?
  • What address does it have?
  • What attributes are recorded?
  • Which provider is associated with it?
  • What values does Terraform currently have in state?

For a broader view, you can retrieve the complete state:

terraform state pull

Be careful with that command. State can contain sensitive information, so piping the result into a file creates another sensitive copy.

For example:

terraform state pull > state-backup.json

Treat that file as a secret-bearing artifact.

Warning

A state file can contain passwords, tokens, private keys, connection strings, and other sensitive values depending on the resources and providers you use. Never assume that hiding an output from the terminal means the value is absent from state.

State Manipulation Is Not Routine Editing

Terraform also provides state-management commands for exceptional situations.

For example:

terraform state mv ...
terraform state rm ...
terraform state pull
terraform state push ...

The first two are useful when changing Terraform’s understanding of resource identity during refactoring.

You may already have encountered resource moves and imports earlier in the course. The important operational lesson here is that state manipulation changes Terraform’s recorded ownership and identity.

terraform state push is particularly dangerous because it can overwrite remote state.

Warning

Treat terraform state push as an emergency tool, not a normal workflow. Before manually replacing state, preserve a backup and make sure you understand the state lineage and serial/version information involved.

Terraform protects some state operations using state metadata. In particular, state has a lineage identifying its history and a serial that increases as the state changes. These mechanisms help prevent accidentally replacing newer state with an unrelated or older snapshot.

Why State Backups Matter

A state backup gives you a recovery point before a risky operation.

For example, before an unusual manual state repair:

terraform state pull > state-before-repair.json

You can inspect the backup without touching the live state.

Remote backends can also provide their own recovery mechanisms. For example, an S3 backend can use bucket versioning so previous versions of a state object remain available.

The important distinction is:

Locking
   β”‚
   └── prevents conflicting writers

Backup / Versioning
   β”‚
   └── helps recover from mistakes

Locking does not replace backups.

Backups do not prevent two engineers from writing concurrently.

What Happens When State Becomes Corrupted?

State corruption can have several meanings.

The state file itself might be damaged. More commonly, the problem is that Terraform’s recorded state no longer makes sense relative to the infrastructure or the configuration.

Possible causes include:

  • An interrupted or failed operation
  • Manual state manipulation
  • A backend failure
  • Multiple writers operating without coordination
  • Human error
  • Accidental deletion or modification of state
  • Provider behavior that leaves Terraform with incomplete information

Recovery should be deliberate.

A sensible recovery sequence is:

  1. Stop additional Terraform operations.
  2. Determine whether another operation is still running.
  3. Preserve the current state.
  4. Inspect recent state versions or backups.
  5. Compare configuration, state, and real infrastructure.
  6. Correct the smallest possible problem.
  7. Run a plan before making further changes.

Do not immediately delete the state file because it “looks wrong.”

Deleting state does not delete the infrastructure. It deletes Terraform’s knowledge of what it manages, which can create a much larger recovery problem.

Why Two Engineers Cannot Safely Share Local State

Imagine two engineers working from separate laptops:

Engineer A ──► local state A
                    β”‚
                    β–Ό
               Infrastructure

Engineer B ──► local state B
                    β”‚
                    β–Ό
               Infrastructure

Suppose both start from the same state.

Engineer A changes the network.

Engineer B changes an application.

Both run Terraform independently.

Each laptop has its own state snapshot. Neither laptop automatically knows that the other engineer has just changed the shared infrastructure or state.

Now consider a worse case:

          Shared Infrastructure
             β–²          β–²
             β”‚          β”‚
        Engineer A   Engineer B
             β”‚          β”‚
          State A     State B

Both engineers can produce perfectly valid Terraform plans based on stale information.

This creates a coordination problem, not merely a syntax problem.

The Race Condition

Imagine both operations read state at roughly the same time:

Time ───────────────────────────────►

A: Read state ── Plan ── Apply ── Write state
B:      Read state ── Plan ── Apply ── Write state

The result can depend on timing.

One operation can overwrite or invalidate assumptions made by another. Even when Terraform detects some inconsistencies, the workflow is fundamentally unsafe because there is no shared coordination point.

The team therefore needs two things:

  1. Shared state storage
  2. Coordination around state-changing operations

That brings us to state locking.

State Storage and State Locking Are Different

These concepts are often confused.

State storage answers:

Where is the authoritative state?

Examples include:

Local disk
S3
HCP Terraform
Consul

State locking answers:

Who is currently allowed to perform a state-changing operation?

A backend may support one, both, or neither depending on its capabilities.

Conceptually:

                 Terraform State
                       β”‚
             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
             β”‚                   β”‚
        State Storage       State Locking
             β”‚                   β”‚
       "Where is it?"       "Who can write?"

If a backend stores state centrally but does not provide suitable locking, everyone can still be looking at the same state while competing to modify it.

Centralized storage alone does not solve concurrency.

How State Locking Protects You

When the backend supports locking, Terraform automatically acquires a lock for operations that could write state.

A simplified sequence looks like this:

Engineer A
    β”‚
    β–Ό
Acquire lock
    β”‚
    β–Ό
Read state
    β”‚
    β–Ό
Plan / Apply
    β”‚
    β–Ό
Write state
    β”‚
    β–Ό
Release lock

Engineer B
    β”‚
    └── waits while A holds the lock

Terraform’s state-locking behavior is automatic for supported backends. If Terraform cannot acquire the lock, it does not simply continue as though nothing happened.

You can technically disable locking for many commands:

terraform plan -lock=false

That is generally a bad idea for shared infrastructure.

Warning

-lock=false removes an important safety mechanism. Do not use it as a way to “get around” a lock in a team environment.

If Terraform reports that a lock is already held, first determine whether another operation is genuinely running.

Terraform also provides:

terraform force-unlock LOCK_ID

Warning

Use terraform force-unlock only when you are certain the lock is stale and belongs to an operation that is no longer running. Force-unlocking a legitimate lock can allow multiple writers to operate at the same time.

State Ownership Is an Operational Decision

Once state becomes shared, the team needs to decide who can modify it.

For a small project, everyone on the infrastructure team might have access.

For a larger organization, you may want:

Developers
   β”‚
   └── Plan / inspect

Infrastructure team
   β”‚
   └── Plan / Apply

Production administrators
   β”‚
   └── Production state access

Remember that state access itself can expose sensitive information.

Therefore:

Who can run Terraform?
        +
Who can read state?
        +
Who can modify state?

are all access-control questions.

A Practical Rule for Teams

Treat Terraform state like an important shared infrastructure asset.

You want:

  • One authoritative state location
  • Controlled access
  • State locking where supported
  • Backups or version history
  • A recovery procedure
  • Minimal manual state manipulation
  • Clear ownership

The next chapter turns this into an actual Terraform backend architecture.

You will move from:

terraform.tfstate on my laptop

to:

Terraform
   β”‚
   β–Ό
Shared Backend
   β”‚
   β–Ό
Authoritative State

That is the point where Terraform starts behaving like a team system rather than a command you happen to run locally.

Last updated on