Sign in
Menu
Start free trial

AWS S3 bucket Terraform module

Terraform module which creates S3 bucket on AWS with all (or almost all) features provided by Terraform AWS provider.

SWUbanner

These features of S3 bucket configurations are supported:

  • static web-site hosting
  • access logging
  • versioning
  • CORS
  • lifecycle rules
  • server-side encryption
  • object locking
  • Cross-Region Replication (CRR)
  • ELB log delivery bucket policy
  • ALB/NLB log delivery bucket policy
  • WAF log delivery bucket policy
  • Account-level Public Access Block
  • S3 Directory Bucket
  • S3 Table Bucket
  • S3 Vectors

Usage

Private bucket with versioning enabled

module "s3_bucket" {
  source = "terraform-aws-modules/s3-bucket/aws"

  bucket = "my-s3-bucket"
  acl    = "private"

  control_object_ownership = true
  object_ownership         = "ObjectWriter"

  versioning = {
    enabled = true
  }
}

Bucket with ELB access log delivery policy attached

module "s3_bucket_for_logs" {
  source = "terraform-aws-modules/s3-bucket/aws"

  bucket = "my-s3-bucket-for-logs"
  acl    = "log-delivery-write"

  # Allow deletion of non-empty bucket
  force_destroy = true

  control_object_ownership = true
  object_ownership         = "ObjectWriter"

  attach_elb_log_delivery_policy = true
}

Bucket with ALB/NLB access log delivery policy attached

module "s3_bucket_for_logs" {
  source = "terraform-aws-modules/s3-bucket/aws"

  bucket = "my-s3-bucket-for-logs"

  # Allow deletion of non-empty bucket
  force_destroy = true

  control_object_ownership = true
  object_ownership         = "ObjectWriter"

  attach_lb_log_delivery_policy = true # Required for ALB/NLB logs
}

Bucket with WAF log delivery policy attached

module "s3_bucket_for_waf_logs" {
  source = "terraform-aws-modules/s3-bucket/aws"

  bucket = "my-s3-bucket-for-waf-logs"

  # Allow deletion of non-empty bucket
  force_destroy = true

  control_object_ownership = true
  object_ownership         = "ObjectWriter"

  attach_waf_log_delivery_policy = true  # Required for WAF logs
}

Bucket with a custom policy attached

When you need to attach a custom policy to the bucket, use the policy argument. The placeholders _S3_BUCKET_ID_, _S3_BUCKET_ARN_ and _AWS_ACCOUNT_ID_ are replaced with the real values when the policy is attached, which is what makes a policy work against a bucket whose name is generated from bucket_prefix.

module "s3_bucket" {
  source = "terraform-aws-modules/s3-bucket/aws"

  bucket_prefix = "my-s3-bucket-"

  attach_policy = true
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Sid       = "DenyInsecureTransport"
      Effect    = "Deny"
      Principal = "*"
      Action    = "s3:*"
      Resource  = ["_S3_BUCKET_ARN_", "_S3_BUCKET_ARN_/*"]
      Condition = {
        Bool = { "aws:SecureTransport" = "false" }
      }
    }]
  })
}

Conditional creation

Sometimes you need to have a way to create S3 resources conditionally but Terraform does not allow to use count inside module block, so the solution is to specify argument create_bucket.

# This S3 bucket will not be created
module "s3_bucket" {
  source = "terraform-aws-modules/s3-bucket/aws"

  create_bucket = false
  # ... omitted
}

Module wrappers

Users of this Terraform module can create multiple similar resources by using for_each meta-argument within module block which became available in Terraform 0.13.

Users of Terragrunt can achieve similar results by using modules provided in the wrappers directory, if they prefer to reduce amount of configuration files.

Known limitations (Terraform/OpenTofu, not this module)

A few requests come up again and again and cannot be implemented by this module, or by any module: Terraform requires lifecycle arguments to be literal values inside the resource block. hashicorp/terraform#18367 has been open since 2018, #22544 since 2019, and opentofu/opentofu#1329 is the same request for OpenTofu.

  • prevent_destroy cannot be set through a module input. Asked for in #83, #186, #203, #215, #283 and #407, and not something this module can offer: the lifecycle block has to be a literal inside the resource. Native options: an explicit Deny on s3:DeleteBucket in the bucket policy, or fork this module and add the block yourself.
  • ignore_changes cannot be set either (#21). A tag written by a scanner, a cost tool or an AWS Config rule is removed on the next apply and written back by the tool after that. Native options: move tag ownership into Terraform with provider default_tags, or fork this module and add ignore_changes = [tags, tags_all].

Compliance.tf serves this module with these rules applied at download time, on top of whatever your organization already has enabled there. Inputs and outputs do not change; the source line does. Drop the version argument and pin the release you use by adding &version= and that release number to the URL. To get started, register a free compliance.tf account and configure an access token:

source = "https://registry.compliance.tf/terraform-aws-modules/s3-bucket/aws?add_rules=lifecycle_ignore_tags,lifecycle_prevent_destroy_data"

The full workaround for each item above, and the exact diff each rule makes, are in the compliance.tf docs for this module. To preview a diff without an account, open this module in the Rules Playground.

Disclosure: written by this module's maintainer, who also builds compliance.tf.

Examples

Authors

Module is maintained by Anton Babenko with help from these awesome contributors.

License

Apache 2 Licensed. See LICENSE for full details.

Additional information for users from Russia and Belarus