66
77 dynamodb_table_name = local. dynamodb_enabled ? coalesce (var. dynamodb_table_name , module. dynamodb_table_label . id ) : " "
88
9- prevent_unencrypted_uploads = local. enabled && var. prevent_unencrypted_uploads && var . enable_server_side_encryption
9+ prevent_unencrypted_uploads = local. enabled && var. prevent_unencrypted_uploads
1010
1111 policy = local. prevent_unencrypted_uploads ? join (
1212 " " ,
@@ -23,36 +23,39 @@ locals {
2323
2424 terraform_backend_config_content = templatefile (local. terraform_backend_config_template_file , {
2525 region = data.aws_region.current.name
26- bucket = join (" " , aws_s3_bucket. default . * . id )
27-
28- dynamodb_table = local.dynamodb_enabled ? element (
29- coalescelist (
30- aws_dynamodb_table. with_server_side_encryption . * . name ,
31- aws_dynamodb_table. without_server_side_encryption . * . name
32- ),
33- 0
34- ) : " "
35-
36- encrypt = var.enable_server_side_encryption ? " true" : " false"
37- role_arn = var.role_arn
38- profile = var.profile
39- terraform_version = var.terraform_version
40- terraform_state_file = var.terraform_state_file
41- namespace = var.namespace
42- stage = var.stage
43- environment = var.environment
44- name = var.name
26+ # Template file inputs cannot be null, so we use empty string if the variable is null
27+ bucket = try (aws_s3_bucket. default [0 ]. id , " " )
28+
29+ dynamodb_table = try (aws_dynamodb_table. with_server_side_encryption [0 ]. name , " " )
30+
31+ encrypt = " true"
32+ role_arn = var.role_arn == null ? " " : var.role_arn
33+ profile = var.profile == null ? " " : var.profile
34+ terraform_version = var.terraform_version == null ? " " : var.terraform_version
35+ terraform_state_file = var.terraform_state_file == null ? " " : var.terraform_state_file
36+ namespace = var.namespace == null ? " " : var.namespace
37+ stage = var.stage == null ? " " : var.stage
38+ environment = var.environment == null ? " " : var.environment
39+ name = var.name == null ? " " : var.name
4540 })
4641
47- bucket_name = var. s3_bucket_name != " " ? var. s3_bucket_name : module . this . id
42+ labels_enabled = local . enabled && ( var. s3_bucket_name == " " || var. s3_bucket_name == null )
4843
49- logging_bucket_enabled = local. bucket_enabled && var. logging_bucket_enabled
50- logging_bucket_name_default = try (var. logging [" bucket_name" ], " ${ local . bucket_name } -logs" )
51- logging_prefix_default = try (var. logging [" prefix" ], " logs/" )
52- logging_bucket_name = local. logging_bucket_enabled ? module. log_storage . bucket_id : local. logging_bucket_name_default
53- logging_prefix = local. logging_bucket_enabled ? module. log_storage . prefix : local. logging_prefix_default
44+ bucket_name = local. labels_enabled ? module. bucket_label . id : var. s3_bucket_name
5445}
5546
47+ module "bucket_label" {
48+ source = " cloudposse/label/null"
49+ version = " 0.25.0"
50+
51+ enabled = local. labels_enabled
52+ id_length_limit = 63
53+
54+ context = module. this . context
55+ }
56+
57+ data "aws_region" "current" {}
58+
5659data "aws_iam_policy_document" "prevent_unencrypted_uploads" {
5760 count = local. prevent_unencrypted_uploads ? 1 : 0
5861
@@ -138,82 +141,88 @@ data "aws_iam_policy_document" "prevent_unencrypted_uploads" {
138141 }
139142}
140143
141- module "log_storage" {
142- source = " cloudposse/s3-log-storage/aws"
143- version = " 0.26.0"
144-
145- enabled = local. logging_bucket_enabled
146- access_log_bucket_prefix = local. logging_prefix_default
147- acl = " log-delivery-write"
148- expiration_days = var. logging_bucket_expiration_days
149- glacier_transition_days = var. logging_bucket_glacier_transition_days
150- name = local. logging_bucket_name_default
151- standard_transition_days = var. logging_bucket_standard_transition_days
152-
153- context = module. this . context
154- }
155-
156144resource "aws_s3_bucket" "default" {
157145 count = local. bucket_enabled ? 1 : 0
158146
159147 # bridgecrew:skip=BC_AWS_S3_13:Skipping `Enable S3 Bucket Logging` check until Bridgecrew will support dynamic blocks (https://github.com/bridgecrewio/checkov/issues/776).
160148 # bridgecrew:skip=CKV_AWS_52:Skipping `Ensure S3 bucket has MFA delete enabled` check due to issues operating with `mfa_delete` in terraform
161149 bucket = substr (local. bucket_name , 0 , 63 )
162- acl = var. acl
163150 force_destroy = var. force_destroy
164- policy = local. policy
165151
166- versioning {
167- enabled = true
168- mfa_delete = var. mfa_delete
169- }
152+ tags = module. this . tags
153+ }
170154
171- server_side_encryption_configuration {
172- rule {
173- apply_server_side_encryption_by_default {
174- sse_algorithm = " AES256"
175- }
176- }
177- }
155+ resource "aws_s3_bucket_policy" "default" {
156+ count = local. bucket_enabled ? 1 : 0
178157
179- dynamic "replication_configuration" {
180- for_each = var. s3_replication_enabled ? toset ([var . s3_replica_bucket_arn ]) : []
181- content {
182- role = aws_iam_role. replication [0 ]. arn
183-
184- rules {
185- id = module. this . id
186- prefix = " "
187- status = " Enabled"
188-
189- destination {
190- bucket = var. s3_replica_bucket_arn
191- storage_class = " STANDARD"
192- }
193- }
194- }
158+ bucket = one (aws_s3_bucket. default . * . id )
159+ policy = local. policy
160+ }
161+
162+ resource "aws_s3_bucket_acl" "default" {
163+ count = local. bucket_enabled && ! var. bucket_ownership_enforced_enabled ? 1 : 0
164+
165+ bucket = one (aws_s3_bucket. default . * . id )
166+ acl = var. acl
167+
168+ # Default "bucket ownership controls" for new S3 buckets is "BucketOwnerEnforced", which disables ACLs.
169+ # So, we need to wait until we change bucket ownership to "BucketOwnerPreferred" before we can set ACLs.
170+ depends_on = [aws_s3_bucket_ownership_controls . default ]
171+ }
172+
173+ resource "aws_s3_bucket_versioning" "default" {
174+ count = local. bucket_enabled ? 1 : 0
175+
176+ bucket = one (aws_s3_bucket. default . * . id )
177+
178+ versioning_configuration {
179+ status = " Enabled"
180+ mfa_delete = var. mfa_delete ? " Enabled" : " Disabled"
195181 }
182+ }
183+
184+ resource "aws_s3_bucket_server_side_encryption_configuration" "default" {
185+ count = local. bucket_enabled ? 1 : 0
186+
187+ bucket = one (aws_s3_bucket. default . * . id )
196188
197- dynamic "logging" {
198- for_each = var. logging == null ? [] : [1 ]
199- content {
200- target_bucket = local. logging_bucket_name
201- target_prefix = local. logging_prefix
189+ rule {
190+ apply_server_side_encryption_by_default {
191+ sse_algorithm = " AES256"
202192 }
203193 }
194+ }
204195
205- tags = module. this . tags
196+ resource "aws_s3_bucket_logging" "default" {
197+ count = local. bucket_enabled && length (var. logging ) > 0 ? 1 : 0
198+
199+ bucket = one (aws_s3_bucket. default . * . id )
200+
201+ target_bucket = var. logging [0 ]. target_bucket
202+ target_prefix = var. logging [0 ]. target_prefix
206203}
207204
208205resource "aws_s3_bucket_public_access_block" "default" {
209- count = local. bucket_enabled && var. enable_public_access_block ? 1 : 0
210- bucket = join (" " , aws_s3_bucket. default . * . id )
206+ count = local. bucket_enabled && var. enable_public_access_block ? 1 : 0
207+
208+ bucket = one (aws_s3_bucket. default . * . id )
211209 block_public_acls = var. block_public_acls
212210 ignore_public_acls = var. ignore_public_acls
213211 block_public_policy = var. block_public_policy
214212 restrict_public_buckets = var. restrict_public_buckets
215213}
216214
215+ # After you apply the bucket owner enforced setting for Object Ownership, ACLs are disabled for the bucket.
216+ # See https://docs.aws.amazon.com/AmazonS3/latest/userguide/about-object-ownership.html
217+ resource "aws_s3_bucket_ownership_controls" "default" {
218+ count = local. bucket_enabled ? 1 : 0
219+ bucket = one (aws_s3_bucket. default . * . id )
220+
221+ rule {
222+ object_ownership = var. bucket_ownership_enforced_enabled ? " BucketOwnerEnforced" : " BucketOwnerPreferred"
223+ }
224+ }
225+
217226module "dynamodb_table_label" {
218227 source = " cloudposse/label/null"
219228 version = " 0.25.0"
@@ -223,7 +232,7 @@ module "dynamodb_table_label" {
223232}
224233
225234resource "aws_dynamodb_table" "with_server_side_encryption" {
226- count = local. dynamodb_enabled && var . enable_server_side_encryption ? 1 : 0
235+ count = local. dynamodb_enabled ? 1 : 0
227236 name = local. dynamodb_table_name
228237 billing_mode = var. billing_mode
229238 read_capacity = var. billing_mode == " PROVISIONED" ? var. read_capacity : null
@@ -248,30 +257,6 @@ resource "aws_dynamodb_table" "with_server_side_encryption" {
248257 tags = module. dynamodb_table_label . tags
249258}
250259
251- resource "aws_dynamodb_table" "without_server_side_encryption" {
252- count = local. dynamodb_enabled && ! var. enable_server_side_encryption ? 1 : 0
253- name = local. dynamodb_table_name
254- billing_mode = var. billing_mode
255- read_capacity = var. billing_mode == " PROVISIONED" ? var. read_capacity : null
256- write_capacity = var. billing_mode == " PROVISIONED" ? var. write_capacity : null
257-
258- # https://www.terraform.io/docs/backends/types/s3.html#dynamodb_table
259- hash_key = " LockID"
260-
261- point_in_time_recovery {
262- enabled = var. enable_point_in_time_recovery
263- }
264-
265- attribute {
266- name = " LockID"
267- type = " S"
268- }
269-
270- tags = module. dynamodb_table_label . tags
271- }
272-
273- data "aws_region" "current" {}
274-
275260resource "local_file" "terraform_backend_config" {
276261 count = local. enabled && var. terraform_backend_config_file_path != " " ? 1 : 0
277262 content = local. terraform_backend_config_content
0 commit comments