AWS S3 bucket Terraform module
Terraform module which creates S3 bucket on AWS with all (or almost all) features provided by Terraform AWS provider.
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)
- Elastic Load Balancing and AWS WAF log delivery bucket policies
- file system access to bucket data with Amazon S3 Files
- Account-level Public Access Block
Besides general purpose buckets, this repository manages these S3 bucket types:
- S3 Directory Bucket, created by the root module when
is_directory_bucket = true - S3 Table Bucket, through the
table-bucketsub-module - S3 Vectors, through the
vectorssub-module
See the respective sub-module README.md for details on how to use:
account-public-access- the account-level Public Access Block, which applies to every bucket in the accountfile-system- an Amazon S3 Files file system on a bucket this module does not managenotification- bucket notifications to Lambda, SQS, SNS and EventBridgeobject- objects placed in a buckettable-bucket- table buckets and the Iceberg tables in themvectors- vector buckets and vector indexes
Usage
See the examples directory for working examples to reference.
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 log delivery policies attached
Each attach_*_log_delivery_policy flag adds the statements one service needs to write its logs to the bucket, and the module merges every enabled flag into a single bucket policy.
module "s3_bucket_for_logs" {
source = "terraform-aws-modules/s3-bucket/aws"
bucket = "aws-waf-logs-my-s3-bucket-for-logs"
# Application and Classic Load Balancer access logs
attach_elb_log_delivery_policy = true
# Network Load Balancer access logs
attach_lb_log_delivery_policy = true
# AWS WAF logs, which require a bucket name that starts with `aws-waf-logs-`
attach_waf_log_delivery_policy = true
}
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" }
}
}]
})
}
Bucket with an S3 file system
S3 Files requires versioning on the bucket. The module creates an IAM role for each file system and a security group shared by the mount targets. A principal listed in an access point's read_access_arns or read_write_access_arns can mount that file system only through the access points that list it.
To put a file system on a bucket this module does not manage, call modules/file-system directly. Its README covers the one thing that module asks of callers, which is to pass the bucket's versioning status as a reference to the versioning resource rather than as a literal.
module "s3_bucket" {
source = "terraform-aws-modules/s3-bucket/aws"
bucket = "my-s3-bucket"
versioning = {
enabled = true
}
file_system_security_group_vpc_id = "vpc-1234556abcdef"
file_system_security_group_ingress_rules = {
clients = {
referenced_security_group_id = "sg-1234556abcdef"
}
}
file_systems = {
datasets = {
prefix = "datasets/"
mount_targets = {
eu-west-1a = { subnet_id = "subnet-abcde012" }
eu-west-1b = { subnet_id = "subnet-bcde012a" }
}
}
}
}
Notes
- A bucket carrying an S3 file system needs versioning enabled and either SSE-S3 or SSE-KMS encryption. S3 Files is not available on a directory bucket, and the module says so rather than ignoring
file_systems. - Changing a file system's
prefix, its KMS key, or its IAM role replaces the file system, along with its mount targets and access points. An access point cannot be edited either, so any change to one replaces it and gives it a new ARN. - Removing a
synchronization_configurationleaves the file system expiring data after 365 days, not the 30 days AWS applies to a file system that never had one. - A principal named in an access point's
read_access_arnsorread_write_access_arnsreaches that file system only through the access points that name it. The module writes the allow and deny pair AWS recommends, and a read-only principal is also denied writes through its own access point. - Key
mount_targetsby something known at plan time, such as the Availability Zone. Terraform cannot create resources whose map keys come from values that only exist after an apply.
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 Terragrunt can create multiple similar resources from one configuration with the modules in the wrappers directory.
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_destroycannot be set through a module input. Asked for in #83, #186, #203, #215, #283 and #407, and not something this module can offer: thelifecycleblock has to be a literal inside the resource. Native options: an explicitDenyons3:DeleteBucketin the bucket policy, or fork this module and add the block yourself.ignore_changescannot 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 providerdefault_tags, or fork this module and addignore_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
- Complete - Complete S3 bucket with most of supported features enabled
- Cross-Region Replication - S3 bucket with Cross-Region Replication (CRR) enabled
- S3 Notifications - S3 bucket notifications to Lambda functions, SQS queues, and SNS topics.
- S3 Object - Manage S3 bucket objects.
- S3 Inventory and Analytics - S3 bucket Inventory and Analytics configurations.
- S3 ACLs - S3 bucket ACLs, for the buckets that still require them.
- S3 Bucket Policies - Attach the bundled log delivery and transport policies.
- S3 file system - Amazon S3 Files file systems scoped to bucket prefixes, with mount targets and access points.
- S3 Account-level Public Access Block - Manage S3 account-level Public Access Block.
- S3 Directory Bucket - S3 Directory Bucket configuration.
- S3 Table Bucket - S3 Table Bucket configuration.
- S3 Vectors - S3 Vectors vector bucket with indexes configuration.
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
- Russia has illegally annexed Crimea in 2014 and brought the war in Donbas followed by full-scale invasion of Ukraine in 2022.
- Russia has brought sorrow and devastations to millions of Ukrainians, killed hundreds of innocent people, damaged thousands of buildings, and forced several million people to flee.
- Putin khuylo!