diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index b8f1b8a..adea23e 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -71,6 +71,14 @@ jobs: id: minMax uses: clowdhaus/terraform-min-max@v1.0.3 + - name: Install hcledit (for terraform_wrapper_module_for_each hook) + shell: bash + run: | + curl -L "$(curl -s https://github.com/gitapi/repos/minamijoyo/hcledit/releases/latest | grep -o -E -m 1 "https://.+?_linux_amd64.tar.gz")" > hcledit.tgz + sudo tar -xzf hcledit.tgz -C /usr/bin/ hcledit + rm -f hcledit.tgz 2> /dev/null + hcledit version + - name: Pre-commit Terraform ${{ steps.minMax.outputs.maxVersion }} uses: clowdhaus/terraform-composite-actions/pre-commit@v1.3.0 with: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 19dda01..954c537 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,6 +3,7 @@ repos: rev: v1.71.0 hooks: - id: terraform_fmt + - id: terraform_wrapper_module_for_each - id: terraform_validate - id: terraform_docs args: diff --git a/README.md b/README.md index 4b90900..79dfe5b 100644 --- a/README.md +++ b/README.md @@ -294,7 +294,7 @@ module "lb" { | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 0.13.1 | +| [terraform](#requirement\_terraform) | >= 1.0.0 | | [aws](#requirement\_aws) | >= 3.67 | ## Providers diff --git a/examples/complete-alb/README.md b/examples/complete-alb/README.md index ead2e9a..8611ebe 100644 --- a/examples/complete-alb/README.md +++ b/examples/complete-alb/README.md @@ -19,7 +19,7 @@ Note that this example may create resources which cost money. Run `terraform des | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 0.13.1 | +| [terraform](#requirement\_terraform) | >= 1.0.0 | | [aws](#requirement\_aws) | >= 3.40 | | [null](#requirement\_null) | >= 2.0 | | [random](#requirement\_random) | >= 2.0 | diff --git a/examples/complete-alb/versions.tf b/examples/complete-alb/versions.tf index 7dd3807..8317cb9 100644 --- a/examples/complete-alb/versions.tf +++ b/examples/complete-alb/versions.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 0.13.1" + required_version = ">= 1.0.0" required_providers { aws = { diff --git a/examples/complete-nlb/README.md b/examples/complete-nlb/README.md index e54a154..88aefba 100644 --- a/examples/complete-nlb/README.md +++ b/examples/complete-nlb/README.md @@ -19,7 +19,7 @@ Note that this example may create resources which cost money. Run `terraform des | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 0.13.1 | +| [terraform](#requirement\_terraform) | >= 1.0.0 | | [aws](#requirement\_aws) | >= 3.40 | | [random](#requirement\_random) | >= 2.0 | diff --git a/examples/complete-nlb/versions.tf b/examples/complete-nlb/versions.tf index 2fc3eef..45d645c 100644 --- a/examples/complete-nlb/versions.tf +++ b/examples/complete-nlb/versions.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 0.13.1" + required_version = ">= 1.0.0" required_providers { aws = { diff --git a/main.tf b/main.tf index 6c0a168..59a2510 100644 --- a/main.tf +++ b/main.tf @@ -144,7 +144,7 @@ locals { } resource "aws_lambda_permission" "lb" { - for_each = var.create_lb && local.target_group_attachments_lambda != null ? local.target_group_attachments_lambda : {} + for_each = { for k, v in local.target_group_attachments_lambda : k => v if local.create_lb } function_name = each.value.lambda_function_name qualifier = try(each.value.lambda_qualifier, null) @@ -158,7 +158,7 @@ resource "aws_lambda_permission" "lb" { } resource "aws_lb_target_group_attachment" "this" { - for_each = local.create_lb && local.target_group_attachments != null ? local.target_group_attachments : {} + for_each = { for k, v in local.target_group_attachments : k => v if local.create_lb } target_group_arn = aws_lb_target_group.main[each.value.tg_index].arn target_id = each.value.target_id diff --git a/versions.tf b/versions.tf index ef030ea..5f49438 100644 --- a/versions.tf +++ b/versions.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 0.13.1" + required_version = ">= 1.0.0" required_providers { aws = { diff --git a/wrappers/README.md b/wrappers/README.md new file mode 100644 index 0000000..909f5ab --- /dev/null +++ b/wrappers/README.md @@ -0,0 +1,100 @@ +# Wrapper for the root module + +The configuration in this directory contains an implementation of a single module wrapper pattern, which allows managing several copies of a module in places where using the native Terraform 0.13+ `for_each` feature is not feasible (e.g., with Terragrunt). + +You may want to use a single Terragrunt configuration file to manage multiple resources without duplicating `terragrunt.hcl` files for each copy of the same module. + +This wrapper does not implement any extra functionality. + +## Usage with Terragrunt + +`terragrunt.hcl`: + +```hcl +terraform { + source = "tfr:///terraform-aws-modules/alb/aws//wrappers" + # Alternative source: + # source = "git::git@github.com:terraform-aws-modules/terraform-aws-alb.git?ref=master//wrappers" +} + +inputs = { + defaults = { # Default values + create = true + tags = { + Terraform = "true" + Environment = "dev" + } + } + + items = { + my-item = { + # omitted... can be any argument supported by the module + } + my-second-item = { + # omitted... can be any argument supported by the module + } + # omitted... + } +} +``` + +## Usage with Terraform + +```hcl +module "wrapper" { + source = "terraform-aws-modules/alb/aws//wrappers" + + defaults = { # Default values + create = true + tags = { + Terraform = "true" + Environment = "dev" + } + } + + items = { + my-item = { + # omitted... can be any argument supported by the module + } + my-second-item = { + # omitted... can be any argument supported by the module + } + # omitted... + } +} +``` + +## Example: Manage multiple S3 buckets in one Terragrunt layer + +`eu-west-1/s3-buckets/terragrunt.hcl`: + +```hcl +terraform { + source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers" + # Alternative source: + # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git?ref=master//wrappers" +} + +inputs = { + defaults = { + force_destroy = true + + attach_elb_log_delivery_policy = true + attach_lb_log_delivery_policy = true + attach_deny_insecure_transport_policy = true + attach_require_latest_tls_policy = true + } + + items = { + bucket1 = { + bucket = "my-random-bucket-1" + } + bucket2 = { + bucket = "my-random-bucket-2" + tags = { + Secure = "probably" + } + } + } +} +``` diff --git a/wrappers/main.tf b/wrappers/main.tf new file mode 100644 index 0000000..a05a3cd --- /dev/null +++ b/wrappers/main.tf @@ -0,0 +1,42 @@ +module "wrapper" { + source = "../" + + for_each = var.items + + create_lb = try(each.value.create_lb, var.defaults.create_lb, true) + drop_invalid_header_fields = try(each.value.drop_invalid_header_fields, var.defaults.drop_invalid_header_fields, false) + enable_deletion_protection = try(each.value.enable_deletion_protection, var.defaults.enable_deletion_protection, false) + enable_http2 = try(each.value.enable_http2, var.defaults.enable_http2, true) + enable_cross_zone_load_balancing = try(each.value.enable_cross_zone_load_balancing, var.defaults.enable_cross_zone_load_balancing, false) + extra_ssl_certs = try(each.value.extra_ssl_certs, var.defaults.extra_ssl_certs, []) + https_listeners = try(each.value.https_listeners, var.defaults.https_listeners, []) + http_tcp_listeners = try(each.value.http_tcp_listeners, var.defaults.http_tcp_listeners, []) + https_listener_rules = try(each.value.https_listener_rules, var.defaults.https_listener_rules, []) + http_tcp_listener_rules = try(each.value.http_tcp_listener_rules, var.defaults.http_tcp_listener_rules, []) + idle_timeout = try(each.value.idle_timeout, var.defaults.idle_timeout, 60) + ip_address_type = try(each.value.ip_address_type, var.defaults.ip_address_type, "ipv4") + listener_ssl_policy_default = try(each.value.listener_ssl_policy_default, var.defaults.listener_ssl_policy_default, "ELBSecurityPolicy-2016-08") + internal = try(each.value.internal, var.defaults.internal, false) + load_balancer_create_timeout = try(each.value.load_balancer_create_timeout, var.defaults.load_balancer_create_timeout, "10m") + load_balancer_delete_timeout = try(each.value.load_balancer_delete_timeout, var.defaults.load_balancer_delete_timeout, "10m") + name = try(each.value.name, var.defaults.name, null) + name_prefix = try(each.value.name_prefix, var.defaults.name_prefix, null) + load_balancer_type = try(each.value.load_balancer_type, var.defaults.load_balancer_type, "application") + load_balancer_update_timeout = try(each.value.load_balancer_update_timeout, var.defaults.load_balancer_update_timeout, "10m") + access_logs = try(each.value.access_logs, var.defaults.access_logs, {}) + subnets = try(each.value.subnets, var.defaults.subnets, null) + subnet_mapping = try(each.value.subnet_mapping, var.defaults.subnet_mapping, []) + tags = try(each.value.tags, var.defaults.tags, {}) + lb_tags = try(each.value.lb_tags, var.defaults.lb_tags, {}) + target_group_tags = try(each.value.target_group_tags, var.defaults.target_group_tags, {}) + https_listener_rules_tags = try(each.value.https_listener_rules_tags, var.defaults.https_listener_rules_tags, {}) + http_tcp_listener_rules_tags = try(each.value.http_tcp_listener_rules_tags, var.defaults.http_tcp_listener_rules_tags, {}) + https_listeners_tags = try(each.value.https_listeners_tags, var.defaults.https_listeners_tags, {}) + http_tcp_listeners_tags = try(each.value.http_tcp_listeners_tags, var.defaults.http_tcp_listeners_tags, {}) + security_groups = try(each.value.security_groups, var.defaults.security_groups, []) + target_groups = try(each.value.target_groups, var.defaults.target_groups, []) + vpc_id = try(each.value.vpc_id, var.defaults.vpc_id, null) + enable_waf_fail_open = try(each.value.enable_waf_fail_open, var.defaults.enable_waf_fail_open, false) + desync_mitigation_mode = try(each.value.desync_mitigation_mode, var.defaults.desync_mitigation_mode, "defensive") + putin_khuylo = try(each.value.putin_khuylo, var.defaults.putin_khuylo, true) +} diff --git a/wrappers/outputs.tf b/wrappers/outputs.tf new file mode 100644 index 0000000..5da7c09 --- /dev/null +++ b/wrappers/outputs.tf @@ -0,0 +1,5 @@ +output "wrapper" { + description = "Map of outputs of a wrapper." + value = module.wrapper + # sensitive = false # No sensitive module output found +} diff --git a/wrappers/variables.tf b/wrappers/variables.tf new file mode 100644 index 0000000..a6ea096 --- /dev/null +++ b/wrappers/variables.tf @@ -0,0 +1,11 @@ +variable "defaults" { + description = "Map of default values which will be used for each item." + type = any + default = {} +} + +variable "items" { + description = "Maps of items to create a wrapper from. Values are passed through to the module." + type = any + default = {} +} diff --git a/wrappers/versions.tf b/wrappers/versions.tf new file mode 100644 index 0000000..51cad10 --- /dev/null +++ b/wrappers/versions.tf @@ -0,0 +1,3 @@ +terraform { + required_version = ">= 0.13.1" +}