Skip to main content

S3 Backend

The S3 backend stores state as a given key in a given S3 bucket on Amazon Web Services (AWS). This backend supports state locking and consistency checking via DynamoDB.

Implementation

Location: /internal/backend/remote-state/s3/backend.go

Use Cases

  • Team collaboration on AWS infrastructure
  • Production environments requiring state locking
  • Integration with AWS-based CI/CD pipelines
  • Compliance requirements for data residency

Basic Configuration

terraform {
  backend "s3" {
    bucket = "my-terraform-state"
    key    = "prod/terraform.tfstate"
    region = "us-east-1"
  }
}

Required Configuration

bucket

  • Type: String
  • Required: Yes
  • Description: The name of the S3 bucket

key

  • Type: String
  • Required: Yes
  • Description: The path to the state file inside the bucket
The key must not:
  • Be empty
  • Contain // (double slashes)
  • Start or end with /

region

  • Type: String
  • Optional: Yes (if environment variables are set)
  • Environment Variables: AWS_REGION, AWS_DEFAULT_REGION
  • Description: AWS region of the S3 bucket and DynamoDB table

Authentication

The S3 backend supports multiple authentication methods:

Static Credentials

terraform {
  backend "s3" {
    bucket     = "my-terraform-state"
    key        = "terraform.tfstate"
    region     = "us-east-1"
    access_key = "AKIAIOSFODNN7EXAMPLE"
    secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
  }
}
Environment Variables:
  • Access Key: AWS_ACCESS_KEY_ID
  • Secret Key: AWS_SECRET_ACCESS_KEY

Shared Credentials File

terraform {
  backend "s3" {
    bucket  = "my-terraform-state"
    key     = "terraform.tfstate"
    region  = "us-east-1"
    profile = "production"
    shared_credentials_files = ["/path/to/credentials"]
  }
}
Environment Variables:
  • AWS_SHARED_CREDENTIALS_FILE
  • AWS_SHARED_CONFIG_FILE

IAM Role Assumption

terraform {
  backend "s3" {
    bucket = "my-terraform-state"
    key    = "terraform.tfstate"
    region = "us-east-1"
    
    assume_role {
      role_arn     = "arn:aws:iam::123456789012:role/TerraformRole"
      session_name = "terraform"
      external_id  = "EXTERNAL_ID"
    }
  }
}
Assume Role Options:
  • role_arn (Required) - The ARN of the IAM role to assume
  • session_name (Optional) - The session name to use
  • duration (Optional) - Duration between 15m and 12h (default: 1h)
  • external_id (Optional) - External ID for the role
  • policy (Optional) - IAM policy JSON to restrict permissions
  • policy_arns (Optional) - Set of policy ARNs to attach
  • tags (Optional) - Map of session tags
  • transitive_tag_keys (Optional) - Set of tag keys to pass to subsequent sessions

Web Identity Token

terraform {
  backend "s3" {
    bucket = "my-terraform-state"
    key    = "terraform.tfstate"
    region = "us-east-1"
    
    assume_role_with_web_identity {
      role_arn                = "arn:aws:iam::123456789012:role/WebIdentityRole"
      session_name            = "terraform"
      web_identity_token_file = "/path/to/token"
    }
  }
}

Encryption

Server-Side Encryption (SSE-S3)

terraform {
  backend "s3" {
    bucket  = "my-terraform-state"
    key     = "terraform.tfstate"
    region  = "us-east-1"
    encrypt = true
  }
}

KMS Encryption (SSE-KMS)

terraform {
  backend "s3" {
    bucket     = "my-terraform-state"
    key        = "terraform.tfstate"
    region     = "us-east-1"
    encrypt    = true
    kms_key_id = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
  }
}

Customer-Provided Encryption (SSE-C)

terraform {
  backend "s3" {
    bucket           = "my-terraform-state"
    key              = "terraform.tfstate"
    region           = "us-east-1"
    sse_customer_key = "<base64-encoded-32-byte-key>"
  }
}
Environment Variable: AWS_SSE_CUSTOMER_KEY Note: You cannot use both kms_key_id and sse_customer_key simultaneously.

State Locking

DynamoDB Locking (Deprecated)

terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-lock"
  }
}
Note: The dynamodb_table attribute is deprecated in favor of use_lockfile.
terraform {
  backend "s3" {
    bucket       = "my-terraform-state"
    key          = "terraform.tfstate"
    region       = "us-east-1"
    use_lockfile = true
  }
}

Workspaces

The S3 backend supports workspaces using a key prefix:
terraform {
  backend "s3" {
    bucket = "my-terraform-state"
    key    = "terraform.tfstate"
    region = "us-east-1"
    workspace_key_prefix = "env"
  }
}
Default prefix: "env:" With workspace production, the state is stored at:
env/production/terraform.tfstate

Advanced Configuration

Custom Endpoints

terraform {
  backend "s3" {
    bucket = "my-terraform-state"
    key    = "terraform.tfstate"
    region = "us-east-1"
    
    endpoints {
      s3       = "https://s3.custom.endpoint"
      dynamodb = "https://dynamodb.custom.endpoint"
      sts      = "https://sts.custom.endpoint"
      iam      = "https://iam.custom.endpoint"
    }
  }
}

Path-Style URLs

terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "terraform.tfstate"
    region         = "us-east-1"
    use_path_style = true
  }
}
Uses https://s3.amazonaws.com/bucket/key instead of https://bucket.s3.amazonaws.com/key.

Skip Validations

terraform {
  backend "s3" {
    bucket                      = "my-terraform-state"
    key                         = "terraform.tfstate"
    region                      = "us-east-1"
    skip_credentials_validation = true
    skip_region_validation      = true
    skip_requesting_account_id  = true
    skip_metadata_api_check     = true
  }
}
Useful for S3-compatible APIs that don’t support all AWS API features.

ACL Configuration

terraform {
  backend "s3" {
    bucket = "my-terraform-state"
    key    = "terraform.tfstate"
    region = "us-east-1"
    acl    = "bucket-owner-full-control"
  }
}

All Configuration Options

OptionTypeRequiredDescription
bucketstringYesS3 bucket name
keystringYesPath to state file in bucket
regionstringYes*AWS region
access_keystringNoAWS access key
secret_keystringNoAWS secret key
tokenstringNoMFA token
profilestringNoAWS profile name
shared_credentials_fileslist(string)NoPaths to credentials files
encryptboolNoEnable server-side encryption
kms_key_idstringNoKMS key ARN
sse_customer_keystringNoCustomer-provided encryption key
aclstringNoCanned ACL for state file
dynamodb_tablestringNoDynamoDB table for locking (deprecated)
use_lockfileboolNoUse lock file for state locking
workspace_key_prefixstringNoPrefix for workspace state paths
skip_credentials_validationboolNoSkip credentials validation
skip_region_validationboolNoSkip region validation
skip_s3_checksumboolNoSkip S3 checksum validation
use_path_styleboolNoUse path-style S3 URLs
use_fips_endpointboolNoUse FIPS endpoints
use_dualstack_endpointboolNoUse dual-stack endpoints

Example: Complete Configuration

terraform {
  backend "s3" {
    bucket = "company-terraform-state"
    key    = "production/network/terraform.tfstate"
    region = "us-west-2"
    
    encrypt        = true
    kms_key_id     = "arn:aws:kms:us-west-2:123456789012:key/12345678-1234-1234-1234-123456789012"
    use_lockfile   = true
    acl            = "bucket-owner-full-control"
    
    workspace_key_prefix = "workspaces"
    
    assume_role {
      role_arn     = "arn:aws:iam::123456789012:role/TerraformRole"
      session_name = "terraform-prod"
    }
  }
}

Build docs developers (and LLMs) love