When working with Terraform, managing your state file is critical. By default, Terraform stores this state file (terraform.tfstate
) locally on your machine. While that’s fine for small projects or individual development, it’s not ideal for collaboration or scalable infrastructure. That’s where remote backends like Amazon S3 come in.
In this post, you’ll learn how to configure an S3 bucket as a remote backend for your Terraform project, optionally using DynamoDB for state locking and consistency.
Why Use an S3 Backend?
Using Amazon S3 as a backend offers several advantages:
- ✅ Remote, centralized storage of state files
- ✅ Enables collaboration across teams
- ✅ Supports state locking (with DynamoDB)
- ✅ Versioning support for rollback
- ✅ Secure and durable storage
🧱 Prerequisites
Make sure you have:
- An AWS account
- IAM permissions to create S3 buckets and DynamoDB tables
- AWS CLI configured (
aws configure
) - Terraform installed (
terraform -v
)
Step 1 – Bootsrap Initial Terraform Resources (S3 & DynamoDb)
Initialize a terraform project with default local backend (no remote backend) and create s3 bucket and dynamo db which are the two resources needed for terrafrom s3 backend.
resource "aws_s3_bucket" "terraform_state" {
bucket = "devops-terraform-state-009"
force_destroy = true
}
This creates a bucket that will be used to store terraform state files, allowing team collaboration and state management.
This bucket will be used to store your Terraform state files, allowing team collaboration and state management.
force_destroy is set to so that when the bucket is deleted via Terraform, it will delete all the objects in it first (otherwise you’d need to empty the bucket manually before deleting it).
resource "aws_s3_bucket_versioning" "bucket_versioning" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration {
status = "Enabled"
}
}
Enables versioning on the bucket. Allowing terraform to keep a history of all changes to the Terraform state file, and recover previous versions if needed.
resource "aws_s3_bucket_server_side_encryption_configuration" "bucket_configuration" {
bucket = aws_s3_bucket.terraform_state.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
Configures encryption at rest for the bucket using AES-256.
Ensures any data (like state files) written to the bucket is automatically encrypted.
resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform_state_loccking"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
}
Creates a DynamoDB table for state locking and consistency.
LockID
is the key used to track the lock.
billing_mode = "PAY_PER_REQUEST"
means you only pay for what you use—no provisioning needed.
This is used to prevent concurrent Terraform runs from corrupting the state file (e.g., two devs running terraform apply
at the same time).
Execute the following commands in sequence:
terraform apply
– Apply the planned changes to the infrastructure.
terraform init
– Initialize the Terraform configuration.
terraform plan
– Preview the changes Terraform will make.
This will create the s3 bucket and the DynamoDB table.
Step 2 – Switch to remote backend
Once this is complete. Update terraform to use s3 remote backend as follows:
backend “s3” {
bucket = “devops-terraform-state-009”
region = “us-east-1”
dynamodb_table = “terraform_state_locking”
encrypt = true
key = “tf-infra/terraform.tfstate”
profile = “terraform-user”
}
bucket = "devops-terraform-state-009"
Specifies the name of the S3 bucket where your Terraform state file will be stored. The bucket must exist before running terraform init
.
region = "us-east-1"
Defines the AWS region where the S3 bucket (and DynamoDB table) is located.
dynamodb_table = "terraform_state_locking"
Points to a DynamoDB table used for state locking and concurrency control. This prevents multiple people or processes from applying changes at the same time, which could corrupt the state.
encrypt = true
Enables server-side encryption for the state file stored in S3, helping secure sensitive information.
key = "tf-infra/terraform.tfstate"
This is the path within the S3 bucket where the state file will be saved. It acts like a file name and can be nested within folders (e.g., “tf-infra”).
profile = "terraform-user"
Specifies the AWS CLI profile to use for authentication. This refers to credentials configured in your ~/.aws/credentials
file under the [terraform-user]
section