Skip to content

About

Terraform module for creating production-oriented Amazon S3 resources.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Terraform AWS S3 Module

Terraform Checks

Terraform module for creating production-oriented Amazon S3 resources. The module is implemented directly with HashiCorp AWS provider resources and does not wrap another S3 module.

The module supports:

  • General purpose S3 buckets, including global and account-regional namespaces, and S3 Express One Zone directory buckets
  • AES256, AWS KMS, and dual-layer AWS KMS server-side encryption, with optional SSE-C upload blocking
  • Public access blocking, object ownership controls, optional ACLs, bucket policies, and bucket ABAC
  • Versioning, Object Lock default retention, lifecycle rules, server access logging, and replication
  • Intelligent-Tiering, Inventory, Analytics, request metrics, and S3 Metadata tables
  • CORS, EventBridge, Lambda, SNS, and SQS notifications
  • Static website configuration and redirects
  • Public or VPC-scoped S3 access points

The root module does not create KMS keys, IAM roles, notification destinations, destination buckets, VPCs, or provider configuration. Callers supply those dependencies when enabling the related capability.

Usage

module "artifacts" {
  source  = "native-cube/s3/aws"
  version = "~> 1.0"

  bucket_name = "acme-production-artifacts"
  versioning  = "Enabled"

  sse_algorithm     = "aws:kms"
  kms_key_id        = var.artifacts_kms_key_arn
  enable_bucket_key = true

  lifecycle_rules = [{
    id     = "archive"
    status = "Enabled"
    filter = {
      prefix = "releases/"
    }
    transition = [{
      days          = 30
      storage_class = "STANDARD_IA"
    }]
    noncurrent_version_expiration = {
      noncurrent_days = 90
    }
  }]

  tags = {
    Environment = "production"
    Service     = "artifacts"
  }
}

Compatibility and Versioning

Version 1.x requires Terraform 1.5 or newer and HashiCorp AWS provider 6.61 or newer within major version 6. The minimum and latest supported combinations are exercised separately in CI.

This module follows semantic versioning. Pin a compatible module version in production and review CHANGELOG.md before upgrading. Major releases may contain breaking changes; minor and patch releases preserve the documented 1.x interface.

Security and Operations

  • Server-side encryption is always configured for a general purpose bucket. AES256 is the default; select aws:kms or aws:kms:dsse to use an optional caller-managed KMS key.
  • All four public-access-block settings default to true, ownership defaults to BucketOwnerEnforced, and ACL management is disabled. To use an ACL, explicitly enable it, select compatible object ownership, and provide exactly one canned or detailed ACL.
  • force_destroy defaults to false. Enabling it allows Terraform to delete bucket contents during destroy, and those objects are not recoverable; Object Lock retention can still prevent deletion.
  • Bucket policy JSON is validated syntactically, but callers remain responsible for least-privilege statements and correct principals, resources, and conditions.
  • Set blocked_encryption_types = ["SSE-C"] to reject uploads encrypted with customer-provided keys while retaining the module's configured bucket-default encryption.
  • Object Lock must be enabled when the bucket is created. Setting object_lock_configuration enables it automatically and supports GOVERNANCE or COMPLIANCE default retention in days or years. Per-object retention and legal holds remain caller responsibilities.

Data Management and Observability

  • Lifecycle rules support prefix, single-tag, multi-tag, and object-size filters; incomplete multipart upload cleanup; current-object transitions and expiration; and noncurrent-version transitions and expiration.
  • Intelligent-Tiering, Inventory, Analytics, and request metrics use maps keyed by stable configuration names, so individual configurations have stable Terraform addresses.
  • Inventory and Analytics destination buckets and their policies are caller managed. S3 Metadata availability, account prerequisites, and supported Regions should be confirmed before enabling metadata_configuration.
  • abac_status = "Enabled" enables bucket attribute-based access control. Review existing IAM policies before adopting tag-based authorization.

Replication and Integrations

  • Replication, including existing-object replication, requires versioning = "Enabled", an existing destination bucket with versioning enabled, and an IAM role that Amazon S3 can assume.
  • Notification destination policies and Lambda invoke permissions are intentionally managed by callers. Configure them before applying the bucket notification resource.
  • Access points inherit the module tags, block public access by default, can target the module's general purpose or directory bucket, and can optionally be restricted to an existing VPC.
  • Website configuration does not make a bucket public. Prefer CloudFront with origin access control, or deliberately configure a tightly scoped bucket policy if direct S3 website access is required.
  • Server access logging defaults to logs/<source-bucket-name>/ in the caller-supplied logging bucket. The prefix and simple or date-partitioned object-key format are configurable; the destination bucket must permit S3 log delivery.

Directory Buckets

Set create_bucket = false and create_directory_bucket = true for directory-only mode. Supply a location-specific directory_bucket_name, the matching Availability Zone or Local Zone ID in location_name, and the appropriate location_type. General purpose bucket features such as policies, lifecycle rules, notifications, and website hosting do not apply to directory buckets through this interface.

Examples

Development

Run make check to verify formatting, generated documentation, initialization, native Terraform tests, and every example. Run make docs after changing inputs, outputs, resources, or version constraints. make release-check additionally requires TFLint and Trivy and runs the complete pre-release gate. See RELEASING.md for the maintainer checklist.

Module Documentation

Requirements

Name Version
terraform >= 1.5.0
aws >= 6.61.0, < 7.0.0

Providers

Name Version
aws >= 6.61.0, < 7.0.0

Resources

Name Type
aws_s3_access_point.main resource
aws_s3_bucket.main resource
aws_s3_bucket_abac.main resource
aws_s3_bucket_acl.main resource
aws_s3_bucket_analytics_configuration.main resource
aws_s3_bucket_cors_configuration.main resource
aws_s3_bucket_intelligent_tiering_configuration.main resource
aws_s3_bucket_inventory.main resource
aws_s3_bucket_lifecycle_configuration.main resource
aws_s3_bucket_logging.main resource
aws_s3_bucket_metadata_configuration.main resource
aws_s3_bucket_metric.main resource
aws_s3_bucket_notification.main resource
aws_s3_bucket_object_lock_configuration.main resource
aws_s3_bucket_ownership_controls.main resource
aws_s3_bucket_policy.main resource
aws_s3_bucket_public_access_block.main resource
aws_s3_bucket_replication_configuration.main resource
aws_s3_bucket_server_side_encryption_configuration.main resource
aws_s3_bucket_versioning.main resource
aws_s3_bucket_website_configuration.main resource
aws_s3_directory_bucket.main resource

Inputs

Name Description Type Default Required
abac_status Optional attribute-based access control status for the general purpose bucket. Null leaves S3 bucket ABAC unmanaged. string null no
access_control_policy Optional detailed access control policy for the general purpose bucket. Use this instead of acl when enable_acl is true.
object({
owner = object({
id = string
display_name = optional(string)
})
grant = list(object({
grantee = object({
type = string
email_address = optional(string)
id = optional(string)
uri = optional(string)
})
permission = string
}))
})
null no
access_points S3 access points to create when enable_access_points is true. Access point names must be unique and may target the module-created general purpose or directory bucket.
list(object({
name = string
bucket_type = optional(string, "general")
account_id = optional(string)
bucket_account_id = optional(string)
block_public_acls = optional(bool, true)
block_public_policy = optional(bool, true)
ignore_public_acls = optional(bool, true)
restrict_public_buckets = optional(bool, true)
vpc_id = optional(string, null)
policy = optional(string, null)
tags = optional(map(string), null)
}))
[] no
acl Optional canned ACL to apply when enable_acl is true. Conflicts with access_control_policy. string null no
analytics_configurations S3 Analytics configurations for the general purpose bucket, keyed by a stable configuration name.
map(object({
filter = optional(object({
prefix = optional(string)
tags = optional(map(string), {})
}))
storage_class_analysis = object({
output_schema_version = optional(string, "V_1")
destination = object({
bucket_account_id = optional(string)
bucket_arn = string
format = optional(string, "CSV")
prefix = optional(string)
})
})
}))
{} no
block_public_acls Whether Amazon S3 should block public ACLs for the general purpose bucket. bool true no
block_public_policy Whether Amazon S3 should block public bucket policies for the general purpose bucket. bool true no
blocked_encryption_types Optional server-side encryption types to reject for object uploads. Set to ["SSE-C"] to block customer-provided encryption keys; null leaves the provider default. set(string) null no
bucket_name Name of the general purpose S3 bucket. When null, Amazon S3 generates a name unless bucket_prefix is selected. string null no
bucket_namespace Optional namespace for the general purpose bucket. Valid values are global and account-regional. Null uses the AWS global namespace default. string null no
bucket_policy Optional S3 bucket policy JSON document used when configure_policy is true. string null no
bucket_prefix Prefix from which Amazon S3 generates the general purpose bucket name when use_bucket_prefix is true. string null no
configure_policy Whether to attach bucket_policy to the general purpose bucket. bool false no
cors_rules Cross-Origin Resource Sharing rules for the general purpose bucket.
list(object({
allowed_methods = list(string)
allowed_origins = list(string)
allowed_headers = optional(list(string), [])
expose_headers = optional(list(string), [])
max_age_seconds = optional(number)
id = optional(string)
}))
[] no
create_bucket Whether to create a general purpose S3 bucket. bool true no
create_directory_bucket Whether to create an S3 directory bucket in addition to any general purpose bucket. bool false no
data_redundancy Data redundancy for the directory bucket. string "SingleAvailabilityZone" no
directory_bucket_name Name of the S3 directory bucket. The name must include the location-specific suffix required by Amazon S3. string null no
enable_access_points Whether to create the access points declared in access_points. bool false no
enable_acl Whether to manage an ACL for the general purpose bucket. ACLs require an object ownership mode other than BucketOwnerEnforced. bool false no
enable_bucket_key Whether to use an S3 Bucket Key for KMS-based server-side encryption. bool false no
enable_replication_configuration Whether to configure replication for the general purpose bucket. bool false no
enable_s3_notification Whether to manage EventBridge, Lambda, SNS, and SQS notifications for the general purpose bucket. bool false no
enable_website_configuration Whether to configure static website hosting or redirection for the general purpose bucket. bool false no
error_document Object key returned for website errors. string null no
eventbridge Whether to send bucket events to Amazon EventBridge when notifications are enabled. bool false no
force_destroy Whether Terraform may delete bucket objects when destroying a bucket. Deleted objects are not recoverable, and Object Lock retention can still prevent deletion. bool false no
ignore_public_acls Whether Amazon S3 should ignore public ACLs for the general purpose bucket. bool true no
index_document Object suffix used as the website index document. string null no
intelligent_tiering_configurations S3 Intelligent-Tiering configurations for the general purpose bucket, keyed by a stable configuration name.
map(object({
status = optional(string, "Enabled")
filter = optional(object({
prefix = optional(string)
tags = optional(map(string), {})
}))
tiering = list(object({
access_tier = string
days = number
}))
}))
{} no
inventory_configurations S3 Inventory configurations for the general purpose bucket, keyed by a stable configuration name.
map(object({
enabled = optional(bool, true)
included_object_versions = optional(string, "All")
optional_fields = optional(set(string), [])
filter_prefix = optional(string)
schedule_frequency = optional(string, "Daily")
destination = object({
account_id = optional(string)
bucket_arn = string
format = optional(string, "CSV")
prefix = optional(string)
encryption = optional(object({
sse_kms = optional(object({
key_id = string
}))
sse_s3 = optional(bool, false)
}))
})
}))
{} no
kms_key_id Optional KMS key ARN or ID used when sse_algorithm is aws:kms or aws:kms:dsse. string null no
lambda_notifications Lambda function notification destinations. Callers must grant Amazon S3 permission to invoke each function.
list(object({
lambda_function_arn = string
events = list(string)
filter_prefix = optional(string)
filter_suffix = optional(string)
id = optional(string)
}))
[] no
lifecycle_configuration_timeouts Optional create and update timeouts for the S3 lifecycle configuration.
object({
create = optional(string)
update = optional(string)
})
null no
lifecycle_rules Lifecycle rules for current and noncurrent objects in the general purpose bucket.
list(object({
id = string
status = string
filter = optional(object({
object_size_greater_than = optional(string)
object_size_less_than = optional(string)
prefix = optional(string)
tag = optional(object({
key = string
value = string
}))
tags = optional(map(string))
}))
abort_incomplete_multipart_upload = optional(object({
days_after_initiation = number
}))
expiration = optional(object({
days = optional(number)
date = optional(string)
expired_object_delete_marker = optional(bool)
}))
transition = optional(list(object({
days = optional(number)
date = optional(string)
storage_class = string
})))
noncurrent_version_expiration = optional(object({
noncurrent_days = number
newer_noncurrent_versions = optional(number)
}))
noncurrent_version_transition = optional(list(object({
noncurrent_days = number
storage_class = string
newer_noncurrent_versions = optional(number)
})))
}))
[] no
lifecycle_transition_default_minimum_object_size Optional default minimum object-size behavior for lifecycle transitions. string null no
location_name Availability Zone ID or Local Zone ID in which to create the directory bucket. string null no
location_type Location type for the directory bucket. string "AvailabilityZone" no
logging_bucket_name Destination bucket name for S3 server access logs. string null no
logging_enabled Whether to deliver server access logs for the general purpose bucket. bool false no
logging_target_object_key_format Optional server access log object-key format. Set exactly one partitioned_prefix or simple_prefix option. Null uses the S3 default simple format.
object({
partitioned_prefix = optional(object({
partition_date_source = optional(string, "EventTime")
}))
simple_prefix = optional(bool, false)
})
null no
logging_target_prefix Optional prefix for server access log object keys. Null uses logs//. string null no
metadata_configuration Optional S3 Metadata inventory and journal table configuration for the general purpose bucket.
object({
inventory_table_configuration = object({
configuration_state = optional(string, "ENABLED")
encryption_configuration = optional(object({
sse_algorithm = string
kms_key_arn = optional(string)
}))
})
journal_table_configuration = object({
encryption_configuration = optional(object({
sse_algorithm = string
kms_key_arn = optional(string)
}))
record_expiration = object({
expiration = optional(string, "ENABLED")
days = optional(number)
})
})
create_timeout = optional(string)
})
null no
metric_configurations S3 request metrics configurations for the general purpose bucket, keyed by a stable configuration name.
map(object({
filter = optional(object({
access_point = optional(string)
prefix = optional(string)
tags = optional(map(string), {})
}))
}))
{} no
object_lock_configuration Optional Object Lock default retention configuration for the general purpose bucket. Requires versioning to be Enabled.
object({
token = optional(string)
default_retention = optional(object({
mode = optional(string, "GOVERNANCE")
days = optional(number)
years = optional(number)
}))
})
null no
object_lock_enabled Whether to enable S3 Object Lock on the general purpose bucket at creation time. object_lock_configuration also enables it automatically. bool false no
object_ownership Object ownership mode for the general purpose bucket. BucketOwnerEnforced disables ACLs. string "BucketOwnerEnforced" no
redirect_all_requests_to Optional website redirect target. This conflicts with index, error, and routing rule configuration.
object({
host_name = string
protocol = optional(string)
})
null no
region Optional AWS Region in which the module manages S3 resources. Defaults to the Region configured by the caller's AWS provider. string null no
replication_configuration Replication role and rules used when enable_replication_configuration is true.
object({
role = string
token = optional(string)
rule = list(object({
id = string
status = string
priority = optional(number)
filter = optional(object({
tag = optional(object({
key = string
value = string
}))
tags = optional(map(any))
prefix = optional(string)
}))
delete_marker_replication = optional(object({
status = string
}))
existing_object_replication = optional(object({
status = string
}))
destination = object({
access_control_translation = optional(object({
owner = string
}))
account = optional(string)
bucket = string
encryption_configuration = optional(object({
replica_kms_key_id = string
}))
metrics = optional(object({
status = string
event_threshold = optional(object({
minutes = number
}))
}))
replication_time = optional(object({
status = string
time = object({
minutes = number
})
}))
storage_class = optional(string)
})
source_selection_criteria = optional(object({
replica_modifications = optional(object({
status = string
}))
sse_kms_encrypted_objects = optional(object({
status = string
}))
}))
}))
})
null no
restrict_public_buckets Whether Amazon S3 should restrict public bucket policies for the general purpose bucket. bool true no
routing_rule Structured website routing rules. Use this or routing_rules, but not both.
list(object({
condition = object({
http_error_code_returned_equals = optional(string)
key_prefix_equals = optional(string)
})
redirect = object({
host_name = optional(string)
http_redirect_code = optional(string)
protocol = optional(string)
replace_key_prefix_with = optional(string)
replace_key_with = optional(string)
})
}))
[] no
routing_rules JSON-encoded website routing rules. Use an empty string when unused. Conflicts with routing_rule. string "" no
sns_notifications SNS topic notification destinations. Callers must configure each topic policy to permit delivery from Amazon S3.
list(object({
topic_arn = string
events = list(string)
filter_prefix = optional(string)
filter_suffix = optional(string)
id = optional(string)
}))
[] no
sqs_notifications SQS queue notification destinations. Callers must configure each queue policy to permit delivery from Amazon S3.
list(object({
queue_arn = string
events = list(string)
filter_prefix = optional(string)
filter_suffix = optional(string)
id = optional(string)
}))
[] no
sse_algorithm Server-side encryption algorithm for the general purpose bucket. string "AES256" no
tags Tags to apply to all module-created buckets and access points. map(string) {} no
use_bucket_prefix Whether to generate the general purpose bucket name from bucket_prefix instead of using bucket_name. bool false no
versioning Optional versioning status for the general purpose bucket: Enabled, Suspended, or Disabled. Null leaves versioning unmanaged. string null no

Outputs

Name Description
access_points Details for access points created by the module, keyed by access point name.
bucket_arn Amazon Resource Name (ARN) of the created general purpose S3 bucket, or null when creation is disabled.
bucket_domain_name Bucket domain name of the created general purpose S3 bucket, or null when creation is disabled.
bucket_hosted_zone_id Route 53 hosted zone ID of the created general purpose S3 bucket, or null when creation is disabled.
bucket_id ID and name of the created general purpose S3 bucket, or null when creation is disabled.
bucket_region AWS Region of the created general purpose S3 bucket, or null when creation is disabled.
bucket_regional_domain_name Regional domain name of the created general purpose S3 bucket, or null when creation is disabled.
directory_bucket_arn Amazon Resource Name (ARN) of the created S3 directory bucket, or null when creation is disabled.
directory_bucket_name Name of the created S3 directory bucket, or null when creation is disabled.
website_domain Domain of the configured S3 bucket website, or null when website configuration is disabled.
website_endpoint Endpoint of the configured S3 bucket website, or null when website configuration is disabled.

About

Terraform module for creating production-oriented Amazon S3 resources.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages