Skip to content

Terraform State

Terraform state represents the current condition of infrastructure resources at a specific point in time — essentially, what Terraform expects to exist in your cloud account.

When Terraform provisions resources, it creates a terraform.tfstate file. This JSON file maps resource instances to their remote counterparts.


Terraform State Commands

Terraform provides multiple subcommands to inspect and manipulate state:

  • terraform state list — List resources in the state
  • terraform state show — Display detailed info about a resource
  • terraform state rm — Remove resources from the state
  • terraform state pull — Downloads the remote state over the local state and outputs it
  • terraform state push — Update remote state from local file
  • terraform state replace-provider — Replace a provider in the state
  • terraform state mv — Move a resource within the state (e.g., move into a module or rename) without recreation (e.g., terraform state mv aws_instance.worker module.worker.aws_instance.worker)

State Backups

Terraform automatically creates a backup whenever a state-changing command is run. Backup file: terraform.tfstate.backup Cannot be disabled — ensures recovery best practices


Resource Drift

Drift occurs when the real infrastructure differs from the expected state.

A resource address identifies zero or more resource instances: [module_path][resource_spec]

module_path → module hierarchy: module.<module_name>[<module_index>]

  • module - the namespace for modules
  • module_name - user-defined name of the module
  • module_index - when multiple, specify an index

resource_spec → specific resource instance: <resource_type>.<resource_name>[<instance_index>]

  • resource_type - type of the resource being addressed
  • resource_name - user-defined name of the resource
  • instance_index - when multiple, specify an index

Drift Resolutions

Replacing Resources

When a resource has become damaged or degraded that cannot be detected by Terraform we can use the -replace flag to actually recreate it.

Terraform deprecated terraform taint in 0.15.2 (used to mark a resource for replacement the next time apply was triggered).

  • Usage: terraform apply -replace="aws_instance.my_web_app"
  • -replace flag is available on both plan and apply. It was changed so that now, it prompts for manual approval
  • -replace appears to only work for a single resource

Importing Resources

When an approved manual addition of resource needs to be added to our state file, we can use the terraform import command.

Command: terraform import <RESOURCE_ADDRESS> <ID>

Usage:

  • Define a placeholder for your imported resource in configuration files.
  • Leave the body blank and fill it in after importing it. It will not be autofilled
resource "aws_instance" "example" {
    # ...
}
  • Import the resource using: terraform import aws_instance.example i-abcd1234

Notes:

  • terraform import command can only import one resource at a time
  • Not all resources are importable, you need to check the bottom of resource documentation for support

Refreshing State

Use terraform apply -refresh-only to update Terraform state without changing actual resources.

Previously: terraform refresh - deprecated due to updating the Terraform state without prompting for approval .

Equivalent: terraform refresh → terraform apply -refresh-only -auto-approve

  • -refresh-only flag is supported by both plan and apply commands.

Cross-referencing stacks

terraform_remote_state data source retrieves root module output values from another Terraform configuration:

data "terraform_remote_state" "vpc" {
    backend = "remote"
    config = {
        organization = "hashicorp"
        workspaces = {
            name = "vpc-prod"
        }
    }
}
resource "aws_instance" "foo" {
    subnet_id = data.terraform_remote_state.vpc.outputs.subnet_id
}

It can also reference local state files:

data "terraform_remote_state" "networking" {
    backend = "local"
    config = {
        path = "${path.module}/networking/terraform.tfstate"
    }
}

Notes:

  • Only root-level outputs are accessible
  • Nested module outputs must be exposed in the root module (otherwise, they are not accessible)
  • instead of terraform_remote_state, for which you need access on the entire state snapshot, it's recommended to use other data sources when possible.

State Locking

Terraform locks state for write operations automatically. This prevents others from acquiring the lock and potentially corrupting your state file.

Notes:

  • Disable with -lock=false (not recommended)
  • Force unlock: terraform force-unlock <LOCK_ID> (useful when automatic unlocking fails)

Protecting Sensitive Data

Terraform state file can contain sensitive data (e.g. long-lived AWS credentials).

  • Local backend → state stored in plaintext JSON → do not commit
  • Remote/Cloud backends (Terraform Cloud / Enterprise) → state encrypted in transit & at rest (Enterprise also includes audit logging)
  • Third-party storage (standard backends) → may ensure encryption, versioning, access controls, audit trails if you configure them

Terraform Ignore Files

.terraformignore (root only) excludes paths from remote operations. This works like .gitignore, but only one file at root.

Default ignores: .git/, .terraform/


Output Values

Outputs expose useful information after Terraform runs:

  • surface resource attributes
  • feed external systems
  • enable cross-stack references via terraform_remote_state
  • provide debugging and visibility into deployed infrastructure

Outputs can be marked sensitive = true so it doesn't show in output of your Terminal (but they will still be visible within the statefile).

CLI commands:

  • terraform output — list all outputs
  • terraform output <name> — show one output
  • terraform output -json — print all outputs as json data
  • terraform output -raw — raw string output (preserve quotes for strings)