Skip to content

Commit

Permalink
Support AWS provider version 4 (#129)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nuru authored Apr 24, 2023
1 parent 8c02473 commit 73e86c7
Show file tree
Hide file tree
Showing 14 changed files with 960 additions and 329 deletions.
51 changes: 28 additions & 23 deletions README.md

Large diffs are not rendered by default.

51 changes: 28 additions & 23 deletions docs/terraform.md

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions examples/complete/fixtures.us-east-2.tfvars
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
enabled = true

region = "us-east-2"

namespace = "eg"
Expand Down
15 changes: 15 additions & 0 deletions examples/complete/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ provider "aws" {
region = var.region
}

resource "aws_s3_bucket" "default" {
count = module.this.enabled ? 1 : 0

bucket = "${module.this.id}-logs"
}

module "tfstate_backend" {
source = "../../"

Expand All @@ -10,5 +16,14 @@ module "tfstate_backend" {
bucket_enabled = var.bucket_enabled
dynamodb_enabled = var.dynamodb_enabled

logging = [
{
target_bucket = one(aws_s3_bucket.default[*].id)
target_prefix = "tfstate/"
}
]

bucket_ownership_enforced_enabled = true

context = module.this.context
}
5 changes: 5 additions & 0 deletions examples/complete/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ output "s3_bucket_id" {
description = "S3 bucket ID"
}

output "s3_replication_role_arn" {
value = module.tfstate_backend.s3_replication_role_arn
description = "The ARN of the IAM Role created for replication, if enabled."
}

output "dynamodb_table_name" {
value = module.tfstate_backend.dynamodb_table_name
description = "DynamoDB table name"
Expand Down
2 changes: 1 addition & 1 deletion examples/complete/versions.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
terraform {
required_version = ">= 0.13.0"
required_version = ">= 1.1.0"

required_providers {
aws = {
Expand Down
191 changes: 88 additions & 103 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ locals {

dynamodb_table_name = local.dynamodb_enabled ? coalesce(var.dynamodb_table_name, module.dynamodb_table_label.id) : ""

prevent_unencrypted_uploads = local.enabled && var.prevent_unencrypted_uploads && var.enable_server_side_encryption
prevent_unencrypted_uploads = local.enabled && var.prevent_unencrypted_uploads

policy = local.prevent_unencrypted_uploads ? join(
"",
Expand All @@ -23,36 +23,39 @@ locals {

terraform_backend_config_content = templatefile(local.terraform_backend_config_template_file, {
region = data.aws_region.current.name
bucket = join("", aws_s3_bucket.default.*.id)

dynamodb_table = local.dynamodb_enabled ? element(
coalescelist(
aws_dynamodb_table.with_server_side_encryption.*.name,
aws_dynamodb_table.without_server_side_encryption.*.name
),
0
) : ""

encrypt = var.enable_server_side_encryption ? "true" : "false"
role_arn = var.role_arn
profile = var.profile
terraform_version = var.terraform_version
terraform_state_file = var.terraform_state_file
namespace = var.namespace
stage = var.stage
environment = var.environment
name = var.name
# Template file inputs cannot be null, so we use empty string if the variable is null
bucket = try(aws_s3_bucket.default[0].id, "")

dynamodb_table = try(aws_dynamodb_table.with_server_side_encryption[0].name, "")

encrypt = "true"
role_arn = var.role_arn == null ? "" : var.role_arn
profile = var.profile == null ? "" : var.profile
terraform_version = var.terraform_version == null ? "" : var.terraform_version
terraform_state_file = var.terraform_state_file == null ? "" : var.terraform_state_file
namespace = var.namespace == null ? "" : var.namespace
stage = var.stage == null ? "" : var.stage
environment = var.environment == null ? "" : var.environment
name = var.name == null ? "" : var.name
})

bucket_name = var.s3_bucket_name != "" ? var.s3_bucket_name : module.this.id
labels_enabled = local.enabled && (var.s3_bucket_name == "" || var.s3_bucket_name == null)

logging_bucket_enabled = local.bucket_enabled && var.logging_bucket_enabled
logging_bucket_name_default = try(var.logging["bucket_name"], "${local.bucket_name}-logs")
logging_prefix_default = try(var.logging["prefix"], "logs/")
logging_bucket_name = local.logging_bucket_enabled ? module.log_storage.bucket_id : local.logging_bucket_name_default
logging_prefix = local.logging_bucket_enabled ? module.log_storage.prefix : local.logging_prefix_default
bucket_name = local.labels_enabled ? module.bucket_label.id : var.s3_bucket_name
}

module "bucket_label" {
source = "cloudposse/label/null"
version = "0.25.0"

enabled = local.labels_enabled
id_length_limit = 63

context = module.this.context
}

data "aws_region" "current" {}

data "aws_iam_policy_document" "prevent_unencrypted_uploads" {
count = local.prevent_unencrypted_uploads ? 1 : 0

Expand Down Expand Up @@ -138,82 +141,88 @@ data "aws_iam_policy_document" "prevent_unencrypted_uploads" {
}
}

module "log_storage" {
source = "cloudposse/s3-log-storage/aws"
version = "0.26.0"

enabled = local.logging_bucket_enabled
access_log_bucket_prefix = local.logging_prefix_default
acl = "log-delivery-write"
expiration_days = var.logging_bucket_expiration_days
glacier_transition_days = var.logging_bucket_glacier_transition_days
name = local.logging_bucket_name_default
standard_transition_days = var.logging_bucket_standard_transition_days

context = module.this.context
}

resource "aws_s3_bucket" "default" {
count = local.bucket_enabled ? 1 : 0

#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).
#bridgecrew:skip=CKV_AWS_52:Skipping `Ensure S3 bucket has MFA delete enabled` check due to issues operating with `mfa_delete` in terraform
bucket = substr(local.bucket_name, 0, 63)
acl = var.acl
force_destroy = var.force_destroy
policy = local.policy

versioning {
enabled = true
mfa_delete = var.mfa_delete
}
tags = module.this.tags
}

server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
resource "aws_s3_bucket_policy" "default" {
count = local.bucket_enabled ? 1 : 0

dynamic "replication_configuration" {
for_each = var.s3_replication_enabled ? toset([var.s3_replica_bucket_arn]) : []
content {
role = aws_iam_role.replication[0].arn

rules {
id = module.this.id
prefix = ""
status = "Enabled"

destination {
bucket = var.s3_replica_bucket_arn
storage_class = "STANDARD"
}
}
}
bucket = one(aws_s3_bucket.default.*.id)
policy = local.policy
}

resource "aws_s3_bucket_acl" "default" {
count = local.bucket_enabled && !var.bucket_ownership_enforced_enabled ? 1 : 0

bucket = one(aws_s3_bucket.default.*.id)
acl = var.acl

# Default "bucket ownership controls" for new S3 buckets is "BucketOwnerEnforced", which disables ACLs.
# So, we need to wait until we change bucket ownership to "BucketOwnerPreferred" before we can set ACLs.
depends_on = [aws_s3_bucket_ownership_controls.default]
}

resource "aws_s3_bucket_versioning" "default" {
count = local.bucket_enabled ? 1 : 0

bucket = one(aws_s3_bucket.default.*.id)

versioning_configuration {
status = "Enabled"
mfa_delete = var.mfa_delete ? "Enabled" : "Disabled"
}
}

resource "aws_s3_bucket_server_side_encryption_configuration" "default" {
count = local.bucket_enabled ? 1 : 0

bucket = one(aws_s3_bucket.default.*.id)

dynamic "logging" {
for_each = var.logging == null ? [] : [1]
content {
target_bucket = local.logging_bucket_name
target_prefix = local.logging_prefix
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}

tags = module.this.tags
resource "aws_s3_bucket_logging" "default" {
count = local.bucket_enabled && length(var.logging) > 0 ? 1 : 0

bucket = one(aws_s3_bucket.default.*.id)

target_bucket = var.logging[0].target_bucket
target_prefix = var.logging[0].target_prefix
}

resource "aws_s3_bucket_public_access_block" "default" {
count = local.bucket_enabled && var.enable_public_access_block ? 1 : 0
bucket = join("", aws_s3_bucket.default.*.id)
count = local.bucket_enabled && var.enable_public_access_block ? 1 : 0

bucket = one(aws_s3_bucket.default.*.id)
block_public_acls = var.block_public_acls
ignore_public_acls = var.ignore_public_acls
block_public_policy = var.block_public_policy
restrict_public_buckets = var.restrict_public_buckets
}

# After you apply the bucket owner enforced setting for Object Ownership, ACLs are disabled for the bucket.
# See https://docs.aws.amazon.com/AmazonS3/latest/userguide/about-object-ownership.html
resource "aws_s3_bucket_ownership_controls" "default" {
count = local.bucket_enabled ? 1 : 0
bucket = one(aws_s3_bucket.default.*.id)

rule {
object_ownership = var.bucket_ownership_enforced_enabled ? "BucketOwnerEnforced" : "BucketOwnerPreferred"
}
}

module "dynamodb_table_label" {
source = "cloudposse/label/null"
version = "0.25.0"
Expand All @@ -223,7 +232,7 @@ module "dynamodb_table_label" {
}

resource "aws_dynamodb_table" "with_server_side_encryption" {
count = local.dynamodb_enabled && var.enable_server_side_encryption ? 1 : 0
count = local.dynamodb_enabled ? 1 : 0
name = local.dynamodb_table_name
billing_mode = var.billing_mode
read_capacity = var.billing_mode == "PROVISIONED" ? var.read_capacity : null
Expand All @@ -248,30 +257,6 @@ resource "aws_dynamodb_table" "with_server_side_encryption" {
tags = module.dynamodb_table_label.tags
}

resource "aws_dynamodb_table" "without_server_side_encryption" {
count = local.dynamodb_enabled && ! var.enable_server_side_encryption ? 1 : 0
name = local.dynamodb_table_name
billing_mode = var.billing_mode
read_capacity = var.billing_mode == "PROVISIONED" ? var.read_capacity : null
write_capacity = var.billing_mode == "PROVISIONED" ? var.write_capacity : null

# https://www.terraform.io/docs/backends/types/s3.html#dynamodb_table
hash_key = "LockID"

point_in_time_recovery {
enabled = var.enable_point_in_time_recovery
}

attribute {
name = "LockID"
type = "S"
}

tags = module.dynamodb_table_label.tags
}

data "aws_region" "current" {}

resource "local_file" "terraform_backend_config" {
count = local.enabled && var.terraform_backend_config_file_path != "" ? 1 : 0
content = local.terraform_backend_config_content
Expand Down
38 changes: 11 additions & 27 deletions outputs.tf
Original file line number Diff line number Diff line change
@@ -1,51 +1,35 @@
output "s3_bucket_domain_name" {
value = join("", aws_s3_bucket.default.*.bucket_domain_name)
value = one(aws_s3_bucket.default[*].bucket_domain_name)
description = "S3 bucket domain name"
}

output "s3_bucket_id" {
value = join("", aws_s3_bucket.default.*.id)
value = one(aws_s3_bucket.default[*].id)
description = "S3 bucket ID"
}

output "s3_bucket_arn" {
value = join("", aws_s3_bucket.default.*.arn)
value = one(aws_s3_bucket.default[*].arn)
description = "S3 bucket ARN"
}

output "s3_replication_role_arn" {
value = one(aws_iam_role.replication[*].arn)
description = "The ARN of the IAM Role created for replication, if enabled."
}

output "dynamodb_table_name" {
value = element(
coalescelist(
aws_dynamodb_table.with_server_side_encryption.*.name,
aws_dynamodb_table.without_server_side_encryption.*.name,
[""]
),
0
)
value = one(aws_dynamodb_table.with_server_side_encryption.*.name)
description = "DynamoDB table name"
}

output "dynamodb_table_id" {
value = element(
coalescelist(
aws_dynamodb_table.with_server_side_encryption.*.id,
aws_dynamodb_table.without_server_side_encryption.*.id,
[""]
),
0
)
value = one(aws_dynamodb_table.with_server_side_encryption.*.id)
description = "DynamoDB table ID"
}

output "dynamodb_table_arn" {
value = element(
coalescelist(
aws_dynamodb_table.with_server_side_encryption.*.arn,
aws_dynamodb_table.without_server_side_encryption.*.arn,
[""]
),
0
)
value = one(aws_dynamodb_table.with_server_side_encryption.*.arn)
description = "DynamoDB table ARN"
}

Expand Down
Loading

0 comments on commit 73e86c7

Please sign in to comment.