Remote Terraform State File (with Locking) Using AWS S3 and DynamoDB

Using a remote backend to manage the Terraform state file provides a few advantages, primarily:

  • State file versioning
  • Removing sensitive data and state file merges in VCS
  • Consistency checking
  • Locking to prevent simultaneous resource creation

S3 Bucket and DynamoDB Resources

# resources.tf

# Create the S3 bucket
resource "aws_s3_bucket" "tf_state" {
bucket = "tf-state-bucket-name"

lifecycle {
prevent_destroy = true
}

tags = {
Name = "Terraform Remote State"
}
}

# Enable bucket versioning
resource "aws_s3_bucket_versioning" "tf_state_versioning" {
bucket = aws_s3_bucket.tf_state.id

versioning_configuration {
status = "Enabled"
}
}

# Enable bucket server-side encryption
resource "aws_s3_bucket_server_side_encryption_configuration" "tf_state_sse" {
bucket = aws_s3_bucket.tf_state.id

rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}

# Create the DyanmoDB table
resource "aws_dynamodb_table" "tf_state_lock" {
name = "TerraformStateLock"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"

attribute {
name = "LockID"
type = "S"
}

tags = {
Name = "dynamodb-tf-state-lock"
}
}

Create the resources with terraform plan and terraform apply.

S3 Backend

Add the S3 backend to the terraform configuration block:

# terraform.tf
terraform {
required_version = "~> 1.1.0"

backend "s3" {
bucket = "tf-state-bucket-name"
key = "production/terraform.tfstate"
encrypt = "true"
region = "eu-west-2"
dynamodb_table = "TerraformStateLock"
}

# ...
}

Run terraform init to re-initialise the backend and copy any existing state to S3. See S3 Backend for more information.