Kaizen

S3 Bucket

A private, versioned S3 bucket with public access blocked and optional TLS only access.

GovCloudCMKView source
Module
s3-bucket
Layer
Data
Interface
10 inputs, 4 outputs
Used by
9 Kaizen apps

Why it matters

Uploads, exports, and logs all end up in S3, and a bucket left open to the public is one of the most common ways cloud data leaks. Every bucket from this module blocks public access, keeps old versions of each file, and encrypts objects at rest. Federal stacks add a customer managed key and a policy that rejects any request not sent over TLS.

Use it when

  • The app stores user uploads, generated files, or exports.
  • You need a log bucket, for example the load balancer's access_logs_bucket.
  • You host a static site behind CloudFront with origin access control.

Reach for something else when

  • You need to query the data with SQL or keep relational records. Use rds-postgres.

What it creates

  • aws_s3_bucket (force_destroy = false by default)
  • aws_s3_bucket_versioning (enabled by default)
  • aws_s3_bucket_server_side_encryption_configuration (AES256, or aws:kms with a bucket key)
  • aws_s3_bucket_public_access_block (all four settings true)
  • aws_s3_bucket_ownership_controls (BucketOwnerEnforced, optional)
  • aws_s3_bucket_lifecycle_configuration (optional)
  • aws_s3_bucket_policy (DenyInsecureTransport and caller JSON, optional)
  • aws_s3_bucket_cors_configuration (optional, for browser uploads)

Secure by default

  • All four public access block settings are on (not configurable).
  • Versioning is on (versioning_enabled = true).
  • Objects are encrypted at rest with AES256 when no key is given.
  • Terraform refuses to delete a bucket that still holds objects (force_destroy = false).

Commercial and GovCloud

main.tf
module "s3_uploads" {
  source = "git::ssh://git@github.com/the-kaizen-labs/terraform-modules.git//s3-bucket?ref=v1.7.0"

  bucket_name = "${local.app_name}-uploads"

  cors_rules = [{
    allowed_origins = [local.app_url]
    allowed_methods = ["GET", "PUT"]
    allowed_headers = ["*"]
    expose_headers  = ["ETag"]
    max_age_seconds = 3000
  }]

  tags = local.tags
}
SettingCommercial defaultFederal settingNote
kms_key_idnull (AES256)customer managed KMS key ARNFedRAMP SC-13.
enforce_tls_requests_onlyfalsetrueFedRAMP SC-8. Also satisfies AWS Config rule s3-bucket-ssl-requests-only.
enforce_bucket_ownerfalsetrueDisables ACLs entirely with BucketOwnerEnforced.

How it connects

  • To ecs-service: bucket_arn, in a policy on task_role_name
  • To alb: bucket_name (as access_logs_bucket)

Inputs

NameTypeDefaultRequiredDescription
bucket_namestringyesS3 bucket name (must be globally unique)
bucket_policy_jsonstringnullnoBucket policy JSON. Null skips the policy. Composes with enforce_tls_requests_only - both can be set.
cors_ruleslist(object({ allowed_origins = list(string) allowed_methods = list(string) allowed_headers = list(string) expose_headers = list(string) max_age_seconds = number }))[]noCORS rules. Leave empty if the bucket is not accessed directly from a browser.
enforce_bucket_ownerboolfalsenoApply BucketOwnerEnforced ownership controls (disables ACLs entirely). Required for federal posture; gated to avoid state churn on pre-April-2023 buckets that still use ObjectWriter.
enforce_tls_requests_onlyboolfalsenoAttach a bucket policy denying any S3 request where aws:SecureTransport = false. Required for federal posture (FedRAMP SC-8) and to satisfy AWS Config rule s3-bucket-ssl-requests-only.
force_destroyboolfalsenoAllow Terraform to delete the bucket even if it contains objects. Disable in production.
kms_key_idstringnullnoCustomer-managed KMS key ARN for default encryption. Null falls back to AES256 (AWS-managed). Federal posture (FedRAMP SC-13) requires a customer-managed key.
lifecycle_ruleslist(object({ id = string prefix = string expiration_days = number noncurrent_version_expiration_days = number abort_multipart_upload_days = number }))[]noLifecycle rules. Null fields skip the corresponding sub-block.
tagsmap(string){}noTags to apply to all resources
versioning_enabledbooltruenoEnable S3 versioning

Outputs

NameDescription
bucket_arnValue: aws_s3_bucket.this.arn
bucket_idValue: aws_s3_bucket.this.id
bucket_nameValue: aws_s3_bucket.this.bucket
bucket_regional_domain_nameValue: aws_s3_bucket.this.bucket_regional_domain_name

Used by

AppPinned ref
BidBuddyv1.0.0
dustv1.1.0
evergreenv1.1.0
ginkgov1.3.0
magnoliav1.1.0
oakv1.1.0
poppyv1.1.0
saplingsv1.3.0
taprootv1.1.0

On this page