Terraform State and Modules

advanced terraform state modules remote-backend

When Terraform creates infrastructure, it needs to remember what it created. Otherwise, how would it know what to update or destroy next time? That memory is called state.

What Is State?

State is a JSON file (terraform.tfstate) that maps our Terraform configuration to real-world resources. When we write resource "aws_s3_bucket" "assets", Terraform stores the actual bucket ID, ARN, and all its properties in state.

Without state, Terraform would have no idea what already exists. It would try to create everything from scratch every time.

Local vs Remote State

By default, state lives in a local file. That’s fine for solo experimentation, but terrible for teams. If two people run terraform apply at the same time with different local state files, things get destroyed or duplicated.

Remote state solves this. We store state in a shared location — usually an S3 bucket with a DynamoDB table for locking.

# Store state remotely in S3
terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "prod/infrastructure.tfstate"
    region         = "ap-south-1"
    dynamodb_table = "terraform-locks"  # prevents concurrent writes
    encrypt        = true
  }
}

Now everyone on the team reads and writes to the same state. No conflicts.

State Locking

When someone runs terraform apply, the state gets locked. This prevents another person from making changes at the same time. Think of it like a database transaction lock — only one writer at a time.

The DynamoDB table in the example above handles this. If we try to run apply while someone else is already running it, Terraform says “state is locked” and waits.

Why State Matters

  • Drift detection — Terraform compares state with what actually exists. If someone manually changed a resource in the console, terraform plan catches the difference.
  • Dependency tracking — state records relationships between resources. Terraform knows it needs to create the VPC before the subnet.
  • Performance — instead of querying every resource from the cloud API, Terraform reads state to know what exists.

Useful State Commands

# List all resources Terraform is tracking
terraform state list

# Show details of a specific resource
terraform state show aws_s3_bucket.assets

# Remove a resource from state (Terraform forgets it, doesn't delete it)
terraform state rm aws_s3_bucket.legacy

# Move a resource (renamed something in code?)
terraform state mv aws_s3_bucket.old aws_s3_bucket.new

Workspaces

Workspaces let us manage multiple environments (dev, staging, prod) with the same code but separate state files:

terraform workspace new staging
terraform workspace new production
terraform workspace select staging
terraform workspace list

Each workspace gets its own state. We can use terraform.workspace in our config to change behavior per environment.

Modules

As our infrastructure grows, copy-pasting resource blocks is painful. Modules are reusable packages of Terraform configuration. Think of them like functions — we define them once and call them with different inputs.

A module is just a directory with .tf files:

modules/
  s3-bucket/
    main.tf         # resource definitions
    variables.tf    # input parameters
    outputs.tf      # exposed values
# modules/s3-bucket/variables.tf
variable "bucket_name" {
  type = string
}

variable "environment" {
  type    = string
  default = "dev"
}
# modules/s3-bucket/main.tf
resource "aws_s3_bucket" "this" {
  bucket = var.bucket_name
  tags = {
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}
# modules/s3-bucket/outputs.tf
output "bucket_arn" {
  value = aws_s3_bucket.this.arn
}

Now we call it from our root config:

# Use our module for different buckets
module "app_assets" {
  source      = "./modules/s3-bucket"
  bucket_name = "my-app-assets"
  environment = "production"
}

module "logs" {
  source      = "./modules/s3-bucket"
  bucket_name = "my-app-logs"
  environment = "production"
}

We can also use modules from the Terraform Registry — thousands of community-maintained modules for common patterns.

In simple language, state is Terraform’s memory of what it built, remote backends make that memory shared and safe, and modules let us stop repeating ourselves. These three concepts are what separate a messy Terraform project from a clean one.