6
6
7
7
dynamodb_table_name = local. dynamodb_enabled ? coalesce (var. dynamodb_table_name , module. dynamodb_table_label . id ) : " "
8
8
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
10
10
11
11
policy = local. prevent_unencrypted_uploads ? join (
12
12
" " ,
@@ -23,36 +23,39 @@ locals {
23
23
24
24
terraform_backend_config_content = templatefile (local. terraform_backend_config_template_file , {
25
25
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
45
40
})
46
41
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 )
48
43
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
54
45
}
55
46
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
+
56
59
data "aws_iam_policy_document" "prevent_unencrypted_uploads" {
57
60
count = local. prevent_unencrypted_uploads ? 1 : 0
58
61
@@ -138,82 +141,88 @@ data "aws_iam_policy_document" "prevent_unencrypted_uploads" {
138
141
}
139
142
}
140
143
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
-
156
144
resource "aws_s3_bucket" "default" {
157
145
count = local. bucket_enabled ? 1 : 0
158
146
159
147
# 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).
160
148
# bridgecrew:skip=CKV_AWS_52:Skipping `Ensure S3 bucket has MFA delete enabled` check due to issues operating with `mfa_delete` in terraform
161
149
bucket = substr (local. bucket_name , 0 , 63 )
162
- acl = var. acl
163
150
force_destroy = var. force_destroy
164
- policy = local. policy
165
151
166
- versioning {
167
- enabled = true
168
- mfa_delete = var. mfa_delete
169
- }
152
+ tags = module. this . tags
153
+ }
170
154
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
178
157
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"
195
181
}
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 )
196
188
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"
202
192
}
203
193
}
194
+ }
204
195
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
206
203
}
207
204
208
205
resource "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 )
211
209
block_public_acls = var. block_public_acls
212
210
ignore_public_acls = var. ignore_public_acls
213
211
block_public_policy = var. block_public_policy
214
212
restrict_public_buckets = var. restrict_public_buckets
215
213
}
216
214
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
+
217
226
module "dynamodb_table_label" {
218
227
source = " cloudposse/label/null"
219
228
version = " 0.25.0"
@@ -223,7 +232,7 @@ module "dynamodb_table_label" {
223
232
}
224
233
225
234
resource "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
227
236
name = local. dynamodb_table_name
228
237
billing_mode = var. billing_mode
229
238
read_capacity = var. billing_mode == " PROVISIONED" ? var. read_capacity : null
@@ -248,30 +257,6 @@ resource "aws_dynamodb_table" "with_server_side_encryption" {
248
257
tags = module. dynamodb_table_label . tags
249
258
}
250
259
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
-
275
260
resource "local_file" "terraform_backend_config" {
276
261
count = local. enabled && var. terraform_backend_config_file_path != " " ? 1 : 0
277
262
content = local. terraform_backend_config_content
0 commit comments