Skip to content

Terraform_state

This chapter covers Terraform state management, remote backends, and state best practices.

Terraform uses state to map your configuration to real-world resources.

┌─────────────────────────────────────────────────────────────────────────────┐
│ Terraform State Purpose │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Terraform State │ │
│ │ │ │
│ │ Configuration Real World State │ │
│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │
│ │ │aws_vpc.main│ ←───▶ │ VPC │ ←───▶ │ vpc-id │ │ │
│ │ │ cidr=10.0 │ │ 10.0.0.0/16│ │ cidr=10.0 │ │ │
│ │ └───────────┘ └───────────┘ └───────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ State Functions: │
│ ✓ Track resource ownership │
│ ✓ Detect changes between config and reality │
│ ✓ Improve performance (caches attribute values) │
│ ✓ Collaborate across teams │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

By default, Terraform stores state in a local file:

Terminal window
# Default location
terraform.tfstate
# View state
terraform show
# List resources in state
terraform state list
# Show specific resource
terraform state show aws_instance.web
┌─────────────────────────────────────────────────────────────────────────────┐
│ Local State Storage │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Working Directory │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ main.tf │ │
│ │ variables.tf │ │
│ │ outputs.tf │ │
│ │ terraform.tfstate ◄── Local state file │ │
│ │ terraform.tfstate.backup ◄── Backup file │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ Pros: Simple, no additional setup │
│ Cons: Not shared, risk of data loss, conflicts in teams │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
{
"version": 4,
"terraform_version": "1.6.0",
"serial": 1,
"lineage": "a1b2c3d4-...",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "aws_vpc",
"name": "main",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"attributes": {
"id": "vpc-0abc123",
"cidr_block": "10.0.0.0/16",
"enable_dns_hostnames": true,
"tags": {
"Name": "main-vpc"
}
}
}
]
}
]
}

Remote backends store state in a shared location, enabling team collaboration.

┌─────────────────────────────────────────────────────────────────────────────┐
│ Remote Backend Types │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ S3 │ │ Azure │ │ GCS │ │ Terraform │ │
│ │ (AWS) │ │ (Azure) │ │ (GCP) │ │ Cloud │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Consul │ │ etcd │ │ PostgreSQL │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ Key features to consider: │
│ ✓ State locking - prevents concurrent modifications │
│ ✓ Encryption at rest - security for sensitive data │
│ ✓ Versioning - state file history │
│ ✓ Accessibility - team access controls │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
backend.tf
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/networking/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
┌─────────────────────────────────────────────────────────────────────────────┐
│ S3 Backend with DynamoDB │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ S3 + DynamoDB Backend │ │
│ │ │ │
│ │ ┌───────────────┐ DynamoDB Table │ │
│ │ │ │ ┌───────────────┐ │ │
│ │ │ S3 │◄──────────────────────│ LockID │ │ │
│ │ │ Bucket │ State Files │ Digest │ │ │
│ │ │ (encrypted) │ │ LockExpires │ │ │
│ │ │ │ └───────────────┘ │ │
│ │ └───────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ State locking │ │ │
│ └──────────┼───────────────────────────────────────┘───────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────┐ │
│ │ Terraform CLI │ │
│ └──────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Terminal window
# Create DynamoDB table for state locking
aws dynamodb create-table \
--table-name terraform-locks \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--region us-east-1
terraform {
backend "azurerm" {
resource_group_name = "terraform-state"
storage_account_name = "terraformstate123"
container_name = "tfstate"
key = "prod.terraform.tfstate"
}
}
terraform {
backend "gcs" {
bucket = "my-terraform-state"
prefix = "prod/networking"
}
}
terraform {
backend "remote" {
organization = "my-org"
workspaces {
name = "prod-networking"
# or prefix = "prod-"
}
}
}
Terminal window
# Pull state (download to local)
terraform state pull > terraform.tfstate
# Push state (upload local to backend)
terraform state push terraform.tfstate
# List resources
terraform state list
# Show resource details
terraform state show aws_instance.web
# Rename resource
terraform state mv aws_instance.web aws_instance.app
# Remove resource from state (without destroying)
terraform state rm aws_instance.old
# Move state to new resource (when replacing)
terraform state mv aws_instance.old aws_instance.new
# Backup state before modifications
terraform state backup backup.tfstate

State locking prevents concurrent modifications:

┌─────────────────────────────────────────────────────────────────────────────┐
│ State Locking Mechanism │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ User A: terraform apply │
│ ┌─────────────────┐ │
│ │ Acquire Lock │────────────────┐ │
│ └─────────────────┘ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ DynamoDB/ │ │
│ │ Consul/ etcd │ │
│ │ Lock │ │
│ └─────────────┘ │
│ │ │
│ User B: terraform apply │ │
│ ┌─────────────────┐ │ │
│ │ Wait... │◀───────────────┘ │
│ └─────────────────┘ │ │
│ │ │
│ User A: completes ▼ │
│ ┌─────────────────┐ ┌─────────────┐ │
│ │ Release Lock │─────▶│ Unlock │ │
│ └─────────────────┘ └─────────────┘ │
│ │
│ Error without locking: │
│ Error: Error acquiring the state lock │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ State Security Best Practices │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. Encryption at Rest │
│ ✓ S3: Enable server-side encryption │
│ ✓ Azure: Enable Storage Account encryption │
│ ✓ GCS: Enable default encryption │
│ │
│ 2. Access Control │
│ ✓ Use IAM policies to restrict access │
│ ✓ Principle of least privilege │
│ ✓ Enable versioning for audit trail │
│ │
│ 3. Sensitive Data │
│ ✓ Use -var for sensitive values │
│ ✓ Use sensitive variables in outputs │
│ ✓ Consider using Vault for secrets │
│ │
│ 4. State Files Contain: │
│ ✗ Resource IDs │
│ ✗ Configuration values │
│ ✗ Potentially: secrets passed as variables │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

Workspaces allow you to manage multiple environments from the same configuration:

┌─────────────────────────────────────────────────────────────────────────────┐
│ Terraform Workspaces │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Workspace Structure │ │
│ │ │ │
│ │ terraform.workspace │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ default │ │ staging │ │ prod │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ │ │ │
│ │ Each workspace = separate state file │ │
│ │ └─ s3://bucket/staging/terraform.tfstate │ │
│ │ └─ s3://bucket/prod/terraform.tfstate │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ Use cases: │
│ ✓ Multiple environments (dev, staging, prod) │
│ ✓ Feature branches │
│ ✓ Isolated infrastructure │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Terminal window
# List workspaces
terraform workspace list
# Create workspace
terraform workspace new staging
# Select workspace
terraform workspace select staging
# Show current workspace
terraform workspace show
# Delete workspace (must be non-current)
terraform workspace delete staging
# Workspace in configuration
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
# Use workspace in resource naming
tags = {
Name = "web-${terraform.workspace}"
}
# Conditional based on workspace
count = terraform.workspace == "prod" ? 3 : 1
}
Terminal window
# Import existing AWS VPC into Terraform state
terraform import aws_vpc.main vpc-0abc123
# Import into specific module
terraform import module.vpc.aws_vpc.main vpc-0abc123
# Import into workspace
terraform workspace select staging && terraform import aws_vpc.main vpc-0abc123
# In Terraform 1.5+
import {
to = aws_vpc.main
id = "vpc-0abc123def456"
}

In this chapter, you learned:

  • What is State: How Terraform tracks resources
  • Local State: Default file-based storage
  • Remote Backends: S3, Azure, GCS, Terraform Cloud
  • State Locking: Preventing concurrent modifications
  • State Security: Encryption, access control
  • Workspaces: Managing multiple environments
  • Import: Bringing existing resources under Terraform management