From 6c6ad0f10d4958299f6853f1a466c18fcb3baa0e Mon Sep 17 00:00:00 2001 From: Yonatan Koren Date: Fri, 2 Jul 2021 15:00:06 -0400 Subject: [PATCH] Fix: Adhere `module.this.enabled` (#66) * Ensure module can be disabled * Add tests to test module when it is disabled. --- main.tf | 37 +++++++++++------- outputs.tf | 12 +++--- test/src/examples_complete_test.go | 61 +++++++++++++++++++++++++++--- 3 files changed, 84 insertions(+), 26 deletions(-) diff --git a/main.tf b/main.tf index 40a667b..e6ff24b 100644 --- a/main.tf +++ b/main.tf @@ -1,4 +1,5 @@ locals { + enabled = module.this.enabled website_config = { redirect_all = [ { @@ -19,7 +20,7 @@ module "logs" { source = "cloudposse/s3-log-storage/aws" version = "0.20.0" attributes = ["logs"] - enabled = var.logs_enabled + enabled = local.enabled && var.logs_enabled standard_transition_days = var.logs_standard_transition_days glacier_transition_days = var.logs_glacier_transition_days expiration_days = var.logs_expiration_days @@ -36,6 +37,8 @@ module "default_label" { } resource "aws_s3_bucket" "default" { + count = local.enabled ? 1 : 0 + #bridgecrew:skip=BC_AWS_S3_1:The bucket used for a public static website. (https://docs.bridgecrew.io/docs/s3_1-acl-read-permissions-everyone) #bridgecrew:skip=BC_AWS_S3_14:Skipping `Ensure all data stored in the S3 bucket is securely encrypted at rest` 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` due to issue using `mfa_delete` by terraform (https://github.com/hashicorp/terraform-provider-aws/issues/629). @@ -106,15 +109,19 @@ resource "aws_s3_bucket" "default" { # AWS only supports a single bucket policy on a bucket. You can combine multiple Statements into a single policy, but not attach multiple policies. # https://github.com/hashicorp/terraform/issues/10543 resource "aws_s3_bucket_policy" "default" { - bucket = aws_s3_bucket.default.id - policy = data.aws_iam_policy_document.default.json + count = local.enabled ? 1 : 0 + + bucket = aws_s3_bucket.default[0].id + policy = data.aws_iam_policy_document.default[0].json } data "aws_iam_policy_document" "default" { + count = local.enabled ? 1 : 0 + statement { actions = ["s3:GetObject"] - resources = ["${aws_s3_bucket.default.arn}/*"] + resources = ["${aws_s3_bucket.default[0].arn}/*"] principals { type = "AWS" @@ -200,7 +207,7 @@ data "aws_iam_policy_document" "default" { } data "aws_iam_policy_document" "replication" { - count = signum(length(var.replication_source_principal_arns)) + count = local.enabled ? signum(length(var.replication_source_principal_arns)) : 0 statement { principals { @@ -216,25 +223,25 @@ data "aws_iam_policy_document" "replication" { ] resources = [ - aws_s3_bucket.default.arn, - "${aws_s3_bucket.default.arn}/*" + aws_s3_bucket.default[0].arn, + "${aws_s3_bucket.default[0].arn}/*" ] } } data "aws_iam_policy_document" "deployment" { - count = length(keys(var.deployment_arns)) + count = local.enabled ? length(keys(var.deployment_arns)) : 0 statement { actions = var.deployment_actions resources = flatten([ formatlist( - "${aws_s3_bucket.default.arn}%s", + "${aws_s3_bucket.default[0].arn}%s", var.deployment_arns[keys(var.deployment_arns)[count.index]] ), formatlist( - "${aws_s3_bucket.default.arn}%s/*", + "${aws_s3_bucket.default[0].arn}%s/*", var.deployment_arns[keys(var.deployment_arns)[count.index]] ) ]) @@ -247,13 +254,15 @@ data "aws_iam_policy_document" "deployment" { } module "dns" { - source = "cloudposse/route53-alias/aws" - version = "0.12.0" + source = "cloudposse/route53-alias/aws" + version = "0.12.0" + + enabled = local.enabled aliases = compact([signum(length(var.parent_zone_id)) == 1 || signum(length(var.parent_zone_name)) == 1 ? var.hostname : ""]) parent_zone_id = var.parent_zone_id parent_zone_name = var.parent_zone_name - target_dns_name = aws_s3_bucket.default.website_domain - target_zone_id = aws_s3_bucket.default.hosted_zone_id + target_dns_name = join("", aws_s3_bucket.default.*.website_domain) + target_zone_id = join("", aws_s3_bucket.default.*.hosted_zone_id) context = module.this.context } diff --git a/outputs.tf b/outputs.tf index 1fe845a..08cdbb6 100644 --- a/outputs.tf +++ b/outputs.tf @@ -4,31 +4,31 @@ output "hostname" { } output "s3_bucket_name" { - value = aws_s3_bucket.default.id + value = join("", aws_s3_bucket.default.*.id) description = "DNS record of the website bucket" } output "s3_bucket_domain_name" { - value = aws_s3_bucket.default.bucket_domain_name + value = join("", aws_s3_bucket.default.*.bucket_domain_name) description = "Name of the website bucket" } output "s3_bucket_arn" { - value = aws_s3_bucket.default.arn + value = join("", aws_s3_bucket.default.*.arn) description = "ARN identifier of the website bucket" } output "s3_bucket_website_endpoint" { - value = aws_s3_bucket.default.website_endpoint + value = join("", aws_s3_bucket.default.*.website_endpoint) description = "The website endpoint URL" } output "s3_bucket_website_domain" { - value = aws_s3_bucket.default.website_domain + value = join("", aws_s3_bucket.default.*.website_domain) description = "The domain of the website endpoint" } output "s3_bucket_hosted_zone_id" { - value = aws_s3_bucket.default.hosted_zone_id + value = join("", aws_s3_bucket.default.*.hosted_zone_id) description = "The Route 53 Hosted Zone ID for this bucket's region" } diff --git a/test/src/examples_complete_test.go b/test/src/examples_complete_test.go index 5c26647..30371f7 100644 --- a/test/src/examples_complete_test.go +++ b/test/src/examples_complete_test.go @@ -20,11 +20,26 @@ func RandStringRunes(n int) string { return string(b) } -// Test the Terraform module in examples/complete using Terratest. func TestExamplesComplete(t *testing.T) { + terraformOptions := &terraform.Options{ + // The path to where our Terraform code is located + TerraformDir: "../../examples/complete", + Upgrade: true, + // Variables to pass to our Terraform code using -var-file options + VarFiles: []string{"fixtures.us-west-1.tfvars"}, + } + + terraform.Init(t, terraformOptions) + // Run tests in parallel + t.Run("Enabled", testExamplesCompleteEnabled) + t.Run("Disabled", testExamplesCompleteDisabled) +} + +// Test the Terraform module in examples/complete using Terratest. +func testExamplesCompleteEnabled(t *testing.T) { t.Parallel() - testName := "s3-website-test-"+RandStringRunes(10) + testName := "s3-website-test-" + RandStringRunes(10) terraformOptions := &terraform.Options{ // The path to where our Terraform code is located @@ -32,9 +47,9 @@ func TestExamplesComplete(t *testing.T) { Upgrade: true, // Variables to pass to our Terraform code using -var-file options VarFiles: []string{"fixtures.us-west-1.tfvars"}, - Vars: map[string]interface{} { - "name": testName, - "hostname": testName+".testing.cloudposse.co", + Vars: map[string]interface{}{ + "name": testName, + "hostname": testName + ".testing.cloudposse.co", }, } @@ -42,7 +57,7 @@ func TestExamplesComplete(t *testing.T) { defer terraform.Destroy(t, terraformOptions) // This will run `terraform init` and `terraform apply` and fail the test if there are any errors - terraform.InitAndApply(t, terraformOptions) + terraform.Apply(t, terraformOptions) // Run `terraform output` to get the value of an output variable hostname := terraform.Output(t, terraformOptions, "hostname") @@ -59,3 +74,37 @@ func TestExamplesComplete(t *testing.T) { // Verify we're getting back the outputs we expect assert.Equal(t, testName+".testing.cloudposse.co.s3.amazonaws.com", s3BucketDomainName) } + +// Test the Terraform module in examples/complete using Terratest, but with the root module disabled. +func testExamplesCompleteDisabled(t *testing.T) { + t.Parallel() + + testName := "s3-website-test-" + RandStringRunes(10) + + terraformOptions := &terraform.Options{ + // The path to where our Terraform code is located + TerraformDir: "../../examples/complete", + Upgrade: true, + EnvVars: map[string]string{ + "TF_CLI_ARGS": "-state=terraform-disabled-test.tfstate", + }, + // Variables to pass to our Terraform code using -var-file options + VarFiles: []string{"fixtures.us-west-1.tfvars"}, + Vars: map[string]interface{}{ + "enabled": "false", + "name": testName, + "hostname": testName + ".testing.cloudposse.co", + }, + } + + // At the end of the test, run `terraform destroy` to clean up any resources that were created + defer terraform.Destroy(t, terraformOptions) + + // This will run `terraform init` and `terraform apply` and fail the test if there are any errors + terraform.Apply(t, terraformOptions) + + // Run `terraform output` to get the value of an output variable + s3BucketDomainName := terraform.Output(t, terraformOptions, "s3_bucket_domain_name") + // Verify we're getting back the outputs we expect + assert.Empty(t, s3BucketDomainName) +}