Skip to content

Terraform State Management: Remote Storage, Locking & Migration

DodaTech 5 min read

Terraform state management is the practice of storing, locking, migrating, and securing Terraform state files to enable team collaboration, prevent corruption, and maintain infrastructure reliability at scale.

What You'll Learn

In this tutorial, you will learn advanced Terraform state management patterns including remote backend configuration, DynamoDB state locking, state migration strategies, partial state recovery, and state file security.

Why It Matters

State is Terraform's source of truth. A corrupted or conflicting state file can cause duplicate resources, orphaned infrastructure, and lengthy recovery efforts. Teams using remote state with locking eliminate these risks and enable concurrent collaboration.

Real-World Use

DodaTech manages over 30 Terraform state files across dev, staging, and production environments for Durga Antivirus Pro. Each state file is stored in an encrypted S3 bucket with DynamoDB locking, and state history is retained for compliance auditing.

Remote State Backend Configuration

graph TD
    A[Terraform Configuration] --> B[terraform init]
    B --> C[S3 Backend]
    C --> D[dodatech-terraform-state bucket]
    D --> E[env:/production/network/terraform.tfstate]
    C --> F[DynamoDB Lock Table]
    F --> G[Lock ID: 2f8a1b3c...]
    G --> H[Acquired by: CI Runner #47]
    G --> I[Expires: 120s]
    style C fill:#4a90d9,color:#fff
    style F fill:#ff9900,color:#fff

S3 Backend with DynamoDB Locking

# backend-config.hcl
bucket         = "dodatech-terraform-state"
key            = "production/network/terraform.tfstate"
region         = "us-east-1"
encrypt        = true
kms_key_id     = "arn:aws:kms:us-east-1:123456789012:key/abc-123"
dynamodb_table = "terraform-state-locks"
terraform init -backend-config=backend-config.hcl

Expected output:

Initializing the backend...
Successfully configured the backend "s3"! Terraform will automatically
use this backend for state operations.

DynamoDB Lock Table Creation

# locks.tf
resource "aws_dynamodb_table" "terraform_locks" {
  name         = "terraform-state-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"

  attribute {
    name = "LockID"
    type = "S"
  }

  tags = {
    Name        = "Terraform State Lock Table"
    Environment = "Global"
    ManagedBy   = "Terraform"
  }
}
terraform apply -target=aws_dynamodb_table.terraform_locks -auto-approve

Expected output:

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

State Migration Strategies

Migrating from Local to Remote State

# Backup existing state
cp terraform.tfstate terraform.tfstate.backup

# Configure backend and migrate
terraform init -migrate-state

Expected output:

Do you want to copy existing state to the new backend?
  Pre-existing state was found while migrating the previous "local" backend.
  Do you want to copy this state to the new "s3" backend?
  Enter "yes" to copy, "no" to start with empty state.

  Enter a value: yes

State File Splitting

When splitting a monolithic state into service-specific states, use <a href="/devops/terraform/">terraform</a> state mv:

# Move resources from monolithic state to service-specific state
terraform state mv -state-out=network.tfstate \
  aws_vpc.main aws_vpc.main

terraform state mv -state-out=database.tfstate \
  aws_db_instance.main aws_db_instance.main

Expected output:

Move "aws_vpc.main" to "aws_vpc.main"
Successfully moved 1 object(s).

State Recovery Procedures

Force Unlock

When a state lock is stuck due to a crashed process:

# Get the lock ID
terraform force-unlock -force LOCK_ID

Expected output:

Terraform state has been successfully unlocked!

State Backup and Restore

# Automated backup script
#!/bin/bash
BUCKET="dodatech-terraform-state"
KEY="production/terraform.tfstate"
BACKUP_KEY="backups/$(date +%Y%m%d-%H%M%S)/terraform.tfstate"

aws s3 cp s3://${BUCKET}/${KEY} s3://${BUCKET}/${BACKUP_KEY}
echo "Backup created at s3://${BUCKET}/${BACKUP_KEY}"

Expected output:

copy: s3://dodatech-terraform-state/production/terraform.tfstate to s3://dodatech-terraform-state/backups/20260623-143000/terraform.tfstate

Partial State Recovery with Terraform Import

When state is lost but resources still exist:

# import.tf
resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
}
terraform import aws_instance.web i-0abcd1234efgh5678

Expected output:

aws_instance.web: Importing from ID "i-0abcd1234efgh5678"...
aws_instance.web: Import prepared!
  Prepared aws_instance for import
aws_instance.web: Refreshing state... [id=i-0abcd1234efgh5678]

Import successful!

State File Security

Encryption at Rest

# State backend with KMS encryption
terraform {
  backend "s3" {
    bucket         = "dodatech-terraform-state"
    key            = "production/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    kms_key_id     = "arn:aws:kms:us-east-1:123456789012:key/abc-123"
  }
}

Bucket Policy for State Protection

resource "aws_s3_bucket_policy" "state_protection" {
  bucket = aws_s3_bucket.terraform_state.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect    = "Deny]
        Principal = "*"
        Action    = "s3:DeleteObject"
        Resource  = "${aws_s3_bucket.terraform_state.arn}/*"
      }
    ]
  })
}

Common Mistakes

1. Storing State in Git

State files contain sensitive data like resource IDs and plaintext secrets. Never commit them. Use .gitignore with *.tfstate*.

2. Forgetting State Locking

Without locking, concurrent applies corrupt the state. Always configure DynamoDB locking with S3 backends.

3. Manual State Edits

Editing <a href="/devops/terraform/">terraform</a>.tfstate directly breaks Terraform's internal structure. Always use <a href="/devops/terraform/">terraform</a> state subcommands.

4. No State Backups

Without backups, a corrupted state file requires importing every resource manually. Schedule automated backups.

5. Sharing One State Across Environments

A single state for dev and production means a bad apply affects both. Isolate state per environment and service.

Practice Questions

1. What components are needed for Terraform remote state with locking? An S3 bucket for state storage and a DynamoDB table for locking, configured in the backend "s3" block.

2. How do you safely migrate from local to remote state? Run <a href="/devops/terraform/">terraform</a> init -migrate-state, which copies the local state to the remote backend with a confirmation prompt.

3. What is the purpose of <a href="/devops/terraform/">terraform</a> state mv? To move resources within or between state files without destroying and recreating infrastructure.

4. Challenge: Create an S3 bucket with versioning and a DynamoDB lock table. Configure a remote backend, apply a resource, simulate a lock by running concurrent applies, then verify state versioning in S3.

Mini Project: Production State Infrastructure

Provision an S3 bucket with versioning enabled, server-side encryption, and a deny-delete bucket policy. Create a DynamoDB table with PAY_PER_REQUEST billing and LockID hash key. Configure the backend, migrate existing local state, verify locking, and set up a daily backup script.

Remote State with S3 & DynamoDB
Terraform Security

What's Next

Master Terraform state management for your infrastructure, then implement Remote State as your production backend. Study DevOps practices for infrastructure automation.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro