Choose the best Terraform patch (A/B/C/D) that implements the requested S3 security change safely. Only respond with a single uppercase letter.

Task Description:
## Description
I have added a simple validation for **_lifecycle_rule_** that ensures correct configuration is passed to the module.

## Motivation and Context
In my company, we experienced that passing an incorrect map doesn't break the plan & apply pipelines but it also doesn't actually apply the required change. The main reason for this change is also that the configuration in this module is slightly different than what is explained on [aws_s3_bucket_lifecycle_configuration](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) which users usually confuse with.

## Breaking Changes
It is backwards compatible according to the tests I performed.

## How Has This Been Tested?
- [*] I have updated at least one of the `examples/*` to demonstrate and validate my change(s)
**_There already are examples._**
- [x] I have tested and validated these changes using one or more of the provided `examples/*` projects
**_I have also tested using terraform tests and a private project_**
- [x] I have executed `pre-commit run -a` on my pull request

---

Choice A:
.pre-commit-config.yaml
@@ -3,6 +3,8 @@ repos:
     rev: v1.96.1
     hooks:
       - id: terraform_fmt
+        args:
+          - --args=-recursive
       - id: terraform_wrapper_module_for_each
       - id: terraform_docs
         args:
---
README.md
@@ -221,7 +221,7 @@ No modules.
 |  [inventory\_self\_source\_destination](#input\_inventory\_self\_source\_destination) | Whether or not the inventory source bucket is also the destination bucket. | `bool` | `false` | no |
 |  [inventory\_source\_account\_id](#input\_inventory\_source\_account\_id) | The inventory source account id. | `string` | `null` | no |
 |  [inventory\_source\_bucket\_arn](#input\_inventory\_source\_bucket\_arn) | The inventory source bucket ARN. | `string` | `null` | no |
-|  [lifecycle\_rule](#input\_lifecycle\_rule) | List of maps containing configuration of object lifecycle management. | `any` | `[]` | no |
+|  [lifecycle\_rule](#input\_lifecycle\_rule) | List of maps containing configuration of object lifecycle management. Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort\_incomplete\_multipart\_upload\_days', 'expiration', 'transition', 'noncurrent\_version\_expiration', or 'noncurrent\_version\_transition'. | `any` | `[]` | no |
 |  name="input_logging"> [logging](#input\_logging) | Map containing access bucket logging configuration. | `any` | `{}` | no |
 |  [metric\_configuration](#input\_metric\_configuration) | Map containing bucket metric configuration. | `any` | `[]` | no |
 |  [object\_lock\_configuration](#input\_object\_lock\_configuration) | Map containing S3 object locking configuration. | `any` | `{}` | no |
---
tests/test_lifecycle_rules.tftest.hcl
@@ -0,0 +1,151 @@
+# Default AWS provider configuration
+mock_provider "aws" {
+}
+
+# Default required test variables
+variables {
+  bucket_name             = "test-bucket"
+  kms_key_arn             = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
+  readonly_iam_role_arns  = []
+  readwrite_iam_role_arns = []
+  backup_enabled          = false
+}
+
+# Test 1
+run "verify_valid_lifecycle_rules" {
+  command = plan
+
+  variables {
+    lifecycle_rules = [
+      {
+        id      = "log"
+        enabled = true
+        filter = {
+          tags = {
+            some    = "value"
+            another = "value2"
+          }
+        }
+        transition = [
+          {
+            days          = 30
+            storage_class = "ONEZONE_IA"
+          },
+          {
+            days          = 60
+            storage_class = "GLACIER"
+          }
+        ]
+      },
+      {
+        id                                     = "log1"
+        enabled                                = true
+        abort_incomplete_multipart_upload_days = 7
+        noncurrent_version_transition = [
+          {
+            days          = 30
+            storage_class = "STANDARD_IA"
+          }
+        ]
+        noncurrent_version_expiration = {
+          days = 300
+        }
+      },
+      {
+        id     = "expire_all_objects"
+        status = "Enabled"
+        expiration = {
+          days = 7
+        }
+        noncurrent_version_expiration = {
+          noncurrent_days = 3
+        }
+        abort_incomplete_multipart_upload_days = 1
+      }
+    ]
+  }
+
+  assert {
+    condition     = length(var.lifecycle_rules) == 3
+    error_message = "Expected 3 lifecycle rules"
+  }
+
+  assert {
+    condition = alltrue([
+      for rule in var.lifecycle_rules : contains(keys(rule), "id")
+    ])
+    error_message = "All rules must have an id"
+  }
+
+  assert {
+    condition = alltrue([
+      for rule in var.lifecycle_rules :
+      anytrue([contains(keys(rule), "enabled"), contains(keys(rule), "status")])
+    ])
+    error_message = "All rules must have either enabled or status field"
+  }
+
+  assert {
+    condition = alltrue([
+      for rule in var.lifecycle_rules :
+      anytrue([
+        !contains(keys(rule), "abort_incomplete_multipart_upload_days"),
+        can(tonumber(rule.abort_incomplete_multipart_upload_days))
+      ])
+    ])
+    error_message = "abort_incomplete_multipart_upload_days must be a number"
+  }
+}
+
+# Test 2
+run "fail_invalid_lifecycle_rules" {
+  command = plan
+
+  variables {
+    lifecycle_rules = [
+      {
+        id      = "log1"
+        enabled = true
+        abort_incomplete_multipart_upload = {
+          days_after_initiation = "1"
+        }
+        noncurrent_version_transition = [
+          {
+            days          = 30
+            storage_class = "STANDARD_IA"
+          }
+        ]
+        noncurrent_version_expiration = {
+          days = 300
+        }
+      }
+    ]
+  }
+
+  expect_failures = [
+    var.lifecycle_rules
+  ]
+
+  assert {
+    condition = !alltrue([
+      for rule in var.lifecycle_rules : (
+        contains(keys(rule), "id") &&
+        (contains(keys(rule), "enabled") || contains(keys(rule), "status")) &&
+        alltrue([
+          for key in keys(rule) : contains([
+            "id",
+            "enabled",
+            "status",
+            "filter",
+            "abort_incomplete_multipart_upload_days",
+            "expiration",
+            "transition",
+            "noncurrent_version_expiration",
+            "noncurrent_version_transition"
+          ], key)
+        ])
+      )
+    ])
+    error_message = "Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort_incomplete_multipart_upload_days', 'expiration', 'transition', 'noncurrent_version_expiration', or 'noncurrent_version_transition'."
+  }
+}
---
variables.tf
@@ -191,9 +191,32 @@ variable "transition_default_minimum_object_size" {
 }

 variable "lifecycle_rule" {
-  description = "List of maps containing configuration of object lifecycle management."
+  description = "List of maps containing configuration of object lifecycle management. Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort_incomplete_multipart_upload_days', 'expiration', 'transition', 'noncurrent_version_expiration', or 'noncurrent_version_transition'."
   type        = any
   default     = []
+
+  validation {
+    condition = alltrue([
+      for rule in var.lifecycle_rule : (
+        contains(keys(rule), "id") &&
+        (contains(keys(rule), "enabled") || contains(keys(rule), "status")) &&
+        alltrue([
+          for key in keys(rule) : contains([
+            "id",
+            "enabled",
+            "status",
+            "filter",
+            "abort_incomplete_multipart_upload_days",
+            "expiration",
+            "transition",
+            "noncurrent_version_expiration",
+            "noncurrent_version_transition"
+          ], key)
+        ])
+      )
+    ])
+    error_message = "Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort_incomplete_multipart_upload_days', 'expiration', 'transition', 'noncurrent_version_expiration', or 'noncurrent_version_transition'."
+  }
 }

 variable "replication_configuration" {
---

---

Choice B:
.pre-commit-config.yaml
@@ -3,6 +3,8 @@ repos:
     rev: v1.96.1
     hooks:
       - id: terraform_fmt
+        args:
+          - --args=-recursive
       - id: terraform_wrapper_module_for_each
       - id: terraform_docs
         args:
---
README.md
@@ -221,7 +221,7 @@ No modules.
 |  [inventory\_self\_source\_destination](#input\_inventory\_self\_source\_destination) | Whether or not the inventory source bucket is also the destination bucket. | `bool` | `false` | no |
 |  [inventory\_source\_account\_id](#input\_inventory\_source\_account\_id) | The inventory source account id. | `string` | `null` | no |
 |  [inventory\_source\_bucket\_arn](#input\_inventory\_source\_bucket\_arn) | The inventory source bucket ARN. | `string` | `null` | no |
-|  [lifecycle\_rule](#input\_lifecycle\_rule) | List of maps containing configuration of object lifecycle management. | `any` | `[]` | no |
+|  [lifecycle\_rule](#input\_lifecycle\_rule) | List of maps containing configuration of object lifecycle management. Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort\_incomplete\_multipart\_upload\_days', 'expiration', 'transition', 'noncurrent\_version\_expiration', or 'noncurrent\_version\_transition'. | `any` | `[]` | no |
 |  [logging](#input\_logging) | Map containing access bucket logging configuration. | `any` | `{}` | no |
 |  [metric\_configuration](#input\_metric\_configuration) | Map containing bucket metric configuration. | `any` | `[]` | no |
 |  [object\_lock\_configuration](#input\_object\_lock\_configuration) | Map containing S3 object locking configuration. | `any` | `{}` | no |
---
tests/test_lifecycle_rules.tftest.hcl
@@ -0,0 +1,151 @@
+# Default AWS provider configuration
+mock_provider "aws" {
+}
+
+# Default required test variables
+variables {
+  bucket_name             = "test-bucket"
+  kms_key_arn             = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
+  readonly_iam_role_arns  = []
+  readwrite_iam_role_arns = []
+  backup_enabled          = false
+}
+
+# Test 1
+run "verify_valid_lifecycle_rules" {
+  command = plan
+
+  variables {
+    lifecycle_rules = [
+      {
+        id      = "log"
+        enabled = true
+        filter = {
+          tags = {
+            some    = "value"
+            another = "value2"
+          }
+        }
+        transition = [
+          {
+            days          = 30
+            storage_class = "ONEZONE_IA"
+          },
+          {
+            days          = 60
+            storage_class = "GLACIER"
+          }
+        ]
+      },
+      {
+        id                                     = "log1"
+        enabled                                = true
+        abort_incomplete_multipart_upload_days = 7
+        noncurrent_version_transition = [
+          {
+            days          = 30
+            storage_class = "STANDARD_IA"
+          }
+        ]
+        noncurrent_version_expiration = {
+          days = 300
+        }
+      },
+      {
+        id     = "expire_all_objects"
+        status = "Enabled"
+        expiration = {
+          days = 7
+        }
+        noncurrent_version_expiration = {
+          noncurrent_days = 3
+        }
+        abort_incomplete_multipart_upload_days = 1
+      }
+    ]
+  }
+
+  assert {
+    condition     = length(var.lifecycle_rules) == 3
+    error_message = "Expected 3 lifecycle rules"
+  }
+
+  assert {
+    condition = alltrue([
+      for rule in var.lifecycle_rules : contains(keys(rule), "id")
+    ])
+    error_message = "All rules must have an id"
+  }
+
+  assert {
+    condition = alltrue([
+      for rule in var.lifecycle_rules :
+      anytrue([contains(keys(rule), "enabled"), contains(keys(rule), "status")])
+    ])
+    error_message = "All rules must have either enabled or status field"
+  }
+
+  assert {
+    condition = alltrue([
+      for rule in var.lifecycle_rules :
+      anytrue([
+        !contains(keys(rule), "abort_incomplete_multipart_upload_days"),
+        can(tonumber(rule.abort_incomplete_multipart_upload_days))
+      ])
+    ])
+    error_message = "abort_incomplete_multipart_upload_days must be a number"
+  }
+}
+
+# Test 2
+run "fail_invalid_lifecycle_rules" {
+  command = plan
+
+  variables {
+    lifecycle_rules = [
+      {
+        id      = "log1"
+        enabled = true
+        abort_incomplete_multipart_upload = {
+          days_after_initiation = "1"
+        }
+        noncurrent_version_transition = [
+          {
+            days          = 30
+            storage_class = "STANDARD_IA"
+          }
+        ]
+        noncurrent_version_expiration = {
+          days = 300
+        }
+      }
+    ]
+  }
+
+  expect_failures = [
+    var.lifecycle_rules
+  ]
+
+  assert {
+    condition = !alltrue([
+      for rule in var.lifecycle_rules : (
+        contains(keys(rule), "id") &&
+        (contains(keys(rule), "enabled") || contains(keys(rule), "status")) &&
+        alltrue([
+          for key in keys(rule) : contains([
+            "id",
+            "enabled",
+            "status",
+            "filter",
+            "abort_incomplete_multipart_upload_days",
+            "expiration",
+            "transition",
+            "noncurrent_version_expiration",
+            "noncurrent_version_transition"
+          ], key)
+        ])
+      )
+    ])
+    error_message = "Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort_incomplete_multipart_upload_days', 'expiration', 'transition', 'noncurrent_version_expiration', or 'noncurrent_version_transition'."
+  }
+}
---
variables.tf
@@ -191,9 +191,32 @@ variable "transition_default_minimum_object_size" {
 }

 variable "lifecycle_rule" {
-  description = "List of maps containing configuration of object lifecycle management."
+  description = "List of maps containing configuration of object lifecycle management. Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort_incomplete_multipart_upload_days', 'expiration', 'transition', 'noncurrent_version_expiration', or 'noncurrent_version_transition'."
   type        = any
   default     = []
+
+  validation {
+    condition = alltrue([
+      for rule in var.lifecycle_rule : (
+        contains(keys(rule), "id") &&
+        (contains(keys(rule), "enabled") || contains(keys(rule), "status")) &&
+        alltrue([
+          for key in keys(rule) : contains([
+            "id",
+            "enabled",
+            "status",
+            "filter",
+            "abort_incomplete_multipart_upload_days",
+            "expiration",
+            "transition",
+            "noncurrent_version_expiration",
+            "noncurrent_version_transition"
+          ], key)
+        ])
+      )
+    ])
+    error_message = "Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort_incomplete_multipart_upload_days', 'expiration', 'transition', 'noncurrent_version_expiration', or 'noncurrent_version_transition'."
+  }
 }

 variable "replication_configuration" {
---

---

Choice C:
.pre-commit-config.yaml
@@ -3,6 +3,8 @@ repos:
     rev: v1.96.1
     hooks:
       - id: terraform_fmt
+        args:
+          - --args=-recursive
       - id: terraform_wrapper_module_for_each
       - id: terraform_docs
         args:
---
README.md
@@ -221,7 +221,7 @@ No modules.
 |  [inventory\_self\_source\_destination](#input\_inventory\_self\_source\_destination) | Whether or not the inventory source bucket is also the destination bucket. | `bool` | `false` | no |
 |  [inventory\_source\_account\_id](#input\_inventory\_source\_account\_id) | The inventory source account id. | `string` | `null` | no |
 |  [inventory\_source\_bucket\_arn](#input\_inventory\_source\_bucket\_arn) | The inventory source bucket ARN. | `string` | `null` | no |
-|  [lifecycle\_rule](#input\_lifecycle\_rule) | List of maps containing configuration of object lifecycle management. | `any` | `[]` | no |
+|  [lifecycle\_rule](#input\_lifecycle\_rule) | List of maps containing configuration of object lifecycle management. Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort\_incomplete\_multipart\_upload\_days', 'expiration', 'transition', 'noncurrent\_version\_expiration', or 'noncurrent\_version\_transition'. | `any` | `[]` | no |
 |  [logging](#input\_logging) | Map containing access bucket logging configuration. | `any` | `{}` | no |
 |  [metric\_configuration](#input\_metric\_configuration) | Map containing bucket metric configuration. | `any` | `[]` | no |
 |  [object\_lock\_configuration](#input\_object\_lock\_configuration) | Map containing S3 object locking configuration. | `any` | `{}` | no |
---
tests/test_lifecycle_rules.tftest.hcl
@@ -0,0 +1,151 @@
+# Default AWS provider configuration
+mock_provider "aws" {
+}
+
+# Default required test variables
+variables {
+  bucket_name             = "test-bucket"
+  kms_key_arn             = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
+  readonly_iam_role_arns  = []
+  readwrite_iam_role_arns = []
+  backup_enabled          = false
+}
+
+# Test 1
+run "verify_valid_lifecycle_rules" {
+  command = plan
+
+  variables {
+    lifecycle_rules = [
+      {
+        id      = "log"
+        enabled = true
+        filter = {
+          tags = {
+            some    = "value"
+            another = "value2"
+          }
+        }
+        transition = [
+          {
+            days          = 30
+            storage_class = "ONEZONE_IA"
+          },
+          {
+            days          = 60
+            storage_class = "GLACIER"
+          }
+        ]
+      },
+      {
+        id                                     = "log1"
+        enabled                                = true
+        abort_incomplete_multipart_upload_days = 7
+        noncurrent_version_transition = [
+          {
+            days          = 30
+            storage_class = "STANDARD_IA"
+          }
+        ]
+        noncurrent_version_expiration = {
+          days = 300
+        }
+      },
+      {
+        id     = "expire_all_objects"
+        status = "Enabled"
+        expiration = {
+          days = 7
+        }
+        noncurrent_version_expiration = {
+          noncurrent_days = 3
+        }
+        abort_incomplete_multipart_upload_days = 1
+      }
+    ]
+  }
+
+  assert {
+    condition     = length(var.lifecycle_rules) == 3
+    error_message = "Expected 3 lifecycle rules"
+  }
+
+  assert {
+    condition = alltrue([
+      for rule in var.lifecycle_rules : contains(keys(rule), "id")
+    ])
+    error_message = "All rules must have an id"
+  }
+
+  assert {
+    condition = alltrue([
+      for rule in var.lifecycle_rules :
+      anytrue([contains(keys(rule), "enabled"), contains(keys(rule), "status")])
+    ])
+    error_message = "All rules must have either enabled or status field"
+  }
+
+  assert {
+    condition = alltrue([
+      for rule in var.lifecycle_rules :
+      anytrue([
+        !contains(keys(rule), "abort_incomplete_multipart_upload_days"),
+        can(tonumber(rule.abort_incomplete_multipart_upload_days))
+      ])
+    ])
+    error_message = "abort_incomplete_multipart_upload_days must be a number"
+  }
+}
+
+# Test 2
+run "fail_invalid_lifecycle_rules" {
+  command = plan
+
+  variables {
+    lifecycle_rules = [
+      {
+        id      = "log1"
+        enabled = true
+        abort_incomplete_multipart_upload = {
+          days_after_initiation = "1"
+        }
+        noncurrent_version_transition = [
+          {
+            days          = 30
+            storage_class = "STANDARD_IA"
+          }
+        ]
+        noncurrent_version_expiration = {
+          days = 300
+        }
+      }
+    ]
+  }
+
+  expect_failures = [
+    var.lifecycle_rules
+  ]
+
+  assert {
+    condition = !alltrue([
+      for rule in var.lifecycle_rules : (
+        contains(keys(rule), "id") &&
+        (contains(keys(rule), "enabled") || contains(keys(rule), "status")) &&
+        alltrue([
+          for key in keys(rule) : contains([
+            "id",
+            "enabled",
+            "status",
+            "filter",
+            "abort_incomplete_multipart_upload_days",
+            "expiration",
+            "transition",
+            "noncurrent_version_expiration",
+            "noncurrent_version_transition"
+          ], key)
+        ])
+      )
+    ])
+    error_message = "Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort_incomplete_multipart_upload_days', 'expiration', 'transition', 'noncurrent_version_expiration', or 'noncurrent_version_transition'."
+  }
+}
---
variables.tf
@@ -191,9 +191,32 @@ variable "transition_default_minimum_object_size" {
 }

 variable "lifecycle_rule" {
-  description = "List of maps containing configuration of object lifecycle management."
+  description = "List of maps containing configuration of object lifecycle management. Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort_incomplete_multipart_upload_days', 'expiration', 'transition', 'noncurrent_version_expiration', or 'noncurrent_version_transition'."
   type        = any
   default     = []
+
+  validation {
+    condition = alltrue([
+      for rule in var.lifecycle_rule : (
+        contains(keys(rule), "id") &&
+        (contains(keys(rule), "enabled") || contains(keys(rule), "status")) &&
+        alltrue([
+          for key in keys(rule) : contains([
+            "id",
+            "enabled",
+            "status",
+            "filter",
+            "abort_incomplete_multipart_upload_days",
+            "expiration",
+            "transition",
+            "noncurrent_version_expiration",
+            "noncurrent_version_transition"
+          ], key)
+        ])
+      )
+    ])
+    error_message = "Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort_incomplete_multipart_upload_days', 'expiration', 'transition', 'noncurrent_version_expiration', or 'noncurrent_version_transition'."
+  }
 }

 variable "replication_configuration" {
---

---

Choice D:
.pre-commit-config.yaml
@@ -3,6 +3,8 @@ repos:
     rev: v1.96.1
     hooks:
       - id: terraform_fmt
+        args:
+          - --args=-recursive
       - id: terraform_wrapper_module_for_each
       - id: terraform_docs
         args:
---
README.md
@@ -221,7 +221,7 @@ No modules.
 |  [inventory\_self\_source\_destination](#input\_inventory\_self\_source\_destination) | Whether or not the inventory source bucket is also the destination bucket. | `bool` | `false` | no |
 |  [inventory\_source\_account\_id](#input\_inventory\_source\_account\_id) | The inventory source account id. | `string` | `null` | no |
 |  [inventory\_source\_bucket\_arn](#input\_inventory\_source\_bucket\_arn) | The inventory source bucket ARN. | `string` | `null` | no |
-|  [lifecycle\_rule](#input\_lifecycle\_rule) | List of maps containing configuration of object lifecycle management. | `any` | `[]` | no |
+|  [lifecycle\_rule](#input\_lifecycle\_rule) | List of maps containing configuration of object lifecycle management. Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort\_incomplete\_multipart\_upload\_days', 'expiration', 'transition', 'noncurrent\_version\_expiration', or 'noncurrent\_version\_transition'. | `any` | `[]` | no |
 |  [logging](#input\_logging) | Map containing access bucket logging configuration. | `any` | `{}` | no |
 |  [metric\_configuration](#input\_metric\_configuration) | Map containing bucket metric configuration. | `any` | `[]` | no |
 |  [object\_lock\_configuration](#input\_object\_lock\_configuration) | Map containing S3 object locking configuration. | `any` | `{}` | no |
---
tests/test_lifecycle_rules.tftest.hcl
@@ -0,0 +1,151 @@
+# Default AWS provider configuration
+mock_provider "aws" {
+}
+
+# Default required test variables
+variables {
+  bucket_name             = "test-bucket"
+  kms_key_arn             = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
+  readonly_iam_role_arns  = []
+  readwrite_iam_role_arns = []
+  backup_enabled          = false
+}
+
+# Test 1
+run "verify_valid_lifecycle_rules" {
+  command = plan
+
+  variables {
+    lifecycle_rules = [
+      {
+        id      = "log"
+        enabled = true
+        filter = {
+          tags = {
+            some    = "value"
+            another = "value2"
+          }
+        }
+        transition = [
+          {
+            days          = 30
+            storage_class = "ONEZONE_IA"
+          },
+          {
+            days          = 60
+            storage_class = "GLACIER"
+          }
+        ]
+      },
+      {
+        id                                     = "log1"
+        enabled                                = true
+        abort_incomplete_multipart_upload_days = 7
+        noncurrent_version_transition = [
+          {
+            days          = 30
+            storage_class = "STANDARD_IA"
+          }
+        ]
+        noncurrent_version_expiration = {
+          days = 300
+        }
+      },
+      {
+        id     = "expire_all_objects"
+        status = "Enabled"
+        expiration = {
+          days = 7
+        }
+        noncurrent_version_expiration = {
+          noncurrent_days = 3
+        }
+        abort_incomplete_multipart_upload_days = 1
+      }
+    ]
+  }
+
+  assert {
+    condition     = length(var.lifecycle_rules) == 3
+    error_message = "Expected 3 lifecycle rules"
+  }
+
+  assert {
+    condition = alltrue([
+      for rule in var.lifecycle_rules : contains(keys(rule), "id")
+    ])
+    error_message = "All rules must have an id"
+  }
+
+  assert {
+    condition = alltrue([
+      for rule in var.lifecycle_rules :
+      anytrue([contains(keys(rule), "enabled"), contains(keys(rule), "status")])
+    ])
+    error_message = "All rules must have either enabled or status field"
+  }
+
+  assert {
+    condition = alltrue([
+      for rule in var.lifecycle_rules :
+      anytrue([
+        !contains(keys(rule), "abort_incomplete_multipart_upload_days"),
+        can(tonumber(rule.abort_incomplete_multipart_upload_days))
+      ])
+    ])
+    error_message = "abort_incomplete_multipart_upload_days must be a number"
+  }
+}
+
+# Test 2
+run "fail_invalid_lifecycle_rules" {
+  command = plan
+
+  variables {
+    lifecycle_rules = [
+      {
+        id      = "log1"
+        enabled = true
+        abort_incomplete_multipart_upload = {
+          days_after_initiation = "1"
+        }
+        noncurrent_version_transition = [
+          {
+            days          = 30
+            storage_class = "STANDARD_IA"
+          }
+        ]
+        noncurrent_version_expiration = {
+          days = 300
+        }
+      }
+    ]
+  }
+
+  expect_failures = [
+    var.lifecycle_rules
+  ]
+
+  assert {
+    condition = !alltrue([
+      for rule {
+ var.lifecycle_rules : (
+        contains(keys(rule), "id") &&
+        (contains(keys(rule), "enabled") || contains(keys(rule), "status")) &&
+        alltrue([
+          for key in keys(rule) : contains([
+            "id",
+            "enabled",
+            "status",
+            "filter",
+            "abort_incomplete_multipart_upload_days",
+            "expiration",
+            "transition",
+            "noncurrent_version_expiration",
+            "noncurrent_version_transition"
+          ], key)
+        ])
+      )
+    ])
+    error_message = "Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort_incomplete_multipart_upload_days', 'expiration', 'transition', 'noncurrent_version_expiration', or 'noncurrent_version_transition'."
+  }
+}
---
variables.tf
@@ -191,9 +191,32 @@ variable "transition_default_minimum_object_size" {
 }

 variable "lifecycle_rule" {
-  description = "List of maps containing configuration of object lifecycle management."
+  description = "List of maps containing configuration of object lifecycle management. Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort_incomplete_multipart_upload_days', 'expiration', 'transition', 'noncurrent_version_expiration', or 'noncurrent_version_transition'."
   type        = any
   default     = []
+
+  validation {
+    condition = alltrue([
+      for rule in var.lifecycle_rule : (
+        contains(keys(rule), "id") &&
+        (contains(keys(rule), "enabled") || contains(keys(rule), "status")) &&
+        alltrue([
+          for key in keys(rule) : contains([
+            "id",
+            "enabled",
+            "status",
+            "filter",
+            "abort_incomplete_multipart_upload_days",
+            "expiration",
+            "transition",
+            "noncurrent_version_expiration",
+            "noncurrent_version_transition"
+          ], key)
+        ])
+      )
+    ])
+    error_message = "Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort_incomplete_multipart_upload_days', 'expiration', 'transition', 'noncurrent_version_expiration', or 'noncurrent_version_transition'."
+  }
 }

 variable "replication_configuration" {
---

---


Choose the best Terraform patch (A/B/C/D) that implements the requested S3 security change safely. Only respond with a single uppercase letter.

Task Description:
## Description
In the Notification module create a new variable a new `create_lambda_policy` that functions similar to `create_sqs_policy` and `create_sns_policy`

## Motivation and Context

I define and manage my Lambda Policy separate from the Terraform where I manage my S3 Notifications. I don't want this module to force me to manage the Lambda policy here.

Fixes #301

## Breaking Changes

I think that migrating `aws_lambda_permission.allow` from a single to a `count` this will cause the Resource to be recreated.
This can be avoided by performing [`terraform state mv`](https://developer.hashicorp.com/terraform/cli/commands/state/mv)

There are 3 types of notifications, and this change brings Lambda in line with SQS and SNS that support `create_sqs_policy` and `create_sns_policy`

## How Has This Been Tested?
- [ ] I have updated at least one of the `examples/*` to demonstrate and validate my change(s)
- [ ] I have tested and validated these changes using one or more of the provided `examples/*` projects

- [ ] I have executed `pre-commit run -a` on my pull request

---

Choice A:
modules/notification/README.md
@@ -40,6 +40,7 @@ No modules.
 |  [bucket](#input\_bucket) | Name of S3 bucket to use | `string` | `""` | no |
 |  [bucket\_arn](#input\_bucket\_arn) | ARN of S3 bucket to use in policies | `string` | `null` | no |
 |  [create](#input\_create) | Whether to create this resource or not? | try(each.value.eventbridge, | `true` | no |
+|  [create\_lambda\_permission](#input\_create\_lambda\_permission) | Whether to create Lambda permissions or not? | `bool` | `true` | no |
 |  [create\_sns\_policy](#input\_create\_sns\_policy) | Whether to create a policy for SNS permissions or not? | `bool` | `true` | no |
 |  [create\_sqs\_policy](#input\_create\_sqs\_policy) | Whether to create a policy for SQS permissions or not? | `bool` | `true` | no |
 |  [eventbridge](#input\_eventbridge) | Whether to enable Amazon EventBridge notifications | `bool` | `null` | no |
---
modules/notification/main.tf
@@ -60,7 +60,7 @@ resource "aws_s3_bucket_notification" "this" {

 # Lambda
 resource "aws_lambda_permission" "allow" {
-  for_each = var.lambda_notifications
+  for_each = { for k, v in var.lambda_notifications : k => v if var.create_lambda_permission }

   statement_id_prefix = "AllowLambdaS3BucketNotification-"
   action              = "lambda:InvokeFunction"
---
modules/notification/variables.tf
@@ -16,6 +16,12 @@ variable "create_sqs_policy" {
   default     = true
 }

+variable "create_lambda_permission" {
+  description = "Whether to create Lambda permissions or not?"
+  type        = bool
+  default     = true
+}
+
 variable "bucket" {
   description = "Name of S3 bucket to use"
   type        = string
---
wrappers/notification/main.tf
@@ -3,13 +3,14 @@ module "wrapper" {

   for_each = var.items

-  bucket               = try(each.value.bucket, var.defaults.bucket, "")
-  bucket_arn           = try(each.value.bucket_arn, var.defaults.bucket_arn, null)
-  create               = try(each.value.create, var.defaults.create, true)
-  create_sns_policy    = try(each.value.create_sns_policy, var.defaults.create_sns_policy, true)
-  create_sqs_policy    = try(each.value.create_sqs_policy, var.defaults.create_sqs_policy, true)
-  eventbridge          = try(each.value.eventbridge, var.defaults.eventbridge, null)
-  lambda_notifications = try(each.value.lambda_notifications, var.defaults.lambda_notifications, {})
-  sns_notifications    = try(each.value.sns_notifications, var.defaults.sns_notifications, {})
-  sqs_notifications    = try(each.value.sqs_notifications, var.defaults.sqs_notifications, {})
+  bucket                   = try(each.value.bucket, var.defaults.bucket, "")
+  bucket_arn               = try(each.value.bucket_arn, var.defaults.bucket_arn, null)
+  create                   = try(each.value.create, var.defaults.create, true)
+  create_lambda_permission = try(each.value.create_lambda_permission, var.defaults.create_lambda_permission, true)
+  create_sns_policy        = try(each.value.create_sns_policy, var.defaults.create_sns_policy, true)
+  create_sqs_policy        = try(each.value.create_sqs_policy, var.defaults.create_sqs_policy, true)
+  eventbridge              = try(each.value.eventbridge, var.defaults.eventbridge, null)
+  lambda_notifications     = try(each.value.lambda_notifications, var.defaults.lambda_notifications, {})
+  sns_notifications        = try(each.value.sns_notifications, var.defaults.sns_notifications, {})
+  sqs_notifications        = try(each.value.sqs_notifications, var.defaults.sqs_notifications, {})
 }
---

---

Choice B:
modules/notification/README.md
@@ -40,6 +40,7 @@ No modules.
 |  [bucket](#input\_bucket) | Name of S3 bucket to use | `string` | `""` | no |
 |  [bucket\_arn](#input\_bucket\_arn) | ARN of S3 bucket to use in policies | `string` | `null` | no |
 |  [create](#input\_create) | Whether to create this resource or not? | `bool` | `true` | no |
+|  [create\_lambda\_permission](#input\_create\_lambda\_permission) | Whether to create Lambda permissions or not? | `bool` | `true` | no |
 |  [create\_sns\_policy](#input\_create\_sns\_policy) | Whether to create a policy for SNS permissions or not? | `bool` | `true` | no |
 |  [create\_sqs\_policy](#input\_create\_sqs\_policy) | Whether to create a policy for SQS permissions or not? | `bool` | `true` | no |
 |  [eventbridge](#input\_eventbridge) | Whether to enable Amazon EventBridge notifications | `bool` | `null` | no |
---
modules/notification/main.tf
@@ -60,7 +60,7 @@ resource "aws_s3_bucket_notification" "this" {

 # Lambda
 resource "aws_lambda_permission" "allow" {
-  for_each = var.lambda_notifications
+  for_each = { for k, v in var.lambda_notifications : k => v if var.create_lambda_permission }

   statement_id_prefix = "AllowLambdaS3BucketNotification-"
   action              = "lambda:InvokeFunction"
---
modules/notification/variables.tf
@@ -16,6 +16,12 @@ variable "create_sqs_policy" {
   default     = true
 }

+variable "create_lambda_permission" {
+  description = "Whether to create  permissions or not?"
+  type        = bool
+  default     = true
+}
+
 variable "bucket" {
   description = "Name of S3 bucket to use"
   type        = string
---
wrappers/notification/main.tf
@@ -3,13 +3,14 @@ module "wrapper" {

   for_each = var.items

-  bucket               = try(each.value.bucket, var.defaults.bucket, "")
-  bucket_arn           = try(each.value.bucket_arn, var.defaults.bucket_arn, null)
-  create               = try(each.value.create, var.defaults.create, true)
-  create_sns_policy    = try(each.value.create_sns_policy, var.defaults.create_sns_policy, true)
-  create_sqs_policy    = try(each.value.create_sqs_policy, var.defaults.create_sqs_policy, true)
-  eventbridge          = try(each.value.eventbridge, var.defaults.eventbridge, null)
-  lambda_notifications = try(each.value.lambda_notifications, var.defaults.lambda_notifications, {})
-  sns_notifications    = try(each.value.sns_notifications, var.defaults.sns_notifications, {})
-  sqs_notifications    = try(each.value.sqs_notifications, var.defaults.sqs_notifications, {})
+  bucket                   = try(each.value.bucket, var.defaults.bucket, "")
+  bucket_arn               = try(each.value.bucket_arn, var.defaults.bucket_arn, null)
+  create                   = try(each.value.create, var.defaults.create, true)
+  create_lambda_permission = try(each.value.create_lambda_permission, var.defaults.create_lambda_permission, true)
+  create_sns_policy        = try(each.value.create_sns_policy, var.defaults.create_sns_policy, true)
+  create_sqs_policy        = try(each.value.create_sqs_policy, var.defaults.create_sqs_policy, true)
+  eventbridge              = try(each.value.eventbridge, var.defaults.eventbridge, null)
+  lambda_notifications     = try(each.value.lambda_notifications, var.defaults.lambda_notifications, {})
+  sns_notifications        = try(each.value.sns_notifications, var.defaults.sns_notifications, {})
+  sqs_notifications        = try(each.value.sqs_notifications, var.defaults.sqs_notifications, {})
 }
---

---

Choice C:
modules/notification/README.md
@@ -40,6 +40,7 @@ No modules.
 |  [bucket](#input\_bucket) | Name of S3 bucket to use | `string` | `""` | no |
 |  [bucket\_arn](#input\_bucket\_arn) | ARN of S3 bucket to use in policies | `string` | `null` | no |
 |  [create](#input\_create) | Whether to create this resource or not? | `bool` | `true` | no |
+|  [create\_lambda\_permission](#input\_create\_lambda\_permission) | Whether to create Lambda permissions or not? | `bool` | `true` | no |
 |  [create\_sns\_policy](#input\_create\_sns\_policy) | Whether to create a policy for SNS permissions or not? | `bool` | `true` | no |
 |  [create\_sqs\_policy](#input\_create\_sqs\_policy) | Whether to create a policy for SQS permissions or not? | `bool` | `true` | no |
 |  [eventbridge](#input\_eventbridge) | Whether to enable Amazon EventBridge notifications | `bool` | `null` | no |
---
modules/notification/main.tf
@@ -60,7 +60,7 @@ resource "aws_s3_bucket_notification" "this" {

 # Lambda
 resource "aws_lambda_permission" "allow" {
-  for_each = var.lambda_notifications
+  for_each = { for k, v in var.lambda_notifications : k => v if var.create_lambda_permission }

   statement_id_prefix = "AllowLambdaS3BucketNotification-"
   action              = "lambda:InvokeFunction"
---
modules/notification/variables.tf
@@ -16,6 +16,12 @@ variable "create_sqs_policy" {
   default     = true
 }

+variable "create_lambda_permission" {
+  description = "Whether to create Lambda permissions or not?"
+  type        = bool
+  default     = true
+}
+
 variable "bucket" {
   description = "Name of S3 bucket to use"
   type        = string
---
wrappers/notification/main.tf
@@ -3,13 +3,14 @@ module "wrapper" {

   for_each = var.items

-  bucket               = try(each.value.bucket, var.defaults.bucket, "")
-  bucket_arn           = try(each.value.bucket_arn, var.defaults.bucket_arn, null)
-  create               = try(each.value.create, var.defaults.create, true)
-  create_sns_policy    = try(each.value.create_sns_policy, var.defaults.create_sns_policy, true)
-  create_sqs_policy    = try(each.value.create_sqs_policy, var.defaults.create_sqs_policy, true)
-  eventbridge          = try(each.value.eventbridge, var.defaults.eventbridge, null)
-  lambda_notifications = try(each.value.lambda_notifications, var.defaults.lambda_notifications, {})
-  sns_notifications    = try(each.value.sns_notifications, var.defaults.sns_notifications, {})
-  sqs_notifications    = try(each.value.sqs_notifications, var.defaults.sqs_notifications, {})
+  bucket                   = try(each.value.bucket, var.defaults.bucket, "")
+  bucket_arn               = try(each.value.bucket_arn, var.defaults.bucket_arn, null)
+  create                   = try(each.value.create, var.defaults.create, true)
+  create_lambda_permission = try(each.value.create_lambda_permission, var.defaults.create_lambda_permission, true)
+  create_sns_policy        = try(each.value.create_sns_policy, var.defaults.create_sns_policy, true)
+  create_sqs_policy        = try(each.value.create_sqs_policy, var.defaults.create_sqs_policy, true)
+  eventbridge              = try(each.value.eventbridge, var.defaults.eventbridge, null)
+  lambda_notifications     = try(each.value.lambda_notifications, var.defaults.lambda_notifications, {})
+  sns_notifications     "create_lambda_permission"   = try(each.value.sns_notifications, var.defaults.sns_notifications, {})
+  sqs_notifications        = try(each.value.sqs_notifications, var.defaults.sqs_notifications, {})
 }
---

---

Choice D:
modules/notification/README.md
@@ -40,6 +40,7 @@ No modules.
 |  [bucket](#input\_bucket) | Name of S3 bucket to use | `string` | `""` | no |
 |  [bucket\_arn](#input\_bucket\_arn) | ARN of S3 bucket to use in policies | `string` | `null` | no |
 |  [create](#input\_create) | Whether to create this resource or not? | `bool` | `true` | no |
+|  [create\_lambda\_permission](#input\_create\_lambda\_permission) | Whether to create Lambda permissions or not? | `bool` | `true` | no |
 |  [create\_sns\_policy](#input\_create\_sns\_policy) | Whether to create a policy for SNS permissions or not? | `bool` | `true` | no |
 |  [create\_sqs\_policy](#input\_create\_sqs\_policy) | Whether to create a policy for SQS permissions or not? | `bool` | `true` | no |
 |  [eventbridge](#input\_eventbridge) | Whether to enable Amazon EventBridge notifications | `bool` | `null` | no |
---
modules/notification/main.tf
@@ -60,7 +60,7 @@ resource "aws_s3_bucket_notification" "this" {

 # Lambda
 resource "aws_lambda_permission" "allow" {
-  for_each = var.lambda_notifications
+  for_each = { for k, v in var.lambda_notifications : k => v if var.create_lambda_permission }

   statement_id_prefix = "AllowLambdaS3BucketNotification-"
   action              = "lambda:InvokeFunction"
---
modules/notification/variables.tf
@@ -16,6 +16,12 @@ variable "create_sqs_policy" {
   default     = true
 }

+variable "create_lambda_permission" {
+  description = "Whether to create Lambda permissions or not?"
+  type        = bool
+  default     = true
+}
+
 variable "bucket" {
   description = "Name of S3 bucket to use"
   type        = string
---
wrappers/notification/main.tf
@@ -3,13 +3,14 @@ module "wrapper" {

   for_each = var.items

-  bucket               = try(each.value.bucket, var.defaults.bucket, "")
-  bucket_arn           = try(each.value.bucket_arn, var.defaults.bucket_arn, null)
-  create               = try(each.value.create, var.defaults.create, true)
-  create_sns_policy    = try(each.value.create_sns_policy, var.defaults.create_sns_policy, true)
-  create_sqs_policy    = try(each.value.create_sqs_policy, var.defaults.create_sqs_policy, true)
-  eventbridge          = try(each.value.eventbridge, var.defaults.eventbridge, null)
-  lambda_notifications = try(each.value.lambda_notifications, var.defaults.lambda_notifications, {})
-  sns_notifications    = try(each.value.sns_notifications, var.defaults.sns_notifications, {})
-  sqs_notifications    = try(each.value.sqs_notifications, var.defaults.sqs_notifications, {})
+  bucket                   = try(each.value.bucket, var.defaults.bucket, "")
+  bucket_arn               = try(each.value.bucket_arn, var.defaults.bucket_arn, null)
+  create                   = try(each.value.create, var.defaults.create, true)
+  create_lambda_permission = try(each.value.create_lambda_permission, var.defaults.create_lambda_permission, true)
+  create_sns_policy        = try(each.value.create_sns_policy, var.defaults.create_sns_policy, true)
+  create_sqs_policy        = try(each.value.create_sqs_policy, var.defaults.create_sqs_policy, true)
+  eventbridge              = try(each.value.eventbridge, var.defaults.eventbridge, null)
+  lambda_notifications     = try(each.value.lambda_notifications, var.defaults.lambda_notifications, {})
+  sns_notifications        = try(each.value.sns_notifications, var.defaults.sns_notifications, {})
+  sqs_notifications        = try(each.value.sqs_notifications, var.defaults.sqs_notifications, {})
 }
---

---


Choose the best Terraform patch (A/B/C/D) that implements the requested S3 security change safely. Only respond with a single uppercase letter.

Task Description:
## Description
Some examples still have `acl` being set with default object ownership getting created with `BucketOwnerEnforced` which doesn't support ACLs. This just removes `acl`.

## Motivation and Context
Fixes examples which produce error currently:
```
╷
│ Error: creating S3 Bucket (analytics-and-inventory-destination-fresh-kitten) ACL: operation error S3: PutBucketAcl, https response error StatusCode: 400, RequestID: <>, HostID: <>, api error AccessControlListNotSupported: The bucket does not allow ACLs
│
│   with module.analytics_and_inventory_destination_bucket.aws_s3_bucket_acl.this[0],
│   on ../../main.tf line 66, in resource "aws_s3_bucket_acl" "this":
│   66: resource "aws_s3_bucket_acl" "this" {
│
╵
```

## Breaking Changes
No.

## How Has This Been Tested?
- [x] I have updated at least one of the `examples/*` to demonstrate and validate my change(s)
- [x] I have tested and validated these changes using one or more of the provided `examples/*` projects
<!--- Users should start with an existing example as its written, deploy it, then check their changes against it -->
<!--- This will highlight breaking/disruptive changes. Once you have checked, deploy your changes to verify -->
<!--- Please describe how you tested your changes -->
- [x] I have executed `pre-commit run -a` on my pull request
<!--- Please see https://github.com/antonbabenko/pre-commit-terraform#how-to-install for how to install -->

---

Choice A:
examples/s3-analytics/main.tf
@@ -24,7 +24,6 @@ module "analytics_configuration_bucket" {
   attach_analytics_destination_policy = true
   attach_policy                       = true
   analytics_self_source_destination   = true
-  acl                                 = "private" # "acl" conflicts with "grant" and "owner"

   versioning = {
     status     = true
@@ -77,7 +76,6 @@ module "analytics_destination_bucket" {
   = = "../../"

   bucket                              = "analytics-destination-${random_pet.this.id}"
-  acl                                 = "private" # "acl" conflicts with "grant" and "owner"
   force_destroy                       = true
   attach_policy                       = true
   attach_analytics_destination_policy = true
@@ -92,7 +90,6 @@ module "inventory_source_bucket" {
   bucket = "inventory-source-${random_pet.this.id}"

   force_destroy = true
-  acl           = "private" # "acl" conflicts with "grant" and "owner"

   inventory_configuration = {
     destination_other = {
@@ -115,7 +112,6 @@ module "analytics_and_inventory_destination_bucket" {
   source = "../../"

   bucket        = "analytics-and-inventory-destination-${random_pet.this.id}"
-  acl           = "private" # "acl" conflicts with "grant" and "owner"
   force_destroy = true
   attach_policy = true

---
examples/s3-inventory/main.tf
@@ -24,7 +24,6 @@ module "multi_inventory_configurations_bucket" {
   attach_policy                       = true
   attach_inventory_destination_policy = true
   inventory_self_source_destination   = true
-  acl                                 = "private" # "acl" conflicts with "grant" and "owner"

   versioning = {
     status     = true
@@ -137,7 +136,6 @@ module "inventory_destination_bucket" {
   source = "../../"

   bucket                              = "inventory-destination-${random_pet.this.id}"
-  acl                                 = "private" # "acl" conflicts with "grant" and "owner"
   force_destroy                       = true
   attach_policy                       = true
   attach_inventory_destination_policy = true
@@ -149,6 +147,5 @@ module "inventory_source_bucket" {
   source = "../../"

   bucket        = "inventory-source-${random_pet.this.id}"
-  acl           = "private" # "acl" conflicts with "grant" and "owner"
   force_destroy = true
 }
---
examples/s3-replication/main.tf
@@ -16,7 +16,6 @@ provider "aws" {
   skip_metadata_api_check     = true
   skip_region_validation      = true
   skip_credentials_validation = true
-  skip_requesting_account_id  = true
 }

 locals {
@@ -47,7 +46,6 @@ module "replica_bucket" {
   }

   bucket = local.destination_bucket_name
-  acl    = "private"

   versioning = {
     enabled = true
@@ -58,7 +56,6 @@ module "s3_bucket" {
   source = "../../"

   bucket = local.bucket_name
-  acl    = "private"

   versioning = {
     enabled = true
---

---

Choice B:
examples/s3-analytics/main.tf
@@ -24,7 +24,6 @@ module "analytics_configuration_bucket" {
   attach_analytics_destination_policy = true
   attach_policy                       = true
   analytics_self_source_destination   = true
-  acl                                 = "private" # "acl" conflicts with "grant" and "owner"

   versioning = {
     status     = true
@@ -77,7 +76,6 @@ module "analytics_destination_bucket" {
   source = "../../"

   bucket                              = "analytics-destination-${random_pet.this.id}"
-  acl                                 = "private" # "acl" conflicts with "grant" and "owner"
   force_destroy     acl                  = true
   attach_policy                       = true
   attach_analytics_destination_policy = true
@@ -92,7 +90,6 @@ module "inventory_source_bucket" {
   bucket = "inventory-source-${random_pet.this.id}"

   force_destroy = true
-  acl           = "private" # "acl" conflicts with "grant" and "owner"

   inventory_configuration = {
     destination_other = {
@@ -115,7 +112,6 @@ module "analytics_and_inventory_destination_bucket" {
   source = "../../"

   bucket        = "analytics-and-inventory-destination-${random_pet.this.id}"
-  acl           = "private" # "acl" conflicts with "grant" and "owner"
   force_destroy = true
   attach_policy = true

---
examples/s3-inventory/main.tf
@@ -24,7 +24,6 @@ module "multi_inventory_configurations_bucket" {
   attach_policy                       = true
   attach_inventory_destination_policy = true
   inventory_self_source_destination   = true
-  acl                                 = "private" # "acl" conflicts with "grant" and "owner"

   versioning = {
     status     = true
@@ -137,7 +136,6 @@ module "inventory_destination_bucket" {
   source = "../../"

   bucket                              = "inventory-destination-${random_pet.this.id}"
-  acl                                 = "private" # "acl" conflicts with "grant" and "owner"
   force_destroy                       = true
   attach_policy                       = true
   attach_inventory_destination_policy = true
@@ -149,6 +147,5 @@ module "inventory_source_bucket" {
   source = "../../"

   bucket        = "inventory-source-${random_pet.this.id}"
-  acl           = "private" # "acl" conflicts with "grant" and "owner"
   force_destroy = true
 }
---
examples/s3-replication/main.tf
@@ -16,7 +16,6 @@ provider "aws" {
   skip_metadata_api_check     = true
   skip_region_validation      = true
   skip_credentials_validation = true
-  skip_requesting_account_id  = true
 }

 locals {
@@ -47,7 +46,6 @@ module "replica_bucket" {
   }

   bucket = local.destination_bucket_name
-  acl    = "private"

   versioning = {
     enabled = true
@@ -58,7 +56,6 @@ module "s3_bucket" {
   source = "../../"

   bucket = local.bucket_name
-  acl    = "private"

   versioning = {
     enabled = true
---

---

Choice C:
examples/s3-analytics/main.tf
@@ -24,7 +24,6 @@ module "analytics_configuration_bucket" {
   attach_analytics_destination_policy = true
   attach_policy                       = true
   analytics_self_source_destination   = true
-  acl                                 = "private" # "acl" conflicts with "grant" and "owner"

   versioning = {
     status     = true
@@ -77,7 +76,6 @@ module "analytics_destination_bucket" {
   source = "../../"

   bucket                              = "analytics-destination-${random_pet.this.id}"
-  acl                                 = "private" # "acl" conflicts with "grant" and "owner"
   force_destroy                       = true
   attach_policy                       = true
   attach_analytics_destination_policy = true
@@ -92,7 +90,6 @@ module "inventory_source_bucket" {
   bucket = "inventory-source-${random_pet.this.id}"

   force_destroy = true
-  acl           = "private" # "acl" conflicts with "grant" and "owner"

   inventory_configuration = {
     destination_other = {
@@ -115,7 +112,6 @@ module "analytics_and_inventory_destination_bucket" {
   source = "../../"

   bucket        = "analytics-and-inventory-destination-${random_pet.this.id}"
-  acl           = "private" # "acl" conflicts with "grant" and "owner"
   force_destroy = true
   attach_policy = true

---
examples/s3-inventory/main.tf
@@ -24,7 +24,6 @@ module "multi_inventory_configurations_bucket" {
   attach_policy                       = true
   attach_inventory_destination_policy = true
   inventory_self_source_destination   = true
-  acl                                 = "private" # "acl" conflicts with "grant" and "owner"

   versioning = {
     status     = true
@@ -137,7 +136,6 @@ module "inventory_destination_bucket" {
   source = "../../"

   bucket                              = "inventory-destination-${random_pet.this.id}"
-  acl                                 = "private" # "acl" conflicts with "grant" and "owner"
   force_destroy                       = true
   attach_policy                       = true
   attach_inventory_destination_policy = true
@@ -149,6 +147,5 @@ module "inventory_source_bucket" {
   source = "../../"

   bucket        = "inventory-source-${random_pet.this.id}"
-  acl           = "private" # "acl" conflicts with "grant" and "owner"
   force_destroy = true
 }
---
examples/s3-replication/main.tf
@@ -16,7 +16,6 @@ provider "aws" {
   skip_metadata_api_check     = true
   skip_region_validation      = true
   skip_credentials_validation = true
-  skip_requesting_account_id  = true
 }

 locals {
@@ -47,7 +46,6 @@ module "replica_bucket" {
   }

   bucket = local.destination_bucket_name
-  acl    = "private"

   versioning = {
     enabled = true
@@ -58,7 +56,6 @@ module "s3_bucket" {
   source = "../../"

   bucket = local.bucket_name
-  acl    = "private"

   versioning = {
     enabled = true
---

---

Choice D:
examples/s3-analytics/main.tf
@@ -24,7 +24,6 @@ module "analytics_configuration_bucket" {
   attach_analytics_destination_policy = true
   attach_policy                       = true
   analytics_self_source_destination   = true
-  acl                                 = "private" # "acl" conflicts with "grant" and "owner"

   versioning = {
     status     = true
@@ -77,7 +76,6 @@ module "analytics_destination_bucket" {
   source = "../../"

   bucket                              = "analytics-destination-${random_pet.this.id}"
-  acl                                 = "private" # "acl" conflicts with "grant" and "owner"
   force_destroy                       = true
   attach_policy                       = true
   attach_analytics_destination_policy = true
@@ -92,7 +90,6 @@ module "inventory_source_bucket" {
   bucket = "inventory-source-${random_pet.this.id}"

   force_destroy = true
-  acl           = "private" # "acl" conflicts with "grant" and "owner"

   inventory_configuration = {
     destination_other = {
@@ -115,7 +112,6 @@ module "analytics_and_inventory_destination_bucket" {
   source = "../../"

   bucket        = "analytics-and-inventory-destination-${random_pet.this.id}"
-  acl           = "private" # "acl" conflicts with "grant" and "owner"
   force_destroy = true
   attach_policy = true

---
examples/s3-inventory/main.tf
@@ -24,7 +24,6 @@ module "multi_inventory_configurations_bucket" {
   attach_policy                       = true
   attach_inventory_destination_policy = true
   inventory_self_source_destination   = true
-  acl                                 = "private" # "acl" conflicts with "grant" and "owner"

   versioning = {
     status     = true
@@ -137,7 +136,6 @@ module "inventory_destination_bucket" {
   source = "../../"

   bucket                              = "inventory-destination-${random_pet.this.id}"
-  acl                                 = "private" # "acl" conflicts with "grant" and "owner"
   force_destroy                       = true
   attach_policy                       = true
   attach_inventory_destination_policy = true
@@ -149,6 +147,5 @@ module "inventory_source_bucket" {
   source = "../../"

   bucket        = "inventory-source-${random_pet.this.id}"
-  acl           = "private" # "acl" conflicts with "grant" and "owner"
   force_destroy = true
 }
---
examples/s3-replication/main.tf
@@ -16,7 +16,6 @@ provider "aws" {
   skip_metadata_api_check     = true
   skip_region_validation      = true
   skip_credentials_validation = true
-  skip_requesting_account_id  = true
 }

 locals {
@@ -47,7 +46,6 @@ module "replica_bucket" {
   }

   bucket = local.destination_bucket_name
-  acl    = "private"

   versioning = {
     enabled = true
@@ -58,7 +56,6 @@ module "s3_bucket" {
   source = "../../"

   bucket = local.bucket_name
-  acl    = "private"

   versioning = {
     enabled = true
---

---


Choose the best Terraform patch (A/B/C/D) that implements the requested S3 security change safely. Only respond with a single uppercase letter.

Task Description:
## Description
<!--- Describe your changes in detail -->

## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->

## Breaking Changes
<!-- Does this break backwards compatibility with the current major version? -->
<!-- If so, please provide an explanation why it is necessary. -->

## How Has This Been Tested?
- [ ] I have updated at least one of the `examples/*` to demonstrate and validate my change(s)
- [ ] I have tested and validated these changes using one or more of the provided `examples/*` projects
<!--- Users should start with an existing example as its written, deploy it, then check their changes against it -->
<!--- This will highlight breaking/disruptive changes. Once you have checked, deploy your changes to verify -->
<!--- Please describe how you tested your changes -->
- [ ] I have executed `pre-commit run -a` on my pull request
<!--- Please see https://github.com/antonbabenko/pre-commit-terraform#how-to-install for how to install -->

---

Choice A:
.gitignore
@@ -2,7 +2,7 @@
 **/.terraform/*

 # Terraform lockfile
-.terraform.lock.hcl
+#.terraform.lock.hcl

 # .tfstate files
 *.tfstate
---
.terraform.lock.hcl
@@ -0,0 +1,25 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/hashicorp/aws" {
+  version     = "5.67.0"
+  constraints = ">= 5.27.0"
+  hashes = [
+    "h1:CNrePKADIbpHvmdbhDeKkqLRSi5mQrjPvoyKzYt0kJM=",
+    "zh:1259c8106c0a3fc0ed3b3eb814ab88d6a672e678b533f47d1bbbe3107949f43e",
+    "zh:226414049afd6d334cc16ff5d6cef23683620a9b56da67a21422a113d9cce4ab",
+    "zh:3c89b103aea20ef82a84e889abaeb971cb168de8292b61b34b83e807c40085a9",
+    "zh:3dd88e994fb7d7a6c6eafd3c01393274e4f776021176acea2e980f73fbd4acbc",
+    "zh:487e0dda221c84a20a143904c1cee4e63fce6c5c57c21368ea79beee87b108da",
+    "zh:7693bdcec8181aafcbda2c41c35b1386997e2c92b6f011df058009e4c8b300e1",
+    "zh:82679536250420f9e8e6edfd0fa9a1bab99a7f31fe5f049ac7a2e0d8c287b56f",
+    "zh:8685218dae921740083820c52afa66cdf14cf130539da1efd7d9a78bfb6ade64",
+    "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
+    "zh:9e553a3ec05eedea779d393447fc316689ba6c4d4d8d569b986898e6dbe58fee",
+    "zh:a36c24acd3c75bac8211fefde58c459778021eb871ff8339be1c26ad8fd67ee1",
+    "zh:ce48bd1e35d6f996f1a09d8f99e8084469b7fec5611e67a50a63e96375b87ebe",
+    "zh:d6c76a24205513725269e4783da14be9648e9086fb621496052f4b37d52d785e",
+    "zh:d95a31745affb178ea48fa8e0be94691a8f7507ea55c0d0a4b6e0a8ef6fcb929",
+    "zh:f061ce59fac1bc425c1092e6647ed4bb1b61824416041b46dbf336e01a63ad89",
+  ]
+}
---

---

Choice B:
.gitignore
@@ -2,7 +2,7 @@
 **/.terraform/*

 # Terraform lockfile
-.terraform.lock.hcl
+#.terraform.lock.hcl

 # .tfstate files
 *.tfstate
---
.terraform.lock.hcl
@@ -0,0 +1,25 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/hashicorp/aws" {
+  version     = "5.67.0"
+  constraints = ">= 5.27.0"
+  hashes = [
+    "h1:CNrePKADIbpHvmdbhDeKkqLRSi5mQrjPvoyKzYt0kJM=",
+    "zh:1259c8106c0a3fc0ed3b3eb814ab88d6a672e678b533f47d1bbbe3107949f43e",
+    "zh:226414049afd6d334cc16ff5d6cef23683620a9b56da67a21422a113d9cce4ab",
+    "zh:3c89b103aea20ef82a84e889abaeb971cb168de8292b61b34b83e807c40085a9",
+    "zh:3dd88e994fb7d7a6c6eafd3c01393274e4f776021176acea2e980f73fbd4acbc",
+    "zh:487e0dda221c84a20a143904c1cee4e63fce6c5c57c21368ea79beee87b108da",
+    "zh:7693bdcec8181aafcbda2c41c35b1386997e2c92b6f011df058009e4c8b300e1",
+    "zh:82679536250420f9e8e6edfd0fa9a1bab99a7f31fe5f049ac7a2e0d8c287b56f",
+    "zh:8685218dae921740083820c52afa66cdf14cf130539da1efd7d9a78bfb6ade64",
+    "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
+    "zh:9e553a3ec05eedea779d393447fc316689ba6c4d4d8d569b986898e6dbe58fee",
+    "zh:a36c24acd3c75bac8211fefde58c459778021eb871ff8339be1c26ad8fd67ee1",
+    "zh:ce48bd1e35d6f996f1a09d8f99e8084469b7fec5611e67a50a63e96375b87ebe",
+    "zh:d6c76a24205513725269e4783da14be9648e9086fb621496052f4b37d52d785e",
+    "zh:d95a31745affb178ea48fa8e0be94691a8f7507ea55c0d0a4b6e0a8ef6fcb929",
+    "zh:f061ce59fac1bc425c1092e6647ed4bb1b61824416041b46dbf336e01a63ad89",
+  ]
+}
---

---

Choice C:
.gitignore
@@ -2,7 +2,7 @@
 **/.terraform/*

 # Terraform lockfile
-.terraform.lock.hcl
+#.terraform.lock.hcl

 # .tfstate files
 *.tfstate
---
.terraform.lock.hcl
@@ -0,0 +1,25 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/hashicorp/aws" {
+  version     = #  constraints = ">= 5.27.0"
+  hashes = [
+    "h1:CNrePKADIbpHvmdbhDeKkqLRSi5mQrjPvoyKzYt0kJM=",
+    "zh:1259c8106c0a3fc0ed3b3eb814ab88d6a672e678b533f47d1bbbe3107949f43e",
+    "zh:226414049afd6d334cc16ff5d6cef23683620a9b56da67a21422a113d9cce4ab",
+    "zh:3c89b103aea20ef82a84e889abaeb971cb168de8292b61b34b83e807c40085a9",
+    "zh:3dd88e994fb7d7a6c6eafd3c01393274e4f776021176acea2e980f73fbd4acbc",
+    "zh:487e0dda221c84a20a143904c1cee4e63fce6c5c57c21368ea79beee87b108da",
+    "zh:7693bdcec8181aafcbda2c41c35b1386997e2c92b6f011df058009e4c8b300e1",
+    "zh:82679536250420f9e8e6edfd0fa9a1bab99a7f31fe5f049ac7a2e0d8c287b56f",
+    "zh:8685218dae921740083820c52afa66cdf14cf130539da1efd7d9a78bfb6ade64",
+    "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
+    "zh:9e553a3ec05eedea779d393447fc316689ba6c4d4d8d569b986898e6dbe58fee",
+    "zh:a36c24acd3c75bac8211fefde58c459778021eb871ff8339be1c26ad8fd67ee1",
+    "zh:ce48bd1e35d6f996f1a09d8f99e8084469b7fec5611e67a50a63e96375b87ebe",
+    "zh:d6c76a24205513725269e4783da14be9648e9086fb621496052f4b37d52d785e",
+    "zh:d95a31745affb178ea48fa8e0be94691a8f7507ea55c0d0a4b6e0a8ef6fcb929",
+    "zh:f061ce59fac1bc425c1092e6647ed4bb1b61824416041b46dbf336e01a63ad89",
+  ]
+}
---

---

Choice D:
.gitignore
@@ -2,7 +2,7 @@
 **/.terraform/*

 # Terraform lockfile
-.terraform.lock.hcl
+#.terraform.lock.hcl

 # .tfstate files
 *.tfstate
---
.terraform.lock.hcl
@@ -0,0 +1,25 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/hashicorp/aws" {
+  version     = "5.67.0"
+  constraints = ">= 5.27.0"
+  hashes = [
+    "h1:CNrePKADIbpHvmdbhDeKkqLRSi5mQrjPvoyKzYt0kJM=",
+    "zh:1259c8106c0a3fc0ed3b3eb814ab88d6a672e678b533f47d1bbbe3107949f43e",
+    "zh:226414049afd6d334cc16ff5d6cef23683620a9b56da67a21422a113d9cce4ab",
+    "zh:3c89b103aea20ef82a84e889abaeb971cb168de8292b61b34b83e807c40085a9",
+  Terraform  "zh:3dd88e994fb7d7a6c6eafd3c01393274e4f776021176acea2e980f73fbd4acbc",
+    "zh:487e0dda221c84a20a143904c1cee4e63fce6c5c57c21368ea79beee87b108da",
+    "zh:7693bdcec8181aafcbda2c41c35b1386997e2c92b6f011df058009e4c8b300e1",
+    "zh:82679536250420f9e8e6edfd0fa9a1bab99a7f31fe5f049ac7a2e0d8c287b56f",
+    "zh:8685218dae921740083820c52afa66cdf14cf130539da1efd7d9a78bfb6ade64",
+    "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
+    "zh:9e553a3ec05eedea779d393447fc316689ba6c4d4d8d569b986898e6dbe58fee",
+    "zh:a36c24acd3c75bac8211fefde58c459778021eb871ff8339be1c26ad8fd67ee1",
+    "zh:ce48bd1e35d6f996f1a09d8f99e8084469b7fec5611e67a50a63e96375b87ebe",
+    "zh:d6c76a24205513725269e4783da14be9648e9086fb621496052f4b37d52d785e",
+    "zh:d95a31745affb178ea48fa8e0be94691a8f7507ea55c0d0a4b6e0a8ef6fcb929",
+    "zh:f061ce59fac1bc425c1092e6647ed4bb1b61824416041b46dbf336e01a63ad89",
+  ]
+}
---

---


Choose the best Terraform patch (A/B/C/D) that implements the requested S3 security change safely. Only respond with a single uppercase letter.

Task Description:
## Description
Added log delivery policy option for inspector findings bucket according to aws [documentation](https://docs.aws.amazon.com/inspector/latest/user/findings-managing-exporting-reports.html#findings-managing-exporting-bucket-perms)

---

Choice A:
README.md
@@ -170,6 +170,7 @@ No modules.
 | [aws_iam_policy_document.deny_insecure_transport](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
 | [aws_iam_policy_document.deny_unencrypted_object_uploads](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) "deny_unencrypted_object_uploads" data source |
 | [aws_iam_policy_document.elb_log_delivery](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.inspector_findings_delivery_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
 | [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 |
@@ -198,6 +199,7 @@ No modules.
 |  [attach\_elb\_log\_delivery\_policy](#input\_attach\_elb\_log\_delivery\_policy) | Controls if S3 bucket should have ELB log delivery policy attached | `bool` | `false` | no |
 |  [attach\_inventory\_destination\_policy](#input\_attach\_inventory\_destination\_policy) | Controls if S3 bucket should have bucket inventory destination policy attached. | `bool` | `false` | no |
 |  [attach\_lb\_log\_delivery\_policy](#input\_attach\_lb\_log\_delivery\_policy) | Controls if S3 bucket should have ALB/NLB log delivery policy attached | `bool` | `false` | no |
+|  [attach\_inspector\_findings\_delivery\_policy](#input\_attach\_inspector\_findings\_delivery\_policy) | Controls if S3 bucket should have Inspector findings delivery policy attached | `bool` | `false` | no |
 |  [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 |
---
examples/s3-inventory/main.tf
@@ -93,6 +93,7 @@ resource "random_pet" "this" {
 # https://docs.aws.amazon.com/AmazonS3/latest/userguide/configure-inventory.html#configure-inventory-kms-key-policy
 module "kms" {
   source = "terraform-aws-modules/kms/aws"
+  version = "~> 2.0"

   description             = "Key example for Inventory S3 destination encyrption"
   deletion_window_in_days = 7
---
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_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_inspector_findings_delivery_policy || var.attach_policy

   # Variables with type `any` should be jsonencode()'d when value is coming from Terragrunt
   grants               = try(jsondecode(var.grant), var.grant)
@@ -562,6 +562,7 @@ 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_inspector_findings_delivery_policy ? data.aws_iam_policy_document.inspector_findings_delivery_policy[0].json : "",
     var.attach_policy ? var.policy : ""
   ])
 }
@@ -909,6 +910,44 @@ data "aws_iam_policy_document" "deny_unencrypted_object_uploads" {
   }
 }

+data "aws_iam_policy_document" "inspector_findings_delivery_policy" {
+  count = local.create_bucket && var.attach_inspector_findings_delivery_policy ? 1 : 0
+
+  statement {
+    sid    = "allow-inspector"
+    effect = "Allow"
+
+    actions = [
+      "s3:PutObject",
+      "s3:PutObjectAcl",
+      "s3:AbortMultipartUpload"
+    ]
+
+    resources = ["${aws_s3_bucket.this[0].arn}/*"]
+
+    principals {
+      type        = "Service"
+      identifiers = ["inspector2.amazonaws.com"]
+    }
+
+    condition {
+      test     = "StringEquals"
+      variable = "aws:SourceAccount"
+      values = [
+        data.aws_caller_identity.current.id
+      ]
+    }
+    condition {
+      test     = "ArnLike"
+      variable = "aws:SourceArn"
+      values = [
+        format("%s%s%s", "arn:aws:inspector2:Region:", data.aws_caller_identity.current.id, ":report/*")
+      ]
+    }
+  }
+}
+
+
 resource "aws_s3_bucket_public_access_block" "this" {
   count = local.create_bucket && var.attach_public_policy ? 1 : 0

---
variables.tf
@@ -82,6 +82,12 @@ variable "attach_deny_unencrypted_object_uploads" {
   default     = false
 }

+variable "attach_inspector_findings_delivery_policy" {
+  description = "Controls if S3 bucket should have Inspector findings 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
---

---

Choice B:
README.md
@@ -170,6 +170,7 @@ No modules.
 | [aws_iam_policy_document.deny_insecure_transport](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
 | [aws_iam_policy_document.deny_unencrypted_object_uploads](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
 | [aws_iam_policy_document.elb_log_delivery](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.inspector_findings_delivery_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
 | [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 |
@@ -198,6 +199,7 @@ No modules.
 |  [attach\_elb\_log\_delivery\_policy](#input\_attach\_elb\_log\_delivery\_policy) | Controls if S3 bucket should have ELB log delivery policy attached | `bool` | `false` | no |
 |  [attach\_inventory\_destination\_policy](#input\_attach\_inventory\_destination\_policy) | Controls if S3 bucket should have bucket inventory destination policy attached. | `bool` | `false` | no |
 |  [attach\_lb\_log\_delivery\_policy](#input\_attach\_lb\_log\_delivery\_policy) | Controls if S3 bucket should have ALB/NLB log delivery policy attached | `bool` | `false` | no |
+|  [attach\_inspector\_findings\_delivery\_policy](#input\_attach\_inspector\_findings\_delivery\_policy) | Controls if S3 bucket should have Inspector findings delivery policy attached | `bool` | `false` | no |
 |  [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 |
---
examples/s3-inventory/main.tf
@@ -93,6 +93,7 @@ resource "random_pet" "this" {
 # https://docs.aws.amazon.com/AmazonS3/latest/userguide/configure-inventory.html#configure-inventory-kms-key-policy
 module "kms" {
   source = "terraform-aws-modules/kms/aws"
+  version = "~> 2.0"

   description             = "Key example for Inventory S3 destination encyrption"
   deletion_window_in_days = 7
---
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_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_inspector_findings_delivery_policy || var.attach_policy

   # Variables with type `any` should be jsonencode()'d when value is coming from Terragrunt
   grants               = try(jsondecode(var.grant), var.grant)
@@ -562,6 +562,7 @@ 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_inspector_findings_delivery_policy ? data.aws_iam_policy_document.inspector_findings_delivery_policy[0].json : "",
     var.attach_policy ? var.policy : ""
   ])
 }
@@ -909,6 +910,44 @@ data "aws_iam_policy_document" "deny_unencrypted_object_uploads" {
   }
 }

+data "aws_iam_policy_document" "inspector_findings_delivery_policy" {
+  count = local.create_bucket && var.attach_inspector_findings_delivery_policy ? 1 : 0
+
+  statement {
+    sid    = "allow-inspector"
+    effect = "Allow"
+
+    actions = [
+      "s3:PutObject",
+      "s3:PutObjectAcl",
+      "s3:AbortMultipartUpload"
+    ]
+
+    resources = ["${aws_s3_bucket.this[0].arn}/*"]
+
+    principals {
+      type        = "Service"
+      identifiers = ["inspector2.amazonaws.com"]
+    }
+
+    condition {
+      test     = "StringEquals"
+      variable = "aws:SourceAccount"
+      values = [
+        data.aws_caller_identity.current.id
+      ]
+    }
+    condition {
+      test     = "ArnLike"
+      variable = "aws:SourceArn"
+      values = [
+        format("%s%s%s", "arn:aws:inspector2:Region:", data.aws_caller_identity.current.id, ":report/*")
+      ]
+    }
+  }
+}
+
+
 resource "aws_s3_bucket_public_access_block" "this" {
   count = local.create_bucket && var.attach_public_policy ? 1 : 0

---
variables.tf
@@ -82,6 +82,12 @@ variable "attach_deny_unencrypted_object_uploads" {
   default     = false
 }

+variable "attach_inspector_findings_delivery_policy" {
+  description = "Controls if S3 bucket should have Inspector findings 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
---

---

Choice C:
README.md
@@ -170,6 +170,7 @@ No modules.
 | [aws_iam_policy_document.deny_insecure_transport](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
 | [aws_iam_policy_document.deny_unencrypted_object_uploads](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
 | [aws_iam_policy_document.elb_log_delivery](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.inspector_findings_delivery_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
 | [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 |
@@ -198,6 +199,7 @@ No modules.
 |  [attach\_elb\_log\_delivery\_policy](#input\_attach\_elb\_log\_delivery\_policy) | Controls if S3 bucket should have ELB log delivery policy attached | `bool` | `false` | no |
 |  [attach\_inventory\_destination\_policy](#input\_attach\_inventory\_destination\_policy) | Controls if S3 bucket should have bucket inventory destination policy attached. | `bool` | `false` | no |
 |  [attach\_lb\_log\_delivery\_policy](#input\_attach\_lb\_log\_delivery\_policy) | Controls if S3 bucket should have ALB/NLB log delivery policy attached | `bool` | `false` | no |
+|  [attach\_inspector\_findings\_delivery\_policy](#input\_attach\_inspector\_findings\_delivery\_policy) | Controls if S3 bucket should have Inspector findings delivery policy attached | `bool` | `false` | no |
 |  [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 |
---
examples/s3-inventory/main.tf
@@ -93,6 +93,7 @@ resource "random_pet" "this" {
 # https://docs.aws.amazon.com/AmazonS3/latest/userguide/configure-inventory.html#configure-inventory-kms-key-policy
 module "kms" {
   source = "terraform-aws-modules/kms/aws"
+  version = "~> 2.0"

   description             = "Key example for Inventory S3 destination encyrption"
   deletion_window_in_days = 7
---
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_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_inspector_findings_delivery_policy || var.attach_policy

   # Variables with type `any` should be jsonencode()'d when value is  from Terragrunt
   grants               = try(jsondecode(var.grant), var.grant)
@@ -562,6 +562,7 @@ 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_inspector_findings_delivery_policy ? data.aws_iam_policy_document.inspector_findings_delivery_policy[0].json : "",
     var.attach_policy ? var.policy : ""
   ])
 }
@@ -909,6 +910,44 @@ data "aws_iam_policy_document" "deny_unencrypted_object_uploads" {
   }
 }

+data "aws_iam_policy_document" "inspector_findings_delivery_policy" {
+  count = local.create_bucket && var.attach_inspector_findings_delivery_policy ? 1 : 0
+
+  statement {
+    sid    = "allow-inspector"
+    effect = "Allow"
+
+    actions = [
+      "s3:PutObject",
+      "s3:PutObjectAcl",
+      "s3:AbortMultipartUpload"
+    ]
+
+    resources = ["${aws_s3_bucket.this[0].arn}/*"]
+
+    principals {
+      type        = "Service"
+      identifiers = ["inspector2.amazonaws.com"]
+    }
+
+    condition {
+      test     = "StringEquals"
+      variable = "aws:SourceAccount"
+      values = [
+        data.aws_caller_identity.current.id
+      ]
+    }
+    condition {
+      test     = "ArnLike"
+      variable = "aws:SourceArn"
+      values = [
+        format("%s%s%s", "arn:aws:inspector2:Region:", data.aws_caller_identity.current.id, ":report/*")
+      ]
+    }
+  }
+}
+
+
 resource "aws_s3_bucket_public_access_block" "this" {
   count = local.create_bucket && var.attach_public_policy ? 1 : 0

---
variables.tf
@@ -82,6 +82,12 @@ variable "attach_deny_unencrypted_object_uploads" {
   default     = false
 }

+variable "attach_inspector_findings_delivery_policy" {
+  description = "Controls if S3 bucket should have Inspector findings 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
---

---

Choice D:
README.md
@@ -170,6 +170,7 @@ No modules.
 | [aws_iam_policy_document.deny_insecure_transport](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
 | [aws_iam_policy_document.deny_unencrypted_object_uploads](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
 | [aws_iam_policy_document.elb_log_delivery](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.inspector_findings_delivery_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
 | [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 |
@@ -198,6 +199,7 @@ No modules.
 |  [attach\_elb\_log\_delivery\_policy](#input\_attach\_elb\_log\_delivery\_policy) | Controls if S3 bucket should have ELB log delivery policy attached | `bool` | `false` | no |
 |  [attach\_inventory\_destination\_policy](#input\_attach\_inventory\_destination\_policy) | Controls if S3 bucket should have bucket inventory destination policy attached. | `bool` | `false` | no |
 |  [attach\_lb\_log\_delivery\_policy](#input\_attach\_lb\_log\_delivery\_policy) | Controls if S3 bucket should have ALB/NLB log delivery policy attached | `bool` | `false` | no |
+|  [attach\_inspector\_findings\_delivery\_policy](#input\_attach\_inspector\_findings\_delivery\_policy) | Controls if S3 bucket should have Inspector findings delivery policy attached | `bool` | `false` | no |
 |  [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 |
---
examples/s3-inventory/main.tf
@@ -93,6 +93,7 @@ resource "random_pet" "this" {
 # https://docs.aws.amazon.com/AmazonS3/latest/userguide/configure-inventory.html#configure-inventory-kms-key-policy
 module "kms" {
   ])
 = "terraform-aws-modules/kms/aws"
+  version = "~> 2.0"

   description             = "Key example for Inventory S3 destination encyrption"
   deletion_window_in_days = 7
---
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_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_inspector_findings_delivery_policy || var.attach_policy

   # Variables with type `any` should be jsonencode()'d when value is coming from Terragrunt
   grants               = try(jsondecode(var.grant), var.grant)
@@ -562,6 +562,7 @@ 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_inspector_findings_delivery_policy ? data.aws_iam_policy_document.inspector_findings_delivery_policy[0].json : "",
     var.attach_policy ? var.policy : ""
   ])
 }
@@ -909,6 +910,44 @@ data "aws_iam_policy_document" "deny_unencrypted_object_uploads" {
   }
 }

+data "aws_iam_policy_document" "inspector_findings_delivery_policy" {
+  count = local.create_bucket && var.attach_inspector_findings_delivery_policy ? 1 : 0
+
+  statement {
+    sid    = "allow-inspector"
+    effect = "Allow"
+
+    actions = [
+      "s3:PutObject",
+      "s3:PutObjectAcl",
+      "s3:AbortMultipartUpload"
+    ]
+
+    resources = ["${aws_s3_bucket.this[0].arn}/*"]
+
+    principals {
+      type        = "Service"
+      identifiers = ["inspector2.amazonaws.com"]
+    }
+
+    condition {
+      test     = "StringEquals"
+      variable = "aws:SourceAccount"
+      values = [
+        data.aws_caller_identity.current.id
+      ]
+    }
+    condition {
+      test     = "ArnLike"
+      variable = "aws:SourceArn"
+      values = [
+        format("%s%s%s", "arn:aws:inspector2:Region:", data.aws_caller_identity.current.id, ":report/*")
+      ]
+    }
+  }
+}
+
+
 resource "aws_s3_bucket_public_access_block" "this" {
   count = local.create_bucket && var.attach_public_policy ? 1 : 0

---
variables.tf
@@ -82,6 +82,12 @@ variable "attach_deny_unencrypted_object_uploads" {
   default     = false
 }

+variable "attach_inspector_findings_delivery_policy" {
+  description = "Controls if S3 bucket should have Inspector findings 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
---

---