Terraform & IaC

The Single Source of Truth: State & S3 Backend

Lesson 9 of 14 · 11 min · Advanced

Terraform state (terraform.tfstate) maps your declarative configuration to the actual resources in your cloud provider. For teams and automated CI/CD pipelines, storing state securely on a remote backend like AWS S3 with DynamoDB distributed locking is non-negotiable.

Why Terraform Requires a State File

When you declare resource "aws_instance" "web", AWS returns an instance ID like i-0a1b2c3d4e5f67890. The next time you run terraform apply, Terraform needs to know whether to update the existing instance or create a brand new one. State provides that essential mapping.

4 Core Jobs of the State File

  • Resource Mapping: Maps HCL identifiers to cloud API IDs.
  • Metadata Tracking: Tracks resource dependencies to calculate parallel execution order.
  • Performance Caching: Stores known attributes to minimize expensive API round-trips.
  • Sync & Drift Detection: Compares current code vs real cloud state to generate execution plans.

Never Commit State to Git

1. State files contain plain-text passwords and sensitive attributes.
2. Two engineers committing conflicting state files creates catastrophic Git merge conflicts.
3. Git lacks write-locks during active applies.

Inside terraform.tfstate

terraform.tfstate
{
  "version": 4,
  "terraform_version": "1.6.0",
  "serial": 14,
  "lineage": "c498321a-9812-40ae-bf71-098246a781b2",
  "resources": [
    {
      "mode": "managed",
      "type": "aws_instance",
      "name": "web",
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      "instances": [
        {
          "attributes": {
            "id": "i-0a1b2c3d4e5f67890",
            "ami": "ami-0123456789abcdef0",
            "public_ip": "13.235.45.109"
          }
        }
      ]
    }
  ]
}

Serial Counter

Every time state is modified, Terraform increments the serial number. If two developers try to write conflicting state versions, Terraform detects the serial mismatch and prevents corruption.

Lineage UUID

Unique project identifier ensuring you never accidentally apply state from one Terraform project over an entirely different project's state.

Setting Up AWS S3 Remote Backend with DynamoDB Locking

backend.tf
terraform {
  backend "s3" {
    bucket         = "shan-tfstate-ap-south-1"
    key            = "prod/app-stack/terraform.tfstate"
    region         = "ap-south-1"
    dynamodb_table = "terraform-state-locks"
    encrypt        = true
  }
}

Prerequisites to Create in AWS

  • S3 Bucket: Must have Versioning Enabled so you can roll back corrupted state, and SSE-S3 / KMS Encryption.
  • DynamoDB Table: Must be created with a primary partition key named LockID of type String.
terminal
# Run init to migrate existing local state to the newly configured S3 bucket
$ terraform init -migrate-state

Do you want to copy existing state to the new backend?
  Enter a value: yes
Successfully configured the backend "s3"! Terraform will now use this backend.

How DynamoDB State Locking Works

  • Developer A / CI/CD Runs terraform apply
  • →
  • Acquires Lock in DynamoDB Writes LockID entry
  • →
  • Developer B attempts Apply Denied: "State is locked"
  • →
  • Apply Finishes Releases LockID

Essential State CLI Commands

terminal
# 1. List all managed resources in state
$ terraform state list
aws_instance.web
aws_s3_bucket.app_data

# 2. Show full attributes of a resource
$ terraform state show aws_instance.web

# 3. Rename a resource without destroying it
$ terraform state mv aws_instance.web aws_instance.app_server

# 4. Remove a resource from state (keep alive in AWS)
$ terraform state rm aws_s3_bucket.app_data

terraform state mv

If you rename a block from resource "aws_instance" "web" to resource "aws_instance" "app", Terraform normally destroys and recreates it. Using state mv updates the state index without deleting the live server.

terraform state rm

Removes the resource from Terraform's management without deleting the actual cloud resource in AWS.

State Security & Disaster Recovery

Best PracticeWhy It Matters
Enable S3 Bucket VersioningAllows instant restoration if a corrupted state file is uploaded or an accidental destroy occurs.
Strict IAM Least PrivilegeOnly CI/CD execution roles and Senior DevOps engineers should have write access to the state bucket.
Enable Server-Side Encryption (SSE-KMS)Protects database passwords, TLS private keys, and user-data secrets stored inside the state file.
Add *.tfstate to .gitignorePrevents accidental commits of local state test files.