Module: s3-bucket
This module creates an S3 bucket with support for versioning, lifecycles, object locks, replication, encryption, ACL, bucket object policies, and static website hosting.
For backward compatibility, it sets the S3 bucket ACL to private
and the s3_object_ownership
to ObjectWriter
. Moving forward, setting s3_object_ownership
to BucketOwnerEnforced
is recommended,
and doing so automatically disables the ACL.
This module blocks public access to the bucket by default. See block_public_acls
, block_public_policy
,
ignore_public_acls
, and restrict_public_buckets
to change the settings. See AWS documentation
for more details.
This module can optionally create an IAM User with access to the S3 bucket. This is inherently insecure in that to enable anyone to become the User, access keys must be generated, and anything generated by Terraform is stored unencrypted in the Terraform state. See the Terraform documentation for more details
The best way to grant access to the bucket is to grant one or more IAM Roles access to the bucket via privileged_principal_arns
.
This IAM Role can be assumed by EC2 instances via their Instance Profile, or Kubernetes (EKS) services using
IRSA.
Entities outside of AWS can assume the Role via OIDC.
(See this example of connecting GitHub
to enable GitHub actions to assume AWS IAM roles, or use this Cloud Posse component
if you are already using the Cloud Posse reference architecture.)
If neither of those approaches work, then as a last resort you can set user_enabled = true
and
this module will provision a basic IAM user with permissions to access the bucket.
We do not recommend creating IAM users this way for any other purpose.
If an IAM user is created, the IAM user name is constructed using terraform-null-label
and some input is required. The simplest input is name
. By default the name will be converted to lower case
and all non-alphanumeric characters except for hyphen will be removed. See the documentation for terraform-null-label
to learn how to override these defaults if desired.
If an AWS Access Key is created, it is stored either in SSM Parameter Store or is provided as a module output, but not both. Using SSM Parameter Store is recommended because that will keep the secret from being easily accessible via Terraform remote state lookup, but the key will still be stored unencrypted in the Terraform state in any case.
Usage
Using BucketOwnerEnforced
module "s3_bucket" {
source = "cloudposse/s3-bucket/aws"
# Cloud Posse recommends pinning every module to a specific version
# version = "x.x.x"
name = "app"
stage = "test"
namespace = "eg"
s3_object_ownership = "BucketOwnerEnforced"
enabled = true
user_enabled = false
versioning_enabled = false
privileged_principal_actions = ["s3:GetObject", "s3:ListBucket", "s3:GetBucketLocation"]
privileged_principal_arns = [
{
(local.deployment_iam_role_arn) = [""]
},
{
(local.additional_deployment_iam_role_arn) = ["prefix1/", "prefix2/"]
}
]
}
Configuring S3 storage lifecycle:
locals {
lifecycle_configuration_rules = [{
enabled = true # bool
id = "v2rule"
abort_incomplete_multipart_upload_days = 1 # number
filter_and = null
expiration = {
days = 120 # integer > 0
}
noncurrent_version_expiration = {
newer_noncurrent_versions = 3 # integer > 0
noncurrent_days = 60 # integer >= 0
}
transition = [{
days = 30 # integer >= 0
storage_class = "STANDARD_IA" # string/enum, one of GLACIER, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, DEEP_ARCHIVE, GLACIER_IR.
},
{
days = 60 # integer >= 0
storage_class = "ONEZONE_IA" # string/enum, one of GLACIER, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, DEEP_ARCHIVE, GLACIER_IR.
}]
noncurrent_version_transition = [{
newer_noncurrent_versions = 3 # integer >= 0
noncurrent_days = 30 # integer >= 0
storage_class = "ONEZONE_IA" # string/enum, one of GLACIER, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, DEEP_ARCHIVE, GLACIER_IR.
}]
}]
}
Allowing specific principal ARNs to perform actions on the bucket:
module "s3_bucket" {
source = "cloudposse/s3-bucket/aws"
# Cloud Posse recommends pinning every module to a specific version
# version = "x.x.x"
s3_object_ownership = "BucketOwnerEnforced"
enabled = true
user_enabled = true
versioning_enabled = false
allowed_bucket_actions = ["s3:GetObject", "s3:ListBucket", "s3:GetBucketLocation"]
name = "app"
stage = "test"
namespace = "eg"
privileged_principal_arns = [
{
"arn:aws:iam::123456789012:role/principal1" = ["prefix1/", "prefix2/"]
}, {
"arn:aws:iam::123456789012:role/principal2" = [""]
}]
privileged_principal_actions = [
"s3:PutObject",
"s3:PutObjectAcl",
"s3:GetObject",
"s3:DeleteObject",
"s3:ListBucket",
"s3:ListBucketMultipartUploads",
"s3:GetBucketLocation",
"s3:AbortMultipartUpload"
]
}