Cloud  /  Terraform

IaC Terraform 50 guides · updated 2026

Infrastructure as code done right — providers, state, reusable modules, and the workflow patterns that keep multi-cloud deployments sane in 2026.

Terraform State Locking

State locking prevents concurrent Terraform operations from corrupting the state file. When two engineers run apply simultaneously, one gets the lock and runs; the other gets an error message and waits. Without locking, simultaneous applies can interleave writes and produce a corrupt, inconsistent state.


How Locking Works

  1. terraform plan or terraform apply starts → Terraform writes a lock record to the locking backend
  2. If the lock is already held → Terraform prints an error and exits
  3. Operation completes (or fails) → Terraform releases the lock

The lock contains metadata about who holds it:

{
"ID": "f7a8b2c1-1234-5678-abcd-9876543210ab",
"Operation": "OperationTypeApply",
"Info": "",
"Who": "alice@mycompany.com",
"Version": "1.9.5",
"Created": "2025-03-15T14:32:11.123456789Z",
"Path": "production/terraform.tfstate"
}

DynamoDB Locking (AWS S3 Backend)

DynamoDB stores the lock record as a row with hash key LockID:

terraform {
backend "s3" {
bucket = "mycompany-terraform-state"
key = "production/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-state-lock" # Enables locking
}
}
# Required DynamoDB table structure
resource "aws_dynamodb_table" "state_lock" {
name = "terraform-state-lock"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
tags = {
Purpose = "Terraform state locking"
}
}

When a lock is held, you’ll see in DynamoDB:

LockID: "mycompany-terraform-state/production/terraform.tfstate"
Info: {"ID":"f7a8b2c1-...","Operation":"OperationTypeApply","Who":"alice@mycompany.com",...}

GCS Locking (GCP Backend)

GCS uses native object locking — no separate resource needed:

terraform {
backend "gcs" {
bucket = "mycompany-terraform-state"
prefix = "production"
# Locking is automatic via GCS object conditional updates
}
}

Azure Blob Locking

Azure Blob Storage uses blob leases for locking — also automatic:

terraform {
backend "azurerm" {
resource_group_name = "terraform-state-rg"
storage_account_name = "mycompanytfstate"
container_name = "tfstate"
key = "production.terraform.tfstate"
# Locking is automatic via Blob Storage leases
}
}

When a Lock Gets Stuck

A lock becomes stuck when a Terraform process is interrupted before it can release the lock (e.g., killed terminal, lost network connection, CI runner timeout):

Error: Error locking state: Error acquiring the state lock: ConditionalCheckFailedException
Lock Info:
ID: f7a8b2c1-1234-5678-abcd-9876543210ab
Path: mycompany-terraform-state/production/terraform.tfstate
Operation: OperationTypeApply
Who: alice@mycompany.com
Version: 1.9.5
Created: 2025-03-15T14:32:11.123456789Z
Info:

Do not immediately force-unlock. First verify:

  1. Is the original Terraform process actually still running?
  2. Check CI/CD — is a pipeline job still in progress?
  3. Look at the Who and Created fields — is this lock recent?

Force Unlocking (Use Carefully)

Only force-unlock after confirming no Terraform process is actively running:

Terminal window
# Get the lock ID from the error message
terraform force-unlock f7a8b2c1-1234-5678-abcd-9876543210ab
# Confirmation prompt — type "yes"
Do you really want to force-unlock?
Terraform will remove the lock on the remote state.
This will allow local Terraform commands to modify this state, even though it
may still be in use.
Lock ID: f7a8b2c1-1234-5678-abcd-9876543210ab
Do you want to unlock? Only "yes" will be accepted to confirm.
Enter a value: yes

Force-unlock directly in DynamoDB when the CLI isn’t working:

Terminal window
# Delete the lock record directly from DynamoDB
aws dynamodb delete-item \
--table-name terraform-state-lock \
--key '{"LockID": {"S": "mycompany-terraform-state/production/terraform.tfstate"}}'

For operations where you’re sure no one else is running:

Terminal window
# Skip locking for a single operation
terraform apply -lock=false
# Skip locking with timeout override
terraform apply -lock-timeout=60s # Wait up to 60s for lock to clear

Never disable locking in CI/CD pipelines — the whole point is to serialize concurrent runs.


Best Practices for Team Locking

# In CI/CD: use a lock timeout instead of disabling locking
# If another pipeline is running, wait up to 5 minutes for it to finish
# terraform apply -lock-timeout=5m
# Split state by team/service to reduce lock contention
# networking team's state is independent of app team's state
backend "s3" {
key = "teams/networking/production/terraform.tfstate"
}

Design principles: