diff --git a/README.md b/README.md
index 9a2f5a2..437434b 100644
--- a/README.md
+++ b/README.md
@@ -248,6 +248,7 @@ Available targets:
| [aws_s3_bucket_policy.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
| [aws_s3_bucket_public_access_block.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
| [aws_s3_bucket_replication_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_replication_configuration) | resource |
+| [aws_s3_bucket_request_payment_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_request_payment_configuration) | resource |
| [aws_s3_bucket_server_side_encryption_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource |
| [aws_s3_bucket_versioning.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource |
| [aws_s3_bucket_website_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_website_configuration) | resource |
@@ -283,7 +284,7 @@ Available targets:
| [descriptor\_formats](#input\_descriptor\_formats) | Describe additional descriptors to be output in the `descriptors` output map.
Map of maps. Keys are names of descriptors. Values are maps of the form
`{
format = string
labels = list(string)
}`
(Type is `any` so the map values can later be enhanced to provide additional options.)
`format` is a Terraform format string to be passed to the `format()` function.
`labels` is a list of labels, in order, to pass to `format()` function.
Label values will be normalized before being passed to `format()` so they will be
identical to how they appear in `id`.
Default is `{}` (`descriptors` output will be empty). | `any` | `{}` | no |
| [enabled](#input\_enabled) | Set to false to prevent the module from creating any resources | `bool` | `null` | no |
| [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no |
-| [event\_notification\_details](#input\_event\_notification\_details) | (optional) S3 event notification details |
object({|
enabled = bool
lambda_list = optional(list(object({
lambda_function_arn = string
events = optional(list(string), ["s3:ObjectCreated:*"])
filter_prefix = string
filter_suffix = string
})), [])
queue_list = optional(list(object({
queue_arn = string
events = optional(list(string), ["s3:ObjectCreated:*"])
})), [])
topic_list = optional(list(object({
topic_arn = string
events = optional(list(string), ["s3:ObjectCreated:*"])
})), [])
})
{| no | +| [event\_notification\_details](#input\_event\_notification\_details) | S3 event notification details |
"enabled": false
}
object({|
enabled = bool
eventbridge = optional(bool, false)
lambda_list = optional(list(object({
lambda_function_arn = string
events = optional(list(string), ["s3:ObjectCreated:*"])
filter_prefix = string
filter_suffix = string
})), [])
queue_list = optional(list(object({
queue_arn = string
events = optional(list(string), ["s3:ObjectCreated:*"])
})), [])
topic_list = optional(list(object({
topic_arn = string
events = optional(list(string), ["s3:ObjectCreated:*"])
})), [])
})
{| no | | [expected\_bucket\_owner](#input\_expected\_bucket\_owner) | Account ID of the expected bucket owner.
"enabled": false
}
list(object({| `[]` | no | @@ -314,6 +315,7 @@ Available targets: | [s3\_replication\_permissions\_boundary\_arn](#input\_s3\_replication\_permissions\_boundary\_arn) | Permissions boundary ARN for the created IAM replication role. | `string` | `null` | no | | [s3\_replication\_rules](#input\_s3\_replication\_rules) | Specifies the replication rules for S3 bucket replication if enabled. You must also set s3\_replication\_enabled to true. |
id = string
type = string
permissions = list(string)
uri = string
}))
list(object({| `null` | no | | [s3\_replication\_source\_roles](#input\_s3\_replication\_source\_roles) | Cross-account IAM Role ARNs that will be allowed to perform S3 replication to this bucket (for replication within the same AWS account, it's not necessary to adjust the bucket policy). | `list(string)` | `[]` | no | +| [s3\_request\_payment\_configuration](#input\_s3\_request\_payment\_configuration) | S3 request payment configuration |
id = optional(string)
priority = optional(number)
prefix = optional(string)
status = optional(string, "Enabled")
# delete_marker_replication { status } had been flattened for convenience
delete_marker_replication_status = optional(string, "Disabled")
# Add the configuration as it appears in the resource, for consistency
# this nested version takes precedence if both are provided.
delete_marker_replication = optional(object({
status = string
}))
# destination_bucket is specified here rather than inside the destination object because before optional
# attributes, it made it easier to work with the Terraform type system and create a list of consistent type.
# It is preserved for backward compatibility, but the nested version takes priority if both are provided.
destination_bucket = optional(string) # destination bucket ARN, overrides s3_replica_bucket_arn
destination = object({
bucket = optional(string) # destination bucket ARN, overrides s3_replica_bucket_arn
storage_class = optional(string, "STANDARD")
# replica_kms_key_id at this level is for backward compatibility, and is overridden by the one in `encryption_configuration`
replica_kms_key_id = optional(string, "")
encryption_configuration = optional(object({
replica_kms_key_id = string
}))
access_control_translation = optional(object({
owner = string
}))
# account_id is for backward compatibility, overridden by account
account_id = optional(string)
account = optional(string)
# For convenience, specifying either metrics or replication_time enables both
metrics = optional(object({
event_threshold = optional(object({
minutes = optional(number, 15) # Currently 15 is the only valid number
}), { minutes = 15 })
status = optional(string, "Enabled")
}), { status = "Disabled" })
# To preserve backward compatibility, Replication Time Control (RTC) is automatically enabled
# when metrics are enabled. To enable metrics without RTC, you must explicitly configure
# replication_time.status = "Disabled".
replication_time = optional(object({
time = optional(object({
minutes = optional(number, 15) # Currently 15 is the only valid number
}), { minutes = 15 })
status = optional(string)
}))
})
source_selection_criteria = optional(object({
replica_modifications = optional(object({
status = string # Either Enabled or Disabled
}))
sse_kms_encrypted_objects = optional(object({
status = optional(string)
}))
}))
# filter.prefix overrides top level prefix
filter = optional(object({
prefix = optional(string)
tags = optional(map(string), {})
}))
}))
object({|
enabled = bool
expected_bucket_owner = optional(string)
payer = string
})
{| no | | [source\_ip\_allow\_list](#input\_source\_ip\_allow\_list) | List of IP addresses to allow to perform all actions to the bucket | `list(string)` | `[]` | no | | [source\_policy\_documents](#input\_source\_policy\_documents) | List of IAM policy documents (in JSON) that are merged together into the exported document.
"enabled": false,
"payer": "BucketOwner"
}
object({|
enabled = bool
lambda_list = optional(list(object({
lambda_function_arn = string
events = optional(list(string), ["s3:ObjectCreated:*"])
filter_prefix = string
filter_suffix = string
})), [])
queue_list = optional(list(object({
queue_arn = string
events = optional(list(string), ["s3:ObjectCreated:*"])
})), [])
topic_list = optional(list(object({
topic_arn = string
events = optional(list(string), ["s3:ObjectCreated:*"])
})), [])
})
{| no | +| [event\_notification\_details](#input\_event\_notification\_details) | S3 event notification details |
"enabled": false
}
object({|
enabled = bool
eventbridge = optional(bool, false)
lambda_list = optional(list(object({
lambda_function_arn = string
events = optional(list(string), ["s3:ObjectCreated:*"])
filter_prefix = string
filter_suffix = string
})), [])
queue_list = optional(list(object({
queue_arn = string
events = optional(list(string), ["s3:ObjectCreated:*"])
})), [])
topic_list = optional(list(object({
topic_arn = string
events = optional(list(string), ["s3:ObjectCreated:*"])
})), [])
})
{| no | | [expected\_bucket\_owner](#input\_expected\_bucket\_owner) | Account ID of the expected bucket owner.
"enabled": false
}
list(object({| `[]` | no | diff --git a/main.tf b/main.tf index 40ccda5..60dd688 100644 --- a/main.tf +++ b/main.tf @@ -580,11 +580,14 @@ resource "time_sleep" "wait_for_aws_s3_bucket_settings" { create_duration = "30s" destroy_duration = "30s" } -// S3 event Bucket Notifications + +# S3 event Bucket Notifications resource "aws_s3_bucket_notification" "bucket_notification" { count = var.event_notification_details.enabled ? 1 : 0 bucket = local.bucket_id + eventbridge = var.event_notification_details.eventbridge + dynamic "lambda_function" { for_each = var.event_notification_details.lambda_list content { @@ -612,8 +615,8 @@ resource "aws_s3_bucket_notification" "bucket_notification" { } } -/// Directory Bucket -// https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_directory_bucket +# Directory Bucket +# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_directory_bucket resource "aws_s3_directory_bucket" "default" { count = local.enabled && var.s3_directory_bucket_enabled ? 1 : 0 bucket = local.directory_bucket_name @@ -623,3 +626,11 @@ resource "aws_s3_directory_bucket" "default" { name = var.availability_zone_id } } + +resource "aws_s3_bucket_request_payment_configuration" "default" { + count = local.enabled && var.s3_request_payment_configuration.enabled ? 1 : 0 + + bucket = local.bucket_id + expected_bucket_owner = var.s3_request_payment_configuration.expected_bucket_owner + payer = lower(var.s3_request_payment_configuration.payer) == "requester" ? "Requester" : "BucketOwner" +} diff --git a/replication.tf b/replication.tf index ee1d767..1087d32 100644 --- a/replication.tf +++ b/replication.tf @@ -1,11 +1,7 @@ -locals { - replication_role = format("%s-replication", local.bucket_name) -} - resource "aws_iam_role" "replication" { count = local.replication_enabled ? 1 : 0 - name = local.replication_role + name = format("%s-replication", local.bucket_name) assume_role_policy = data.aws_iam_policy_document.replication_sts[0].json permissions_boundary = var.s3_replication_permissions_boundary_arn @@ -32,7 +28,7 @@ data "aws_iam_policy_document" "replication_sts" { resource "aws_iam_policy" "replication" { count = local.replication_enabled ? 1 : 0 - name = local.replication_role + name = aws_iam_role.replication[0].name policy = data.aws_iam_policy_document.replication[0].json tags = module.this.tags @@ -68,6 +64,7 @@ data "aws_iam_policy_document" "replication" { resources = toset(concat( try(length(var.s3_replica_bucket_arn), 0) > 0 ? ["${var.s3_replica_bucket_arn}/*"] : [], [for rule in local.s3_replication_rules : "${rule.destination_bucket}/*" if try(length(rule.destination_bucket), 0) > 0], + [for rule in local.s3_replication_rules : "${rule.destination.bucket}/*" if try(length(rule.destination.bucket), 0) > 0], )) } } diff --git a/variables.tf b/variables.tf index 1b7d77d..a663d6f 100644 --- a/variables.tf +++ b/variables.tf @@ -466,9 +466,11 @@ variable "expected_bucket_owner" { More information: https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucket-owner-condition.html EOT } + variable "event_notification_details" { type = object({ - enabled = bool + enabled = bool + eventbridge = optional(bool, false) lambda_list = optional(list(object({ lambda_function_arn = string events = optional(list(string), ["s3:ObjectCreated:*"]) @@ -485,11 +487,27 @@ variable "event_notification_details" { topic_arn = string events = optional(list(string), ["s3:ObjectCreated:*"]) })), []) + }) + description = "S3 event notification details" + default = { + enabled = false + } +} +variable "s3_request_payment_configuration" { + type = object({ + enabled = bool + expected_bucket_owner = optional(string) + payer = string }) - description = "(optional) S3 event notification details" + description = "S3 request payment configuration" default = { enabled = false + payer = "BucketOwner" + } + validation { + condition = contains(["bucketowner", "requester"], lower(var.s3_request_payment_configuration.payer)) + error_message = "The s3 request payment config's payer must be either BucketOwner or Requester" } }
id = string
type = string
permissions = list(string)
uri = string
}))