Introduction
When Terraform uses an S3 backend with DynamoDB locking, every plan and apply expects a specific table to exist before it can acquire a lock. If the table was never created, exists in the wrong region, or the active AWS identity cannot access it, Terraform fails before it even evaluates your resource changes.
Symptoms
terraform init,plan, orapplyfails withResourceNotFoundException- The backend points to S3 successfully, but state locking still fails
- The same configuration works for one engineer or CI environment but not another
- Teams assume the problem is a stale lock even though the lock table itself is missing
Common Causes
- The backend configuration references a DynamoDB table name that was never created
- The table exists in a different AWS region or account than the active credentials
- A bootstrap or migration step created the S3 bucket but skipped the lock table
- IAM permissions allow S3 state access but deny DynamoDB read and write operations
Step-by-Step Fix
- 1.Confirm the exact backend lock table and region Terraform is trying to use
- 2.Read the backend block or backend config file first. Do not create a random table name just because the error mentions DynamoDB.
terraform {
backend "s3" {
bucket = "company-terraform-state"
key = "prod/network/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-lock"
}
}- 1.Check whether the table exists in the active account and region
- 2.Run the query with the same profile and region your Terraform process uses. This catches the common case where the table exists, just not where this shell is looking.
aws dynamodb describe-table \
--table-name terraform-lock \
--region us-east-1- 1.Create the lock table if it truly does not exist
- 2.The table needs a single string hash key named
LockID. Nothing more is required for standard Terraform locking.
aws dynamodb create-table \
--table-name terraform-lock \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--region us-east-1- 1.Validate IAM permissions and reinitialize the backend
- 2.A missing table and an access denied path can look similar during rushed incident response. Confirm the caller can read and write the lock table, then re-run backend initialization cleanly.
terraform init -reconfigure
terraform planPrevention
- Provision the S3 backend bucket and DynamoDB lock table together in the same bootstrap workflow
- Keep backend region, table name, and account assumptions documented for every environment
- Give CI and local operators explicit DynamoDB permissions instead of relying on broad hidden inheritance
- Treat
force-unlockas a stale-lock fix, not as a replacement for a missing lock table