Using S3 with DynamoDB State Locking in Terraform
Learn how to configure an S3 backend with DynamoDB locking to safely store Terraform state and prevent concurrent modifications.
14 Jul 2025, 06:35 UTC

Why Use a Remote Backend with State Locking
When multiple people or CI pipelines run Terraform against the same configuration, the .tfstate file can be overwritten, causing lost changes or inconsistent infrastructure. A remote backend stores the state file in a separate service and a lock mechanism prevents two runs from writing at the same time.
Prerequisites
- An AWS account with permission to create S3 buckets and DynamoDB tables.
- Terraform version 0.12 or newer (the -reconfigure flag is required for backend changes).
- An IAM user or role that will run Terraform, with policies allowing S3 and DynamoDB actions (see example below).
Step‑by‑Step Setup
- Create an S3 bucket for the state file.
aws s3api create-bucket --bucket my-terraform-state --region us-east-1 - Create a DynamoDB table named terraform-locks with a STRING primary key and TTL disabled (TTL = 0).
aws dynamodb create-table --table-name terraform-locks --attribute-definitions AttributeName=LockID,AttributeType=S --key-schema AttributeName=LockID,KeyType=HASH --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 - Attach an IAM policy to the executing identity. Example policy JSON:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject" ], "Resource": "arn:aws:s3:::my-terraform-state/*" }, { "Effect": "Allow", "Action": [ "dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:DeleteItem" ], "Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/terraform-locks" } ] } - Write the backend block in your Terraform configuration (e.g., backend.tf):
terraform { backend "s3" { bucket = "my-terraform-state" key = "prod/terraform.tfstate" region = "us-east-1" dynamodb_table = "terraform-locks" encrypt = true } } - Run
terraform init -reconfigureto force Terraform to read the new backend and create the lock table if it does not exist.
terraform init -reconfigure - Perform a plan to verify that no lock errors appear.
terraform plan - Apply the changes.
terraform apply
Verification and Recovery
After apply finishes, check the DynamoDB table. A new item with the current Terraform run ID should appear and then disappear.
- Run
aws dynamodb scan --table-name terraform-locksand look for an item whoseLockIDmatches the run ID shown in the Terraform logs. - To test concurrent execution, open a second terminal and run
terraform applywhile the first command is still processing. The second command should wait until the lock is released, confirming that the lock works.
If a lock becomes stale (for example, a process crashes and does not release the lock), you can manually delete the lock item using the AWS CLI, provided no active Terraform runs are using the same workspace:
aws dynamodb delete-item
--table-name terraform-locks
--key '{"LockID":{"S":""}}'
Limitations and When to Re‑run
- Older Terraform versions (<0.12) do not support
-reconfigure, so changing the backend requires a manualterraform initwithout the flag, which may leave the old backend configuration active. - If the IAM policy is missing
dynamodb:GetItemors3:GetObject, Terraform will fail to acquire the lock and state corruption can occur. - Workspaces that share the same backend but have different key values can still conflict if the lock implementation is mis‑configured; always verify that each workspace uses a unique state file name.
To confirm that the lock is active, run a plan while monitoring the DynamoDB table; the second concurrent apply should pause until the first completes, and the lock item should be present only during the active run.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.