DevOpsil
Terraform
85%
Needs Review

Fix Terraform 'Error Acquiring the State Lock' with DynamoDB

Zara BlackwoodZara Blackwood3 min read

The Error: Error Acquiring the State Lock

Terraform fails before even starting a plan:

Error: Error acquiring the state lock

Error message: AccessDeniedException: User: arn:aws:iam::123456789012:user/deploy
is not authorized to perform: dynamodb:PutItem on resource:
arn:aws:dynamodb:us-east-1:123456789012:table/terraform-locks

Or the table does not exist:

Error: Error acquiring the state lock

Error message: ResourceNotFoundException: Requested resource not found:
Table: terraform-locks not found

Unlike a stuck lock (where the lock exists but is orphaned), this error means Terraform cannot interact with the locking mechanism at all.

Root Cause

Terraform's S3 backend uses DynamoDB for state locking. The lock acquisition fails when:

  1. DynamoDB table does not exist — it was never created or was deleted
  2. IAM permissions are insufficient — the executing role lacks dynamodb:PutItem, GetItem, DeleteItem
  3. Wrong region or table name — backend config points to a non-existent table
  4. DynamoDB is throttling — the table's provisioned capacity is exceeded

Step-by-Step Fix

1. Verify the DynamoDB table exists

aws dynamodb describe-table \
  --table-name terraform-locks \
  --region us-east-1

If you get ResourceNotFoundException, create the table:

aws dynamodb create-table \
  --table-name terraform-locks \
  --attribute-definitions AttributeName=LockID,AttributeType=S \
  --key-schema AttributeName=LockID,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST \
  --region us-east-1

The table only needs one attribute: LockID (String), used as the partition key.

2. Check your backend configuration

In your Terraform backend block, verify the table name and region match:

terraform {
  backend "s3" {
    bucket         = "my-tf-state"
    key            = "prod/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-locks"   # Must match exactly
    encrypt        = true
  }
}

Common mistakes: wrong region, typo in table name, or using a different AWS account.

3. Fix IAM permissions

The IAM role or user running Terraform needs these DynamoDB actions:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "dynamodb:GetItem",
        "dynamodb:PutItem",
        "dynamodb:DeleteItem",
        "dynamodb:DescribeTable"
      ],
      "Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/terraform-locks"
    }
  ]
}

Apply the policy:

aws iam put-user-policy \
  --user-name deploy \
  --policy-name terraform-lock-access \
  --policy-document file://policy.json

4. Check for DynamoDB throttling

If you are using provisioned capacity and see ProvisionedThroughputExceededException:

# Check table metrics
aws cloudwatch get-metric-statistics \
  --namespace AWS/DynamoDB \
  --metric-name ThrottledRequests \
  --dimensions Name=TableName,Value=terraform-locks \
  --start-time 2026-03-30T00:00:00Z \
  --end-time 2026-03-30T23:59:59Z \
  --period 3600 \
  --statistics Sum

# Switch to on-demand billing
aws dynamodb update-table \
  --table-name terraform-locks \
  --billing-mode PAY_PER_REQUEST \
  --region us-east-1

5. Test lock acquisition manually

# Attempt to write a test item
aws dynamodb put-item \
  --table-name terraform-locks \
  --item '{"LockID": {"S": "test-lock"}}' \
  --region us-east-1

# Clean up
aws dynamodb delete-item \
  --table-name terraform-locks \
  --key '{"LockID": {"S": "test-lock"}}' \
  --region us-east-1

If this succeeds, the issue is in Terraform's backend config. If it fails, it is an IAM or network issue.

6. Bypass locking temporarily (emergency only)

terraform plan -lock=false
terraform apply -lock=false

This is dangerous in teams — only use when you are certain no one else is running Terraform.

Prevention Tips

  • Create the DynamoDB table with your backend S3 bucket — use a bootstrap script or a separate Terraform root module for backend resources.
  • Use PAY_PER_REQUEST billing on the lock table — it costs nearly nothing and eliminates throttling.
  • Include DynamoDB permissions in your CI role's base policy alongside S3 state bucket access.
  • Tag the lock table so it does not get accidentally deleted during cleanup sweeps.
  • Use terraform init in CI before every plan/apply to catch backend misconfigurations early.
Share:

Was this article helpful?

Zara Blackwood
Zara Blackwood

Platform Engineer

Terraform enthusiast, platform builder, DRY advocate. I believe infrastructure should be versioned, reviewed, and deployed like any other code. GitOps or bust.

Related Articles

TerraformQuick RefBeginnerNeeds Review

Terraform CLI: Cheat Sheet

Terraform CLI cheat sheet with commands organized by workflow — init, plan, apply, destroy, state manipulation, imports, and workspace management.

Zara Blackwood·
3 min read

More in Terraform

View all →

Discussion