diff --git a/README.md b/README.md
index cc438fea..4ceba773 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,7 @@ These features of S3 bucket configurations are supported:
- Cross-Region Replication (CRR)
- ELB log delivery bucket policy
- ALB/NLB log delivery bucket policy
+- WAF log delivery bucket policy
- Account-level Public Access Block
- S3 Directory Bucket
- S3 Table Bucket
@@ -78,6 +79,24 @@ module "s3_bucket_for_logs" {
}
```
+### Bucket with WAF log delivery policy attached
+
+```hcl
+module "s3_bucket_for_waf_logs" {
+ source = "terraform-aws-modules/s3-bucket/aws"
+
+ bucket = "my-s3-bucket-for-waf-logs"
+
+ # Allow deletion of non-empty bucket
+ force_destroy = true
+
+ control_object_ownership = true
+ object_ownership = "ObjectWriter"
+
+ attach_waf_log_delivery_policy = true # Required for WAF logs
+}
+```
+
## 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`.
@@ -182,6 +201,7 @@ No modules.
| [aws_iam_policy_document.inventory_and_analytics_destination_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.lb_log_delivery](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.require_latest_tls](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.waf_log_delivery](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source |
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
@@ -212,6 +232,7 @@ No modules.
| [attach\_policy](#input\_attach\_policy) | Controls if S3 bucket should have bucket policy attached (set to `true` to use value of `policy` as bucket policy) | `bool` | `false` | no |
| [attach\_public\_policy](#input\_attach\_public\_policy) | Controls if a user defined public bucket policy will be attached (set to `false` to allow upstream to apply defaults to the bucket) | `bool` | `true` | no |
| [attach\_require\_latest\_tls\_policy](#input\_attach\_require\_latest\_tls\_policy) | Controls if S3 bucket should require the latest version of TLS | `bool` | `false` | no |
+| [attach\_waf\_log\_delivery\_policy](#input\_attach\_waf\_log\_delivery\_policy) | Controls if S3 bucket should have WAF log delivery policy attached | `bool` | `false` | no |
| [availability\_zone\_id](#input\_availability\_zone\_id) | Availability Zone ID or Local Zone ID | `string` | `null` | no |
| [block\_public\_acls](#input\_block\_public\_acls) | Whether Amazon S3 should block public ACLs for this bucket. | `bool` | `true` | no |
| [block\_public\_policy](#input\_block\_public\_policy) | Whether Amazon S3 should block public bucket policies for this bucket. | `bool` | `true` | no |
diff --git a/examples/complete/main.tf b/examples/complete/main.tf
index 0a334919..91dc2d7e 100644
--- a/examples/complete/main.tf
+++ b/examples/complete/main.tf
@@ -75,6 +75,7 @@ module "log_bucket" {
attach_access_log_delivery_policy = true
attach_deny_insecure_transport_policy = true
attach_require_latest_tls_policy = true
+ attach_waf_log_delivery_policy = true
access_log_delivery_policy_source_accounts = [data.aws_caller_identity.current.account_id]
access_log_delivery_policy_source_buckets = ["arn:aws:s3:::${local.bucket_name}"]
diff --git a/main.tf b/main.tf
index 7261524d..10f8fdb5 100644
--- a/main.tf
+++ b/main.tf
@@ -12,7 +12,7 @@ locals {
create_bucket_acl = (var.acl != null && var.acl != "null") || length(local.grants) > 0
- attach_policy = var.attach_require_latest_tls_policy || var.attach_access_log_delivery_policy || var.attach_elb_log_delivery_policy || var.attach_lb_log_delivery_policy || var.attach_deny_insecure_transport_policy || var.attach_inventory_destination_policy || var.attach_deny_incorrect_encryption_headers || var.attach_deny_incorrect_kms_key_sse || var.attach_deny_unencrypted_object_uploads || var.attach_deny_ssec_encrypted_object_uploads || var.attach_policy
+ attach_policy = var.attach_require_latest_tls_policy || var.attach_access_log_delivery_policy || var.attach_elb_log_delivery_policy || var.attach_lb_log_delivery_policy || var.attach_deny_insecure_transport_policy || var.attach_inventory_destination_policy || var.attach_deny_incorrect_encryption_headers || var.attach_deny_incorrect_kms_key_sse || var.attach_deny_unencrypted_object_uploads || var.attach_deny_ssec_encrypted_object_uploads || var.attach_policy || var.attach_waf_log_delivery_policy
# Variables with type `any` should be jsonencode()'d when value is coming from Terragrunt
grants = try(jsondecode(var.grant), var.grant)
@@ -576,7 +576,8 @@ data "aws_iam_policy_document" "combined" {
var.attach_deny_incorrect_kms_key_sse ? data.aws_iam_policy_document.deny_incorrect_kms_key_sse[0].json : "",
var.attach_deny_incorrect_encryption_headers ? data.aws_iam_policy_document.deny_incorrect_encryption_headers[0].json : "",
var.attach_inventory_destination_policy || var.attach_analytics_destination_policy ? data.aws_iam_policy_document.inventory_and_analytics_destination_policy[0].json : "",
- var.attach_policy ? var.policy : ""
+ var.attach_policy ? var.policy : "",
+ var.attach_waf_log_delivery_policy ? data.aws_iam_policy_document.waf_log_delivery[0].json : "",
])
}
@@ -816,6 +817,79 @@ data "aws_iam_policy_document" "access_log_delivery" {
}
}
+#WAF
+data "aws_iam_policy_document" "waf_log_delivery" {
+ count = local.create_bucket && var.attach_waf_log_delivery_policy && !var.is_directory_bucket ? 1 : 0
+
+ statement {
+ sid = "AWSLogDeliveryWrite"
+
+ effect = "Allow"
+
+ principals {
+ type = "Service"
+ identifiers = ["delivery.logs.amazonaws.com"]
+ }
+
+ actions = [
+ "s3:PutObject",
+ ]
+
+ resources = [
+ "${aws_s3_bucket.this[0].arn}/AWSLogs/${data.aws_caller_identity.current.id}/*",
+ ]
+
+ condition {
+ test = "StringEquals"
+ values = ["bucket-owner-full-control"]
+ variable = "s3:x-amz-acl"
+ }
+
+ condition {
+ test = "StringEquals"
+ values = [data.aws_caller_identity.current.id]
+ variable = "aws:SourceAccount"
+ }
+
+ condition {
+ test = "ArnLike"
+ values = ["arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.id}:*"]
+ variable = "aws:SourceArn"
+ }
+ }
+
+ statement {
+ sid = "AWSLogDeliveryAclCheck"
+
+ effect = "Allow"
+
+ principals {
+ type = "Service"
+ identifiers = ["delivery.logs.amazonaws.com"]
+ }
+
+ actions = [
+ "s3:GetBucketAcl",
+ ]
+
+ resources = [
+ aws_s3_bucket.this[0].arn,
+ ]
+
+ condition {
+ test = "StringEquals"
+ values = [data.aws_caller_identity.current.id]
+ variable = "aws:SourceAccount"
+ }
+
+ condition {
+ test = "ArnLike"
+ values = ["arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.id}:*"]
+ variable = "aws:SourceArn"
+ }
+ }
+}
+
data "aws_iam_policy_document" "deny_insecure_transport" {
count = local.create_bucket && var.attach_deny_insecure_transport_policy && !var.is_directory_bucket ? 1 : 0
diff --git a/variables.tf b/variables.tf
index b4201ce0..86e03e2a 100644
--- a/variables.tf
+++ b/variables.tf
@@ -88,6 +88,12 @@ variable "attach_deny_ssec_encrypted_object_uploads" {
default = false
}
+variable "attach_waf_log_delivery_policy" {
+ description = "Controls if S3 bucket should have WAF log delivery policy attached"
+ type = bool
+ default = false
+}
+
variable "bucket" {
description = "(Optional, Forces new resource) The name of the bucket. If omitted, Terraform will assign a random, unique name."
type = string
diff --git a/wrappers/main.tf b/wrappers/main.tf
index b21a4885..c38ab67d 100644
--- a/wrappers/main.tf
+++ b/wrappers/main.tf
@@ -26,6 +26,7 @@ module "wrapper" {
attach_policy = try(each.value.attach_policy, var.defaults.attach_policy, false)
attach_public_policy = try(each.value.attach_public_policy, var.defaults.attach_public_policy, true)
attach_require_latest_tls_policy = try(each.value.attach_require_latest_tls_policy, var.defaults.attach_require_latest_tls_policy, false)
+ attach_waf_log_delivery_policy = try(each.value.attach_waf_log_delivery_policy, var.defaults.attach_waf_log_delivery_policy, false)
availability_zone_id = try(each.value.availability_zone_id, var.defaults.availability_zone_id, null)
block_public_acls = try(each.value.block_public_acls, var.defaults.block_public_acls, true)
block_public_policy = try(each.value.block_public_policy, var.defaults.block_public_policy, true)