From c6f7f55e953a3e9fbf5ff5e1bb5f91d43714a5f3 Mon Sep 17 00:00:00 2001 From: Byungjin Park Date: Tue, 12 Dec 2023 14:18:19 +0900 Subject: [PATCH] Refine alb, nlb, gwlb, alb-listener, nlb-listener --- .../alb-with-instance-target-group/main.tf | 15 +- .../versions.tf | 4 +- examples/alb-with-ip-target-group/main.tf | 15 +- examples/alb-with-ip-target-group/versions.tf | 4 +- .../gwlb-with-instance-target-group/main.tf | 2 +- .../versions.tf | 4 +- examples/gwlb-with-ip-target-group/main.tf | 2 +- .../gwlb-with-ip-target-group/versions.tf | 4 +- examples/nlb-with-alb-target-group/alb.tf | 15 +- examples/nlb-with-alb-target-group/main.tf | 13 +- .../nlb-with-alb-target-group/versions.tf | 4 +- .../nlb-with-instance-target-group/main.tf | 13 +- .../versions.tf | 4 +- examples/nlb-with-ip-target-group/main.tf | 13 +- examples/nlb-with-ip-target-group/versions.tf | 4 +- modules/alb-listener/README.md | 10 +- modules/alb-listener/main.tf | 14 +- modules/alb-listener/variables.tf | 33 ++- modules/alb-listener/versions.tf | 4 +- modules/alb/README.md | 27 +-- modules/alb/main.tf | 85 ++++--- modules/alb/migrations.tf | 6 + modules/alb/outputs.tf | 60 ++--- modules/alb/security-group.tf | 93 +++----- modules/alb/variables.tf | 218 ++++++++++++++---- modules/alb/versions.tf | 4 +- modules/gwlb/README.md | 10 +- modules/gwlb/main.tf | 38 ++- modules/gwlb/outputs.tf | 5 - modules/gwlb/variables.tf | 21 +- modules/gwlb/versions.tf | 4 +- modules/nlb-listener/README.md | 11 +- modules/nlb-listener/main.tf | 14 +- modules/nlb-listener/variables.tf | 44 ++-- modules/nlb-listener/versions.tf | 4 +- modules/nlb/README.md | 26 ++- modules/nlb/main.tf | 96 ++++++-- modules/nlb/outputs.tf | 31 ++- modules/nlb/security-groups.tf | 64 +++++ modules/nlb/variables.tf | 183 +++++++++++++-- modules/nlb/versions.tf | 4 +- 41 files changed, 847 insertions(+), 378 deletions(-) create mode 100644 modules/nlb/security-groups.tf diff --git a/examples/alb-with-instance-target-group/main.tf b/examples/alb-with-instance-target-group/main.tf index 598a617..1818163 100644 --- a/examples/alb-with-instance-target-group/main.tf +++ b/examples/alb-with-instance-target-group/main.tf @@ -32,15 +32,16 @@ module "alb" { network_mapping = { for az, subnet in data.aws_subnet.default : az => { - subnet_id = subnet.id + subnet = subnet.id } } default_security_group = { + enabled = true name = "tedilabs-alb-instance" description = "Managed by Terraform." - ingress_cidrs = ["10.0.0.0/8", "172.31.0.0/16"] + listener_ingress_ipv4_cidrs = ["10.0.0.0/8", "172.31.0.0/16"] } security_groups = [] @@ -114,9 +115,13 @@ module "alb" { ] ## Access Log - access_log_enabled = false - access_log_s3_bucket = "my-bucket" - access_log_s3_key_prefix = "/tedilabs-alb-instance/" + access_log = { + enabled = false + s3_bucket = { + name = "my-bucket" + key_prefix = "/tedilabs-alb-instance/" + } + } tags = { "project" = "terraform-aws-load-balancer-examples" diff --git a/examples/alb-with-instance-target-group/versions.tf b/examples/alb-with-instance-target-group/versions.tf index fe30da3..e5cd366 100644 --- a/examples/alb-with-instance-target-group/versions.tf +++ b/examples/alb-with-instance-target-group/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = "~> 1.5" + required_version = "~> 1.6" required_providers { aws = { source = "hashicorp/aws" - version = "~> 4.0" + version = "~> 5.0" } } } diff --git a/examples/alb-with-ip-target-group/main.tf b/examples/alb-with-ip-target-group/main.tf index a700894..108c5d1 100644 --- a/examples/alb-with-ip-target-group/main.tf +++ b/examples/alb-with-ip-target-group/main.tf @@ -32,15 +32,16 @@ module "alb" { network_mapping = { for az, subnet in data.aws_subnet.default : az => { - subnet_id = subnet.id + subnet = subnet.id } } default_security_group = { + enabled = true name = "tedilabs-alb-ip" description = "Managed by Terraform." - ingress_cidrs = ["10.0.0.0/8", "172.31.0.0/16"] + listener_ingress_ipv4_cidrs = ["10.0.0.0/8", "172.31.0.0/16"] } security_groups = [] @@ -114,9 +115,13 @@ module "alb" { ] ## Access Log - access_log_enabled = false - access_log_s3_bucket = "my-bucket" - access_log_s3_key_prefix = "/tedilabs-alb-ip/" + access_log = { + enabled = false + s3_bucket = { + name = "my-bucket" + key_prefix = "/tedilabs-alb-ip/" + } + } tags = { "project" = "terraform-aws-load-balancer-examples" diff --git a/examples/alb-with-ip-target-group/versions.tf b/examples/alb-with-ip-target-group/versions.tf index fe30da3..e5cd366 100644 --- a/examples/alb-with-ip-target-group/versions.tf +++ b/examples/alb-with-ip-target-group/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = "~> 1.5" + required_version = "~> 1.6" required_providers { aws = { source = "hashicorp/aws" - version = "~> 4.0" + version = "~> 5.0" } } } diff --git a/examples/gwlb-with-instance-target-group/main.tf b/examples/gwlb-with-instance-target-group/main.tf index e46e8ea..eaa10a7 100644 --- a/examples/gwlb-with-instance-target-group/main.tf +++ b/examples/gwlb-with-instance-target-group/main.tf @@ -28,7 +28,7 @@ module "gwlb" { network_mapping = { for az, subnet in data.aws_subnet.default : az => { - subnet_id = subnet.id + subnet = subnet.id } } diff --git a/examples/gwlb-with-instance-target-group/versions.tf b/examples/gwlb-with-instance-target-group/versions.tf index fe30da3..e5cd366 100644 --- a/examples/gwlb-with-instance-target-group/versions.tf +++ b/examples/gwlb-with-instance-target-group/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = "~> 1.5" + required_version = "~> 1.6" required_providers { aws = { source = "hashicorp/aws" - version = "~> 4.0" + version = "~> 5.0" } } } diff --git a/examples/gwlb-with-ip-target-group/main.tf b/examples/gwlb-with-ip-target-group/main.tf index 82383c2..edb4489 100644 --- a/examples/gwlb-with-ip-target-group/main.tf +++ b/examples/gwlb-with-ip-target-group/main.tf @@ -28,7 +28,7 @@ module "gwlb" { network_mapping = { for az, subnet in data.aws_subnet.default : az => { - subnet_id = subnet.id + subnet = subnet.id } } diff --git a/examples/gwlb-with-ip-target-group/versions.tf b/examples/gwlb-with-ip-target-group/versions.tf index fe30da3..e5cd366 100644 --- a/examples/gwlb-with-ip-target-group/versions.tf +++ b/examples/gwlb-with-ip-target-group/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = "~> 1.5" + required_version = "~> 1.6" required_providers { aws = { source = "hashicorp/aws" - version = "~> 4.0" + version = "~> 5.0" } } } diff --git a/examples/nlb-with-alb-target-group/alb.tf b/examples/nlb-with-alb-target-group/alb.tf index cec8a6b..ee02705 100644 --- a/examples/nlb-with-alb-target-group/alb.tf +++ b/examples/nlb-with-alb-target-group/alb.tf @@ -15,15 +15,16 @@ module "alb" { network_mapping = { for az, subnet in data.aws_subnet.default : az => { - subnet_id = subnet.id + subnet = subnet.id } } default_security_group = { + enabled = true name = "tedilabs-nlb-alb-alb" description = "Managed by Terraform." - ingress_cidrs = ["10.0.0.0/8", "172.31.0.0/16"] + listener_ingress_ipv4_cidrs = ["10.0.0.0/8", "172.31.0.0/16"] } security_groups = [] @@ -79,9 +80,13 @@ module "alb" { ] ## Access Log - access_log_enabled = false - access_log_s3_bucket = "my-bucket" - access_log_s3_key_prefix = "/tedilabs-nlb-alb-alb/" + access_log = { + enabled = false + s3_bucket = { + name = "my-bucket" + key_prefix = "/tedilabs-nlb-alb-alb/" + } + } tags = { "project" = "terraform-aws-load-balancer-examples" diff --git a/examples/nlb-with-alb-target-group/main.tf b/examples/nlb-with-alb-target-group/main.tf index c6d590f..1426862 100644 --- a/examples/nlb-with-alb-target-group/main.tf +++ b/examples/nlb-with-alb-target-group/main.tf @@ -31,7 +31,7 @@ module "nlb" { network_mapping = { for az, subnet in data.aws_subnet.default : az => { - subnet_id = subnet.id + subnet = subnet.id } } @@ -45,9 +45,14 @@ module "nlb" { target_group = module.target_group.arn }] - access_log_enabled = false - access_log_s3_bucket = "my-bucket" - access_log_s3_key_prefix = "/tedilabs-nlb-alb/" + ## Access Log + access_log = { + enabled = false + s3_bucket = { + name = "my-bucket" + key_prefix = "/tedilabs-nlb-alb/" + } + } tags = { "project" = "terraform-aws-load-balancer-examples" diff --git a/examples/nlb-with-alb-target-group/versions.tf b/examples/nlb-with-alb-target-group/versions.tf index fe30da3..e5cd366 100644 --- a/examples/nlb-with-alb-target-group/versions.tf +++ b/examples/nlb-with-alb-target-group/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = "~> 1.5" + required_version = "~> 1.6" required_providers { aws = { source = "hashicorp/aws" - version = "~> 4.0" + version = "~> 5.0" } } } diff --git a/examples/nlb-with-instance-target-group/main.tf b/examples/nlb-with-instance-target-group/main.tf index 89dfd2b..640353d 100644 --- a/examples/nlb-with-instance-target-group/main.tf +++ b/examples/nlb-with-instance-target-group/main.tf @@ -31,7 +31,7 @@ module "nlb" { network_mapping = { for az, subnet in data.aws_subnet.default : az => { - subnet_id = subnet.id + subnet = subnet.id } } @@ -45,9 +45,14 @@ module "nlb" { target_group = module.target_group.arn }] - access_log_enabled = false - access_log_s3_bucket = "my-bucket" - access_log_s3_key_prefix = "/tedilabs-nlb-instance/" + ## Access Log + access_log = { + enabled = false + s3_bucket = { + name = "my-bucket" + key_prefix = "/tedilabs-nlb-instance/" + } + } tags = { "project" = "terraform-aws-load-balancer-examples" diff --git a/examples/nlb-with-instance-target-group/versions.tf b/examples/nlb-with-instance-target-group/versions.tf index fe30da3..e5cd366 100644 --- a/examples/nlb-with-instance-target-group/versions.tf +++ b/examples/nlb-with-instance-target-group/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = "~> 1.5" + required_version = "~> 1.6" required_providers { aws = { source = "hashicorp/aws" - version = "~> 4.0" + version = "~> 5.0" } } } diff --git a/examples/nlb-with-ip-target-group/main.tf b/examples/nlb-with-ip-target-group/main.tf index 8a07acb..a1819ea 100644 --- a/examples/nlb-with-ip-target-group/main.tf +++ b/examples/nlb-with-ip-target-group/main.tf @@ -31,7 +31,7 @@ module "nlb" { network_mapping = { for az, subnet in data.aws_subnet.default : az => { - subnet_id = subnet.id + subnet = subnet.id } } @@ -45,9 +45,14 @@ module "nlb" { target_group = module.target_group.arn }] - access_log_enabled = false - access_log_s3_bucket = "my-bucket" - access_log_s3_key_prefix = "/tedilabs-nlb-ip/" + ## Access Log + access_log = { + enabled = false + s3_bucket = { + name = "my-bucket" + key_prefix = "/tedilabs-nlb-ip/" + } + } tags = { "project" = "terraform-aws-load-balancer-examples" diff --git a/examples/nlb-with-ip-target-group/versions.tf b/examples/nlb-with-ip-target-group/versions.tf index fe30da3..e5cd366 100644 --- a/examples/nlb-with-ip-target-group/versions.tf +++ b/examples/nlb-with-ip-target-group/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = "~> 1.5" + required_version = "~> 1.6" required_providers { aws = { source = "hashicorp/aws" - version = "~> 4.0" + version = "~> 5.0" } } } diff --git a/modules/alb-listener/README.md b/modules/alb-listener/README.md index deb55e4..580715b 100644 --- a/modules/alb-listener/README.md +++ b/modules/alb-listener/README.md @@ -11,14 +11,14 @@ This module creates following resources. | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.5 | -| [aws](#requirement\_aws) | >= 3.71 | +| [terraform](#requirement\_terraform) | >= 1.6 | +| [aws](#requirement\_aws) | >= 5.30 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | 5.19.0 | +| [aws](#provider\_aws) | 5.30.0 | ## Modules @@ -49,9 +49,7 @@ This module creates following resources. | [resource\_group\_name](#input\_resource\_group\_name) | (Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. | `string` | `""` | no | | [rules](#input\_rules) | (Optional) The rules that you define for the listener determine how the load balancer routes requests to the targets in one or more target groups. Each rule consists of a priority, one or more actions, and one or more conditions. Each item of `rules` block as defined below.
(Required) `priority` - The priority for the rule between `1` and `50000`. A listener can't have multiple rules with the same priority.
(Required) `conditions` - A set of conditions of the rule. One or more condition blocks can be set per rule. Most condition types can only be specified once per rule except for `HTTP_HEADER` and `QUERY` which can be specified multiple times. All condition blocks must be satisfied for the rule to match. Each item of `conditions` block as defined below.
(Required) `type` - The type of the condition. Valid values are `HOST`, `HTTP_METHOD`, `HTTP_HEADER`, `PATH`, `QUERY` and `SOURCE_IP`.
(Optional) `name` - The name of HTTP header to search. The maximum size is 40 characters. Comparison is case insensitive. Only RFC7240 characters are supported. Wildcards are not supported. You cannot use HTTP header condition to specify the host header, use a `HOST` condition instead. Only required if `type` is `HTTP_HEADER`.
(Required) `values` for `HOST` - A list of host header patterns to match. The maximum size of each pattern is 128 characters. Comparison is case insensitive. Wildcard characters supported: * (matches 0 or more characters) and ? (matches exactly 1 character). Only one pattern needs to match for the condition to be satisfied.
(Required) `values` for `HTTP_METHOD` - A list of HTTP request methods or verbs to match. Maximum size is 40 characters. Only allowed characters are A-Z, hyphen (-) and underscore (\_). Comparison is case sensitive. Wildcards are not supported. Only one needs to match for the condition to be satisfied. AWS recommends that GET and HEAD requests are routed in the same way because the response to a HEAD request may be cached.
(Required) `values` for `HTTP_HEADER` - A list of header value patterns to match. Maximum size of each pattern is 128 characters. Comparison is case insensitive. Wildcard characters supported: * (matches 0 or more characters) and ? (matches exactly 1 character). If the same header appears multiple times in the request they will be searched in order until a match is found. Only one pattern needs to match for the condition to be satisfied. To require that all of the strings are a match, create one condition block per string.
(Required) `values` for `PATH` - A list of path patterns to match against the request URL. Maximum size of each pattern is 128 characters. Comparison is case sensitive. Wildcard characters supported: * (matches 0 or more characters) and ? (matches exactly 1 character). Only one pattern needs to match for the condition to be satisfied. Path pattern is compared only to the path of the URL, not to its query string. To compare against the query string, use a `QUERY` condition.
(Required) `values` for `QUERY` - A list of query string pairs to match. Each query string pair consists of `key` and `value`. Maximum size of each string is 128 characters. Comparison is case insensitive. Wildcard characters supported: * (matches 0 or more characters) and ? (matches exactly 1 character). To search for a literal '*' or '?' character in a query string, escape the character with a backslash (\). Only one pair needs to match for the condition to be satisfied.
(Required) `values` for `SOURCE_IP` - A list of source IP CIDR notations to match. You can use both IPv4 and IPv6 addresses. Wildcards are not supported. Condition is satisfied if the source IP address of the request matches one of the CIDR blocks. Condition is not satisfied by the addresses in the `X-Forwarded-For` header, use `HTTP_HEADER` condition instead.
(Required) `action_type` - The type of the routing action. Valid values are `FORWARD`, `WEIGHTED_FORWARD`, `FIXED_RESPONSE`, `REDIRECT_301` and `REDIRECT_302`.
(Optional) `action_parameters` - Same with `default_action_parameters`. | `any` | `[]` | no | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | -| [tls\_additional\_certificates](#input\_tls\_additional\_certificates) | (Optional) A set of ARNs of the certificate to attach to the listener. This is for additional certificates and does not replace the default certificate on the listener. | `set(string)` | `[]` | no | -| [tls\_certificate](#input\_tls\_certificate) | (Optional) The ARN of the default SSL server certificate. For adding additional SSL certificates, see the `tls_additional_certificates` variable. Required if `protocol` is `HTTPS`. | `string` | `null` | no | -| [tls\_security\_policy](#input\_tls\_security\_policy) | (Optional) The name of security policy for a Secure Socket Layer (SSL) negotiation configuration. This is used to negotiate SSL connections with clients. Required if protocol is `HTTPS`. Defaults to `ELBSecurityPolicy-2016-08` security policy. The `ELBSecurityPolicy-2016-08` security policy is always used for backend connections. Application Load Balancers do not support custom security policies. | `string` | `"ELBSecurityPolicy-2016-08"` | no | +| [tls](#input\_tls) | (Optional) The configuration for TLS listener of the load balancer. Required if `protocol` is `HTTPS`. `tls` block as defined below.
(Optional) `certificate` - The ARN of the default SSL server certificate. For adding additional SSL certificates, see the `additional_certificates` variable.
(Optional) `additional_certificates` - A set of ARNs of the certificate to attach to the listener. This is for additional certificates and does not replace the default certificate on the listener.
(Optional) `security_policy` - The name of security policy for a Secure Socket Layer (SSL) negotiation configuration. This is used to negotiate SSL connections with clients. Required if protocol is `HTTPS`. Defaults to `ELBSecurityPolicy-2016-08` security policy. The `ELBSecurityPolicy-2016-08` security policy is always used for backend connections. Application Load Balancers do not support custom security policies. |
object({
certificate = optional(string)
additional_certificates = optional(set(string), [])
security_policy = optional(string, "ELBSecurityPolicy-2016-08")
})
| `{}` | no | ## Outputs diff --git a/modules/alb-listener/main.tf b/modules/alb-listener/main.tf index 525a69c..255f9eb 100644 --- a/modules/alb-listener/main.tf +++ b/modules/alb-listener/main.tf @@ -14,12 +14,18 @@ locals { } : {} } - locals { load_balancer_name = split("/", var.load_balancer)[2] tls_enabled = var.protocol == "HTTPS" } + +################################################### +# ALB Listener +################################################### + +# INFO: Not supported attributes +# - `alpn_policy` resource "aws_lb_listener" "this" { load_balancer_arn = var.load_balancer @@ -27,8 +33,8 @@ resource "aws_lb_listener" "this" { protocol = var.protocol ## TLS - certificate_arn = local.tls_enabled ? var.tls_certificate : null - ssl_policy = local.tls_enabled ? var.tls_security_policy : null + certificate_arn = local.tls_enabled ? var.tls.certificate : null + ssl_policy = local.tls_enabled ? var.tls.security_policy : null dynamic "default_action" { for_each = (var.default_action_type == "FORWARD" @@ -325,7 +331,7 @@ resource "aws_lb_listener_rule" "this" { ################################################### resource "aws_lb_listener_certificate" "this" { - for_each = toset(local.tls_enabled ? var.tls_additional_certificates : []) + for_each = toset(local.tls_enabled ? var.tls.additional_certificates : []) listener_arn = aws_lb_listener.this.arn certificate_arn = each.key diff --git a/modules/alb-listener/variables.tf b/modules/alb-listener/variables.tf index 5fb84a1..9d0a13b 100644 --- a/modules/alb-listener/variables.tf +++ b/modules/alb-listener/variables.tf @@ -1,6 +1,7 @@ variable "load_balancer" { description = "(Required) The ARN of the application load balancer to add the listener." type = string + nullable = false } variable "port" { @@ -130,25 +131,21 @@ variable "rules" { } } -variable "tls_certificate" { - description = "(Optional) The ARN of the default SSL server certificate. For adding additional SSL certificates, see the `tls_additional_certificates` variable. Required if `protocol` is `HTTPS`." - type = string - default = null -} - -variable "tls_additional_certificates" { - description = "(Optional) A set of ARNs of the certificate to attach to the listener. This is for additional certificates and does not replace the default certificate on the listener." - type = set(string) - default = [] - nullable = false -} - # INFO: https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html#describe-ssl-policies -variable "tls_security_policy" { - description = "(Optional) The name of security policy for a Secure Socket Layer (SSL) negotiation configuration. This is used to negotiate SSL connections with clients. Required if protocol is `HTTPS`. Defaults to `ELBSecurityPolicy-2016-08` security policy. The `ELBSecurityPolicy-2016-08` security policy is always used for backend connections. Application Load Balancers do not support custom security policies." - type = string - default = "ELBSecurityPolicy-2016-08" - nullable = false +variable "tls" { + description = < [terraform](#requirement\_terraform) | >= 1.5 | -| [aws](#requirement\_aws) | >= 4.25 | +| [terraform](#requirement\_terraform) | >= 1.6 | +| [aws](#requirement\_aws) | >= 5.30 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | 5.19.0 | +| [aws](#provider\_aws) | 5.30.0 | ## Modules @@ -27,7 +27,7 @@ This module creates following resources. |------|--------|---------| | [listener](#module\_listener) | ../alb-listener | n/a | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.10.0 | -| [security\_group](#module\_security\_group) | tedilabs/network/aws//modules/security-group | ~> 0.25.0 | +| [security\_group](#module\_security\_group) | tedilabs/network/aws//modules/security-group | ~> 0.31.0 | ## Resources @@ -43,10 +43,9 @@ This module creates following resources. |------|-------------|------|---------|:--------:| | [name](#input\_name) | (Required) The name of the load balancer. This name must be unique within your AWS account, can have a maximum of 32 characters, must contain only alphanumeric characters or hyphens, and must not begin or end with a hyphen. | `string` | n/a | yes | | [vpc\_id](#input\_vpc\_id) | (Required) The ID of the VPC which the load balancer belongs to. | `string` | n/a | yes | -| [access\_log\_enabled](#input\_access\_log\_enabled) | (Optional) Indicates whether to enable access logs. Defaults to `false`, even when bucket is specified. | `bool` | `false` | no | -| [access\_log\_s3\_bucket](#input\_access\_log\_s3\_bucket) | (Optional) The name of the S3 bucket used to store the access logs. | `string` | `null` | no | -| [access\_log\_s3\_key\_prefix](#input\_access\_log\_s3\_key\_prefix) | (Optional) The key prefix for the specified S3 bucket. | `string` | `""` | no | -| [default\_security\_group](#input\_default\_security\_group) | (Optional) The configuration of the default security group for your load balancer. `default_security_group` block as defined below.
(Optional) `name` - The name of the default security group.
(Optional) `description` - The description of the default security group.
(Optional) `ingress_cidrs` - A list of IPv4 CIDR blocks to allow inbound traffic from.
(Optional) `ingress_ipv6_cidrs` - A list of IPv6 CIDR blocks to allow inbound traffic from.
(Optional) `ingress_prefix_lists` - A list of Prefix List IDs to allow inbound traffic from.
(Optional) `ingress_security_groups` - A list of source Security Group IDs to allow inbound traffic from. | `any` | `{}` | no | +| [access\_log](#input\_access\_log) | (Optional) A configuration for the access logs for the load balancer. Access logs deliver detailed logs of all requests made to your Elastic Load Balancer. `access_log` as defined below.
(Optional) `enabled` - Indicates whether to enable access logs. Defaults to `false`.
(Optional) `s3_bucket` - A configuration of the S3 Bucket for access logs. `s3_bucket` as defined below.
(Required) `name` - The name of the S3 bucket used to store the access logs.
(Optional) `key_prefix` - The key prefix for the specified S3 bucket. |
object({
enabled = optional(bool, false)
s3_bucket = optional(object({
name = optional(string)
key_prefix = optional(string, "")
}), {})
})
| `{}` | no | +| [cross\_zone\_load\_balancing\_enabled](#input\_cross\_zone\_load\_balancing\_enabled) | (Optional) Cross-zone load balancing distributes traffic evenly across all targets in the Availability Zones enabled for the load balancer. Cross-zone load balancing is always on for Application Load Balancers. However, you can turn it off for a specific target group using target group attributes. Defaults to `true`. | `bool` | `true` | no | +| [default\_security\_group](#input\_default\_security\_group) | (Optional) The configuration of the default security group for the load balancer. `default_security_group` block as defined below.
(Optional) `enabled` - Whether to use the default security group. Defaults to `true`.
(Optional) `name` - The name of the default security group. If not provided, the load balancer name is used for the name of security group.
(Optional) `description` - The description of the default security group.
(Optional) `ingress_rules` - A list of ingress rules in a security group. Defaults to `[]`. Each block of `ingress_rules` as defined below.
(Required) `id` - The ID of the ingress rule. This value is only used internally within Terraform code.
(Optional) `description` - The description of the rule.
(Required) `protocol` - The protocol to match. Note that if `protocol` is set to `-1`, it translates to all protocols, all port ranges, and `from_port` and `to_port` values should not be defined.
(Required) `from_port` - The start of port range for the protocols.
(Required) `to_port` - The end of port range for the protocols.
(Optional) `ipv4_cidrs` - The IPv4 network ranges to allow, in CIDR notation.
(Optional) `ipv6_cidrs` - The IPv6 network ranges to allow, in CIDR notation.
(Optional) `prefix_lists` - The prefix list IDs to allow.
(Optional) `security_groups` - The source security group IDs to allow.
(Optional) `self` - Whether the security group itself will be added as a source to this ingress rule.
(Optional) `egress_rules` - A list of egress rules in a security group. Defaults to `[{ id = "default", protocol = -1, from_port = 1, to_port=65535, ipv4_cidrs = ["0.0.0.0/0"] }]`. Each block of `egress_rules` as defined below.
(Required) `id` - The ID of the egress rule. This value is only used internally within Terraform code.
(Optional) `description` - The description of the rule.
(Required) `protocol` - The protocol to match. Note that if `protocol` is set to `-1`, it translates to all protocols, all port ranges, and `from_port` and `to_port` values should not be defined.
(Required) `from_port` - The start of port range for the protocols.
(Required) `to_port` - The end of port range for the protocols.
(Optional) `ipv4_cidrs` - The IPv4 network ranges to allow, in CIDR notation.
(Optional) `ipv6_cidrs` - The IPv6 network ranges to allow, in CIDR notation.
(Optional) `prefix_lists` - The prefix list IDs to allow.
(Optional) `security_groups` - The source security group IDs to allow.
(Optional) `self` - Whether the security group itself will be added as a source to this ingress rule.
(Optional) `listener_ingress_ipv4_cidrs` - A list of IPv4 CIDR ranges to allow on the listener port. Defaults to `[]`."
(Optional) `listener_ingress_ipv6_cidrs` - A list of IPv6 CIDR ranges to allow on the listener port. Defaults to `[]`."
(Optional) `listener_ingress_prefix_lists` - A list of prefix list IDs for AWS services to allow on the listener port. Defaults to `[]`."
(Optional) `listener_ingress_security_groups` - A list of security group IDs to allow on the listener port. Defaults to `[]`." |
object({
enabled = optional(bool, true)
name = optional(string)
description = optional(string, "Managed by Terraform.")
ingress_rules = optional(
list(object({
id = string
description = optional(string, "Managed by Terraform.")
protocol = string
from_port = number
to_port = number
ipv4_cidrs = optional(list(string), [])
ipv6_cidrs = optional(list(string), [])
prefix_lists = optional(list(string), [])
security_groups = optional(list(string), [])
self = optional(bool, false)
})),
[]
)
egress_rules = optional(
list(object({
id = string
description = optional(string, "Managed by Terraform.")
protocol = string
from_port = number
to_port = number
ipv4_cidrs = optional(list(string), [])
ipv6_cidrs = optional(list(string), [])
prefix_lists = optional(list(string), [])
security_groups = optional(list(string), [])
self = optional(bool, false)
})),
[{
id = "default"
description = "Allow all outbound traffic."
protocol = "-1"
from_port = 1
to_port = 65535
ipv4_cidrs = ["0.0.0.0/0"]
}]
)
listener_ingress_ipv4_cidrs = optional(list(string), [])
listener_ingress_ipv6_cidrs = optional(list(string), [])
listener_ingress_prefix_lists = optional(list(string), [])
listener_ingress_security_groups = optional(list(string), [])
})
| `{}` | no | | [deletion\_protection\_enabled](#input\_deletion\_protection\_enabled) | (Optional) Indicates whether deletion of the load balancer via the AWS API will be protected. Defaults to `false`. | `bool` | `false` | no | | [desync\_mitigation\_mode](#input\_desync\_mitigation\_mode) | (Optional) Determines how the load balancer handles requests that might pose a security risk to your application. Valid values are `DEFENSIVE`, `STRICTEST` and `MONITOR`. Defaults to `DEFENSIVE`. | `string` | `"DEFENSIVE"` | no | | [drop\_invalid\_header\_fields](#input\_drop\_invalid\_header\_fields) | (Optional) Indicates whether HTTP headers with header fields that are not valid are removed by the load balancer (true) or routed to targets (false). Elastic Load Balancing requires that message header names contain only alphanumeric characters and hyphens. Defaults to `false`. | `bool` | `false` | no | @@ -54,16 +53,19 @@ This module creates following resources. | [idle\_timeout](#input\_idle\_timeout) | (Optional) The number of seconds before the load balancer determines the connection is idle and closes it. Defaults to `60` | `number` | `60` | no | | [ip\_address\_type](#input\_ip\_address\_type) | (Optional) The type of IP addresses used by the subnets for your load balancer. The possible values are `IPV4` and `DUALSTACK`. | `string` | `"IPV4"` | no | | [is\_public](#input\_is\_public) | (Optional) Indicates whether the load balancer will be public. Defaults to `false`. | `bool` | `false` | no | -| [listeners](#input\_listeners) | (Optional) A list of listener configurations of the application load balancer. Listeners listen for connection requests using their `protocol` and `port`. Each value of `listener` block as defined below.
(Required) `port` - The number of port on which the listener of load balancer is listening.
(Required) `protocol` - The protocol for connections from clients to the load balancer. Valid values are `HTTP` and `HTTPS`.
(Required) `default_action_type` - The type of default routing action. Valid values are `FORWARD`, `FIXED_RESPONSE`, `REDIRECT_301` and `REDIRECT_302`.
(Optional) `default_action_parameters` - Configuration block for the parameters of the default routing action.
(Optional) `rules` - The rules that you define for the listener determine how the load balancer routes requests to the targets in one or more target groups.
(Optional) `tls_certificate` - The ARN of the default SSL server certificate. For adding additional SSL certificates, see the `tls_additional_certificates` variable. Required if `protocol` is `HTTPS`.
(Optional) `tls_additional_certificates` - A set of ARNs of the certificate to attach to the listener. This is for additional certificates and does not replace the default certificate on the listener.
(Optional) `tls_security_policy` - The name of security policy for a Secure Socket Layer (SSL) negotiation configuration. This is used to negotiate SSL connections with clients. Required if protocol is `HTTPS`. Defaults to `ELBSecurityPolicy-2016-08` security policy. The `ELBSecurityPolicy-2016-08` security policy is always used for backend connections. Application Load Balancers do not support custom security policies. | `any` | `[]` | no | +| [listeners](#input\_listeners) | (Optional) A list of listener configurations of the application load balancer. Listeners listen for connection requests using their `protocol` and `port`. Each value of `listener` block as defined below.
(Required) `port` - The number of port on which the listener of load balancer is listening.
(Required) `protocol` - The protocol for connections from clients to the load balancer. Valid values are `HTTP` and `HTTPS`.
(Required) `default_action_type` - The type of default routing action. Valid values are `FORWARD`, `FIXED_RESPONSE`, `REDIRECT_301` and `REDIRECT_302`.
(Optional) `default_action_parameters` - Configuration block for the parameters of the default routing action.
(Optional) `rules` - The rules that you define for the listener determine how the load balancer routes requests to the targets in one or more target groups.
(Optional) The configuration for TLS listener of the load balancer. Required if `protocol` is `HTTPS`. `tls` block as defined below.
(Optional) `certificate` - The ARN of the default SSL server certificate. For adding additional SSL certificates, see the `additional_certificates` variable.
(Optional) `additional_certificates` - A set of ARNs of the certificate to attach to the listener. This is for additional certificates and does not replace the default certificate on the listener.
(Optional) `security_policy` - The name of security policy for a Secure Socket Layer (SSL) negotiation configuration. This is used to negotiate SSL connections with clients. Required if protocol is `HTTPS`. Defaults to `ELBSecurityPolicy-2016-08` security policy. The `ELBSecurityPolicy-2016-08` security policy is always used for backend connections. Application Load Balancers do not support custom security policies. | `any` | `[]` | no | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | -| [network\_mapping](#input\_network\_mapping) | (Optional) The configuration for the load balancer how routes traffic to targets in which subnets, and in accordance with IP address settings. Select at least two Availability Zone and one subnet for each zone. The load balancer will route traffic only to targets in the selected Availability Zones. Zones that are not supported by the load balancer or VPC cannot be selected. Subnets can be added, but not removed, once a load balancer is created. Each key of `network_mapping` is the availability zone id like `apne2-az1`, `use1-az1`. Each value of `network_mapping` block as defined below.
(Required) `subnet_id` - The id of the subnet of which to attach to the load balancer. You can specify only one subnet per Availability Zone. | `map(map(string))` | `{}` | no | +| [network\_mapping](#input\_network\_mapping) | (Optional) The configuration for the load balancer how routes traffic to targets in which subnets, and in accordance with IP address settings. Select at least two Availability Zone and one subnet for each zone. The load balancer will route traffic only to targets in the selected Availability Zones. Zones that are not supported by the load balancer or VPC cannot be selected. Subnets can be added, but not removed, once a load balancer is created. Each key of `network_mapping` is the availability zone id like `apne2-az1`, `use1-az1`. Each value of `network_mapping` block as defined below.
(Required) `subnet` - The id of the subnet of which to attach to the load balancer. You can specify only one subnet per Availability Zone. |
map(object({
subnet = string
}))
| `{}` | no | | [preserve\_host\_header](#input\_preserve\_host\_header) | (Optional) Indicates whether the Application Load Balancer should preserve the Host header in the HTTP request and send it to the target without any change. Defaults to `false`. | `bool` | `false` | no | | [resource\_group\_description](#input\_resource\_group\_description) | (Optional) The description of Resource Group. | `string` | `"Managed by Terraform."` | no | | [resource\_group\_enabled](#input\_resource\_group\_enabled) | (Optional) Whether to create Resource Group to find and group AWS resources which are created by this module. | `bool` | `true` | no | | [resource\_group\_name](#input\_resource\_group\_name) | (Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. | `string` | `""` | no | -| [security\_groups](#input\_security\_groups) | (Optional) A set of security group IDs to assign to the load balancer. | `set(string)` | `[]` | no | +| [security\_groups](#input\_security\_groups) | (Optional) A list of security group IDs to assign to the Load Balancer. | `list(string)` | `[]` | no | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | +| [timeouts](#input\_timeouts) | (Optional) How long to wait for the load balancer to be created/updated/deleted. |
object({
create = optional(string, "10m")
update = optional(string, "10m")
delete = optional(string, "10m")
})
| `{}` | no | +| [tls\_negotiation\_headers\_enabled](#input\_tls\_negotiation\_headers\_enabled) | (Optional) Whether the two TLS negotiation headers (`x-amzn-tls-version` and `x-amzn-tls-cipher-suite`), which contain information about the negotiated TLS version and cipher suite, are added to the client request before sending it to the target. Defaults to `false`. | `bool` | `false` | no | | [waf\_fail\_open\_enabled](#input\_waf\_fail\_open\_enabled) | (Optional) Indicates whether to allow a WAF-enabled load balancer to route requests to targets if it is unable to forward the request to AWS WAF. Defaults to `false`. | `bool` | `false` | no | +| [xff\_header](#input\_xff\_header) | (Optional) The configuration for . `xff_header` block as defined below.
(Optional) `mode` - How the load balancer modifies the `X-Forwarded-For` header in the HTTP request before sending the request to the target. Valid values are `APPEND`, `PRESERVE`, `REMOVE`. Defaults to `APPEND`.
`APPEND` - The load balancer appends the IP address of the client to the `X-Forwarded-For` header.
`PRESERVE` - The load balancer preserves the original IP address of the client.
`REMOVE` - The load balancer removes the `X-Forwarded-For` header from the request.
(Optional) `client_port_preservation_enabled` - Whether the `X-Forwarded-For` header should preserve the source port that the client used to connect to the load balancer. Defaults to `false`. |
object({
mode = optional(string, "APPEND")
client_port_preservation_enabled = optional(bool, false)
})
| `{}` | no | ## Outputs @@ -74,8 +76,7 @@ This module creates following resources. | [arn\_suffix](#output\_arn\_suffix) | The ARN suffix for use with CloudWatch Metrics. | | [attributes](#output\_attributes) | Load Balancer Attributes that applied to the application load balancer. | | [availability\_zone\_ids](#output\_availability\_zone\_ids) | A list of the Availability Zone IDs which are used by the load balancer. | -| [available\_availability\_zone\_ids](#output\_available\_availability\_zone\_ids) | A list of the Availability Zone IDs available to the current account and region. | -| [default\_security\_group](#output\_default\_security\_group) | The default security group of the load balancer. | +| [default\_security\_group](#output\_default\_security\_group) | The default security group ID of the load balancer. | | [domain](#output\_domain) | The DNS name of the load balancer. | | [id](#output\_id) | The ID of the load balancer. | | [ip\_address\_type](#output\_ip\_address\_type) | The type of IP addresses used by the subnets for your load balancer. | diff --git a/modules/alb/main.tf b/modules/alb/main.tf index 172dfd9..8140886 100644 --- a/modules/alb/main.tf +++ b/modules/alb/main.tf @@ -22,7 +22,7 @@ data "aws_availability_zones" "available" { data "aws_subnet" "this" { for_each = var.network_mapping - id = each.value.subnet_id + id = each.value.subnet } locals { @@ -31,58 +31,89 @@ locals { available_availability_zone_ids = data.aws_availability_zones.available.zone_ids network_mapping = { for zone_id in local.available_availability_zone_ids : - zone_id => try(merge({ - cidr_block = data.aws_subnet.this[zone_id].cidr_block != "" ? data.aws_subnet.this[zone_id].cidr_block : null - }, var.network_mapping[zone_id]), null) + zone_id => (contains(keys(var.network_mapping), zone_id) + ? { + subnet = data.aws_subnet.this[zone_id].id + + ipv4_cidr = data.aws_subnet.this[zone_id].cidr_block != "" ? data.aws_subnet.this[zone_id].cidr_block : null + ipv6_cidr = data.aws_subnet.this[zone_id].ipv6_cidr_block != "" ? data.aws_subnet.this[zone_id].ipv6_cidr_block : null + } + : null + ) } enabled_network_mapping = { for zone_id, config in local.network_mapping : zone_id => config - if try(config.subnet_id, null) != null + if config != null } availability_zone_ids = keys(local.enabled_network_mapping) } + +################################################### +# Application Load Balancer +################################################### + # INFO: Not supported attributes -# - `enable_cross_zone_load_balancing` # - `customer_owned_ipv4_pool` +# - `dns_record_client_routing_policy` +# - `enforce_security_group_inbound_rules_on_private_link_traffic` +# - `subnets` +# TODO: `customer_owned_ipv4_pool` (ALB Only) resource "aws_lb" "this" { name = var.name load_balancer_type = lower(local.load_balancer_type) internal = !var.is_public ip_address_type = lower(var.ip_address_type) - security_groups = setunion( - [module.security_group.id], - var.security_groups, - ) dynamic "subnet_mapping" { for_each = local.enabled_network_mapping content { - subnet_id = subnet_mapping.value.subnet_id + subnet_id = subnet_mapping.value.subnet } } + + ## Access Control + security_groups = local.security_groups + + + ## Logging dynamic "access_logs" { - for_each = var.access_log_enabled ? ["go"] : [] + for_each = var.access_log.enabled ? [var.access_log] : [] + iterator = log content { - enabled = var.access_log_enabled - bucket = var.access_log_s3_bucket - prefix = var.access_log_s3_key_prefix + enabled = log.value.enabled + bucket = log.value.s3_bucket.name + prefix = log.value.s3_bucket.key_prefix } } + ## Attributes - desync_mitigation_mode = lower(var.desync_mitigation_mode) - drop_invalid_header_fields = var.drop_invalid_header_fields - enable_deletion_protection = var.deletion_protection_enabled - enable_http2 = var.http2_enabled - enable_waf_fail_open = var.waf_fail_open_enabled - idle_timeout = var.idle_timeout - preserve_host_header = var.preserve_host_header + desync_mitigation_mode = lower(var.desync_mitigation_mode) + enable_cross_zone_load_balancing = var.cross_zone_load_balancing_enabled + enable_deletion_protection = var.deletion_protection_enabled + enable_http2 = var.http2_enabled + enable_waf_fail_open = var.waf_fail_open_enabled + idle_timeout = var.idle_timeout + + # Headers + drop_invalid_header_fields = var.drop_invalid_header_fields + enable_tls_version_and_cipher_suite_headers = var.tls_negotiation_headers_enabled + preserve_host_header = var.preserve_host_header + enable_xff_client_port = var.xff_header.client_port_preservation_enabled + xff_header_processing_mode = lower(var.xff_header.mode) + + + timeouts { + create = var.timeouts.create + update = var.timeouts.update + delete = var.timeouts.delete + } tags = merge( { @@ -112,14 +143,16 @@ module "listener" { protocol = each.value.protocol default_action_type = each.value.default_action_type - default_action_parameters = try(each.value.default_action_parameters, {}) + default_action_parameters = each.value.default_action_parameters rules = try(each.value.rules, {}) ## TLS - tls_certificate = try(each.value.tls_certificate, null) - tls_additional_certificates = try(each.value.tls_additional_certificates, []) - tls_security_policy = try(each.value.tls_security_policy, "ELBSecurityPolicy-2016-08") + tls = { + certificate = try(each.value.tls.certificate, null) + additional_certificates = try(each.value.tls.additional_certificates, []) + security_policy = try(each.value.tls.security_policy, "ELBSecurityPolicy-2016-08") + } resource_group_enabled = false module_tags_enabled = false diff --git a/modules/alb/migrations.tf b/modules/alb/migrations.tf index 0c687ff..5d78094 100644 --- a/modules/alb/migrations.tf +++ b/modules/alb/migrations.tf @@ -1,3 +1,9 @@ +# INFO: 2023-12-11 - Make optional `security_group` module +moved { + from = module.security_group + to = module.security_group[0] +} + # 2022-10-20 moved { from = aws_resourcegroups_group.this[0] diff --git a/modules/alb/outputs.tf b/modules/alb/outputs.tf index 9e8f604..b8bf79c 100644 --- a/modules/alb/outputs.tf +++ b/modules/alb/outputs.tf @@ -43,34 +43,11 @@ output "ip_address_type" { value = upper(aws_lb.this.ip_address_type) } -output "default_security_group" { - description = "The default security group of the load balancer." - value = { - id = module.security_group.id - name = module.security_group.name - - ingress_cidrs = try(var.default_security_group.ingress_cidrs, []) - ingress_ipv6_cidrs = try(var.default_security_group.ingress_ipv6_cidrs, []) - ingress_prefix_lists = try(var.default_security_group.ingress_prefix_lists, []) - ingress_security_groups = try(var.default_security_group.ingress_security_groups, []) - } -} - -output "security_groups" { - description = "A set of security group IDs which is assigned to the load balancer." - value = aws_lb.this.security_groups -} - output "availability_zone_ids" { description = "A list of the Availability Zone IDs which are used by the load balancer." value = local.availability_zone_ids } -output "available_availability_zone_ids" { - description = "A list of the Availability Zone IDs available to the current account and region." - value = local.available_availability_zone_ids -} - output "vpc_id" { description = "The VPC ID of the load balancer." value = aws_lb.this.vpc_id @@ -86,25 +63,38 @@ output "network_mapping" { value = local.network_mapping } +output "default_security_group" { + description = "The default security group ID of the load balancer." + value = one(module.security_group[*].id) +} + +output "security_groups" { + description = "A set of security group IDs which is assigned to the load balancer." + value = aws_lb.this.security_groups +} + output "access_log" { description = "The configuration for access logs of the load balancer." - value = { - enabled = var.access_log_enabled - s3_bucket = var.access_log_s3_bucket - s3_key_prefix = var.access_log_s3_key_prefix - } + value = var.access_log } output "attributes" { description = "Load Balancer Attributes that applied to the application load balancer." value = { - desync_mitigation_mode = upper(aws_lb.this.desync_mitigation_mode) - drop_invalid_header_fields = aws_lb.this.drop_invalid_header_fields - deletion_protection_enabled = aws_lb.this.enable_deletion_protection - http2_enabled = aws_lb.this.enable_http2 - waf_fail_open_enabled = aws_lb.this.enable_waf_fail_open - idle_timeout = aws_lb.this.idle_timeout - preserve_host_header = aws_lb.this.preserve_host_header + cross_zone_load_balancing_enabled = aws_lb.this.enable_cross_zone_load_balancing + desync_mitigation_mode = upper(aws_lb.this.desync_mitigation_mode) + deletion_protection_enabled = aws_lb.this.enable_deletion_protection + http2_enabled = aws_lb.this.enable_http2 + waf_fail_open_enabled = aws_lb.this.enable_waf_fail_open + idle_timeout = aws_lb.this.idle_timeout + + tls_negotiation_headers_enabled = aws_lb.this.enable_tls_version_and_cipher_suite_headers + drop_invalid_header_fields = aws_lb.this.drop_invalid_header_fields + preserve_host_header = aws_lb.this.preserve_host_header + xff_header = { + mode = upper(aws_lb.this.xff_header_processing_mode) + client_port_preservation_enabled = aws_lb.this.enable_xff_client_port + } } } diff --git a/modules/alb/security-group.tf b/modules/alb/security-group.tf index 8fe45a2..a43ac3c 100644 --- a/modules/alb/security-group.tf +++ b/modules/alb/security-group.tf @@ -1,83 +1,56 @@ +locals { + security_groups = concat( + (var.default_security_group.enabled + ? module.security_group[*].id + : [] + ), + var.security_groups, + ) +} + + ################################################### # Security Group for Application Load Balancer ################################################### module "security_group" { source = "tedilabs/network/aws//modules/security-group" - version = "~> 0.25.0" + version = "~> 0.31.0" - vpc_id = var.vpc_id + count = var.default_security_group.enabled ? 1 : 0 - name = try(var.default_security_group.name, local.metadata.name) - description = try(var.default_security_group.description, "Managed by Terraform.") + name = coalesce(var.default_security_group.name, local.metadata.name) + description = var.default_security_group.description + vpc_id = values(data.aws_subnet.this)[0].vpc_id ingress_rules = concat( + var.default_security_group.ingress_rules, [ for listener in var.listeners : { - id = "${listener.port}/cidrs" - description = "Allow inbound traffic from the CIDRs on the load balancer listener port." - protocol = "tcp" + id = "listener-${listener.port}" + description = "Default rule for the load balancer listener." + protocol = listener.protocol from_port = listener.port to_port = listener.port - cidr_blocks = try(var.default_security_group.ingress_cidrs, []) - ipv6_cidr_blocks = try(var.default_security_group.ingress_ipv6_cidrs, []) + ipv4_cidrs = var.default_security_group.listener_ingress_ipv4_cidrs + ipv6_cidrs = var.default_security_group.listener_ingress_ipv6_cidrs + prefix_lists = var.default_security_group.listener_ingress_prefix_lists + security_groups = var.default_security_group.listener_ingress_security_groups } if anytrue([ - length(try(var.default_security_group.ingress_cidrs, [])) > 0, - length(try(var.default_security_group.ingress_ipv6_cidrs, [])) > 0, + length(var.default_security_group.listener_ingress_ipv4_cidrs) > 0, + length(var.default_security_group.listener_ingress_ipv6_cidrs) > 0, + length(var.default_security_group.listener_ingress_prefix_lists) > 0, + length(var.default_security_group.listener_ingress_security_groups) > 0, ]) - ], - [ - for listener in var.listeners : { - id = "${listener.port}/prefix-lists" - description = "Allow inbound traffic from the Prefix Lists on the load balancer listener port." - protocol = "tcp" - from_port = listener.port - to_port = listener.port - - prefix_list_ids = try(var.default_security_group.ingress_prefix_lists, []) - } - if length(try(var.default_security_group.ingress_prefix_lists, [])) > 0 - ], - flatten([ - for listener in var.listeners : [ - for security_group in try(var.default_security_group.ingress_security_groups, []) : { - id = "${listener.port}/security-groups" - description = "Allow inbound traffic from the source Security Groups on the load balancer listener port." - protocol = "tcp" - from_port = listener.port - to_port = listener.port - - source_security_group_id = security_group - } - ] - ]), + ] ) - # TODO: Limit egress rules egress_rules = concat( - [ - { - id = "instance-listener/cidrs" - description = "Allow outbound traffic to instances on the instance listener port." - protocol = "tcp" - from_port = 1 - to_port = 65535 - - cidr_blocks = ["0.0.0.0/0"] - }, - ], - # [ - # { - # id = "instance-health-check/cidrs" - # description = "Allow outbound traffic to instances on the health check port." - # protocol = "tcp" - # from_port = 1 - # to_port = 65535 - # - # cidr_blocks = "0.0.0.0/0" - # }, - # ] + var.default_security_group.egress_rules, + # TODO: Limit egress rules + # TODO: Target Ports + # TODO: Target Health Check Ports ) revoke_rules_on_delete = true diff --git a/modules/alb/variables.tf b/modules/alb/variables.tf index 49468f6..3337612 100644 --- a/modules/alb/variables.tf +++ b/modules/alb/variables.tf @@ -28,61 +28,142 @@ variable "ip_address_type" { } } +variable "vpc_id" { + description = "(Required) The ID of the VPC which the load balancer belongs to." + type = string +} + +variable "network_mapping" { + description = < [terraform](#requirement\_terraform) | >= 1.5 | -| [aws](#requirement\_aws) | >= 3.71 | +| [terraform](#requirement\_terraform) | >= 1.6 | +| [aws](#requirement\_aws) | >= 5.30 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | 5.19.0 | +| [aws](#provider\_aws) | 5.30.0 | ## Modules @@ -43,11 +43,12 @@ This module creates following resources. | [deletion\_protection\_enabled](#input\_deletion\_protection\_enabled) | (Optional) Indicates whether deletion of the load balancer via the AWS API will be protected. Defaults to `false`. | `bool` | `false` | no | | [listeners](#input\_listeners) | (Optional) A list of listener configurations of the gateway load balancer. Listeners listen for connection requests using their `protocol` and `port`. Each value of `listener` block as defined below.
(Required) `port` - The number of port on which the listener of load balancer is listening. Must be `6081`.
(Required) `target_group` - The ARN of the target group to which to route traffic. |
list(object({
port = number
target_group = string
}))
| `[]` | no | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | -| [network\_mapping](#input\_network\_mapping) | (Optional) The configuration for the load balancer how routes traffic to targets in which subnets, and in accordance with IP address settings. Select at least one Availability Zone and one subnet for each zone. We recommend selecting at least two Availability Zones. The load balancer will route traffic only to targets in the selected Availability Zones. Zones that are not supported by the load balancer or VPC cannot be selected. Subnets can be added, but not removed, once a load balancer is created. Each key of `network_mapping` is the availability zone id like `apne2-az1`, `use1-az1`. Each value of `network_mapping` block as defined below.
(Required) `subnet_id` - The id of the subnet of which to attach to the load balancer. You can specify only one subnet per Availability Zone. | `map(map(string))` | `{}` | no | +| [network\_mapping](#input\_network\_mapping) | (Optional) The configuration for the load balancer how routes traffic to targets in which subnets, and in accordance with IP address settings. Select at least one Availability Zone and one subnet for each zone. We recommend selecting at least two Availability Zones. The load balancer will route traffic only to targets in the selected Availability Zones. Zones that are not supported by the load balancer or VPC cannot be selected. Subnets can be added, but not removed, once a load balancer is created. Each key of `network_mapping` is the availability zone id like `apne2-az1`, `use1-az1`. Each value of `network_mapping` block as defined below.
(Required) `subnet` - The id of the subnet of which to attach to the load balancer. You can specify only one subnet per Availability Zone. |
map(object({
subnet = string
}))
| `{}` | no | | [resource\_group\_description](#input\_resource\_group\_description) | (Optional) The description of Resource Group. | `string` | `"Managed by Terraform."` | no | | [resource\_group\_enabled](#input\_resource\_group\_enabled) | (Optional) Whether to create Resource Group to find and group AWS resources which are created by this module. | `bool` | `true` | no | | [resource\_group\_name](#input\_resource\_group\_name) | (Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. | `string` | `""` | no | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | +| [timeouts](#input\_timeouts) | (Optional) How long to wait for the load balancer to be created/updated/deleted. |
object({
create = optional(string, "10m")
update = optional(string, "10m")
delete = optional(string, "10m")
})
| `{}` | no | ## Outputs @@ -57,7 +58,6 @@ This module creates following resources. | [arn\_suffix](#output\_arn\_suffix) | The ARN suffix for use with CloudWatch Metrics. | | [attributes](#output\_attributes) | Load Balancer Attributes that applied to the gateway load balancer. | | [availability\_zone\_ids](#output\_availability\_zone\_ids) | A list of the Availability Zone IDs which are used by the load balancer. | -| [available\_availability\_zone\_ids](#output\_available\_availability\_zone\_ids) | A list of the Availability Zone IDs available to the current account and region. | | [id](#output\_id) | The ID of the load balancer. | | [listeners](#output\_listeners) | Listeners of the load balancer. | | [name](#output\_name) | The name of the load balancer. | diff --git a/modules/gwlb/main.tf b/modules/gwlb/main.tf index ff6c8ab..27928e5 100644 --- a/modules/gwlb/main.tf +++ b/modules/gwlb/main.tf @@ -22,7 +22,7 @@ data "aws_availability_zones" "available" { data "aws_subnet" "this" { for_each = var.network_mapping - id = each.value.subnet_id + id = each.value.subnet } locals { @@ -31,29 +31,47 @@ locals { available_availability_zone_ids = data.aws_availability_zones.available.zone_ids network_mapping = { for zone_id in local.available_availability_zone_ids : - zone_id => try(merge({ - cidr_block = data.aws_subnet.this[zone_id].cidr_block != "" ? data.aws_subnet.this[zone_id].cidr_block : null - }, var.network_mapping[zone_id]), null) + zone_id => (contains(keys(var.network_mapping), zone_id) + ? { + subnet = data.aws_subnet.this[zone_id].id + + ipv4_cidr = data.aws_subnet.this[zone_id].cidr_block != "" ? data.aws_subnet.this[zone_id].cidr_block : null + ipv6_cidr = data.aws_subnet.this[zone_id].ipv6_cidr_block != "" ? data.aws_subnet.this[zone_id].ipv6_cidr_block : null + } + : null + ) } enabled_network_mapping = { for zone_id, config in local.network_mapping : zone_id => config - if try(config.subnet_id, null) != null + if config != null } availability_zone_ids = keys(local.enabled_network_mapping) } + +################################################### +# Gateway Load Balancer +################################################### + # INFO: Not supported attributes # - `access_logs` # - `customer_owned_ipv4_pool` # - `desync_mitigation_mode` +# - `dns_record_client_routing_policy` # - `drop_invalid_header_fields` # - `enable_http2` +# - `enable_tls_version_and_cipher_suite_headers` # - `enable_waf_fail_open` +# - `enable_xff_client_port` +# - `enforce_security_group_inbound_rules_on_private_link_traffic` # - `idle_timeout` # - `internal` # - `ip_address_type` +# - `preserve_host_header` # - `security_groups` +# - `subnets` +# - `xff_header_processing_mode` resource "aws_lb" "this" { name = var.name @@ -63,14 +81,22 @@ resource "aws_lb" "this" { for_each = local.enabled_network_mapping content { - subnet_id = subnet_mapping.value.subnet_id + subnet_id = subnet_mapping.value.subnet } } + ## Attributes enable_cross_zone_load_balancing = var.cross_zone_load_balancing_enabled enable_deletion_protection = var.deletion_protection_enabled + + timeouts { + create = var.timeouts.create + update = var.timeouts.update + delete = var.timeouts.delete + } + tags = merge( { "Name" = local.metadata.name diff --git a/modules/gwlb/outputs.tf b/modules/gwlb/outputs.tf index 4b6384f..188307d 100644 --- a/modules/gwlb/outputs.tf +++ b/modules/gwlb/outputs.tf @@ -28,11 +28,6 @@ output "availability_zone_ids" { value = local.availability_zone_ids } -output "available_availability_zone_ids" { - description = "A list of the Availability Zone IDs available to the current account and region." - value = local.available_availability_zone_ids -} - output "vpc_id" { description = "The VPC ID of the load balancer." value = aws_lb.this.vpc_id diff --git a/modules/gwlb/variables.tf b/modules/gwlb/variables.tf index 96b9413..97eade1 100644 --- a/modules/gwlb/variables.tf +++ b/modules/gwlb/variables.tf @@ -12,11 +12,13 @@ variable "name" { variable "network_mapping" { description = < [terraform](#requirement\_terraform) | >= 1.5 | -| [aws](#requirement\_aws) | >= 3.71 | +| [terraform](#requirement\_terraform) | >= 1.6 | +| [aws](#requirement\_aws) | >= 5.30 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | 5.19.0 | +| [aws](#provider\_aws) | 5.30.0 | ## Modules @@ -45,10 +45,7 @@ This module creates following resources. | [resource\_group\_enabled](#input\_resource\_group\_enabled) | (Optional) Whether to create Resource Group to find and group AWS resources which are created by this module. | `bool` | `true` | no | | [resource\_group\_name](#input\_resource\_group\_name) | (Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. | `string` | `""` | no | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | -| [tls\_additional\_certificates](#input\_tls\_additional\_certificates) | (Optional) A set of ARNs of the certificate to attach to the listener. This is for additional certificates and does not replace the default certificate on the listener. | `set(string)` | `[]` | no | -| [tls\_alpn\_policy](#input\_tls\_alpn\_policy) | (Optional) The policy of the Application-Layer Protocol Negotiation (ALPN) to select. ALPN is a TLS extension that includes the protocol negotiation within the exchange of hello messages. Can be set if `protocol` is `TLS`. Valid values are `HTTP1Only`, `HTTP2Only`, `HTTP2Optional`, `HTTP2Preferred`, and `None`. Defaults to `None`. | `string` | `"None"` | no | -| [tls\_certificate](#input\_tls\_certificate) | (Optional) The ARN of the default SSL server certificate. For adding additional SSL certificates, see the `tls_additional_certificates` variable. Required if `protocol` is `TLS`. | `string` | `null` | no | -| [tls\_security\_policy](#input\_tls\_security\_policy) | (Optional) The name of security policy for a Secure Socket Layer (SSL) negotiation configuration. This is used to negotiate SSL connections with clients. Required if protocol is `TLS`. Recommend using the `ELBSecurityPolicy-TLS13-1-2-2021-06` security policy. This security policy includes TLS 1.3, which is optimized for security and performance, and is backward compatible with TLS 1.2. | `string` | `"ELBSecurityPolicy-TLS13-1-2-2021-06"` | no | +| [tls](#input\_tls) | (Optional) The configuration for TLS listener of the load balancer. Required if `protocol` is `TLS`. `tls` block as defined below.
(Optional) `certificate` - The ARN of the default SSL server certificate. For adding additional SSL certificates, see the `additional_certificates` variable.
(Optional) `additional_certificates` - A set of ARNs of the certificate to attach to the listener. This is for additional certificates and does not replace the default certificate on the listener.
(Optional) `security_policy` - The name of security policy for a Secure Socket Layer (SSL) negotiation configuration. This is used to negotiate SSL connections with clients. Required if protocol is `TLS`. Recommend using the `ELBSecurityPolicy-TLS13-1-2-2021-06` security policy. This security policy includes TLS 1.3, which is optimized for security and performance, and is backward compatible with TLS 1.2.
(Optional) `alpn_policy` - The policy of the Application-Layer Protocol Negotiation (ALPN) to select. ALPN is a TLS extension that includes the protocol negotiation within the exchange of hello messages. Can be set if `protocol` is `TLS`. Valid values are `HTTP1Only`, `HTTP2Only`, `HTTP2Optional`, `HTTP2Preferred`, and `None`. Defaults to `None`. |
object({
certificate = optional(string)
additional_certificates = optional(set(string), [])
security_policy = optional(string, "ELBSecurityPolicy-TLS13-1-2-2021-06")
alpn_policy = optional(string, "None")
})
| `{}` | no | ## Outputs diff --git a/modules/nlb-listener/main.tf b/modules/nlb-listener/main.tf index f11ab18..d0daead 100644 --- a/modules/nlb-listener/main.tf +++ b/modules/nlb-listener/main.tf @@ -14,12 +14,16 @@ locals { } : {} } - locals { load_balancer_name = split("/", var.load_balancer)[2] tls_enabled = var.protocol == "TLS" } + +################################################### +# NLB Listener +################################################### + resource "aws_lb_listener" "this" { load_balancer_arn = var.load_balancer @@ -27,9 +31,9 @@ resource "aws_lb_listener" "this" { protocol = var.protocol ## TLS - certificate_arn = local.tls_enabled ? var.tls_certificate : null - ssl_policy = local.tls_enabled ? var.tls_security_policy : null - alpn_policy = local.tls_enabled ? var.tls_alpn_policy : null + certificate_arn = local.tls_enabled ? var.tls.certificate : null + ssl_policy = local.tls_enabled ? var.tls.security_policy : null + alpn_policy = local.tls_enabled ? var.tls.alpn_policy : null default_action { type = "forward" @@ -51,7 +55,7 @@ resource "aws_lb_listener" "this" { ################################################### resource "aws_lb_listener_certificate" "this" { - for_each = toset(local.tls_enabled ? var.tls_additional_certificates : []) + for_each = toset(local.tls_enabled ? var.tls.additional_certificates : []) listener_arn = aws_lb_listener.this.arn certificate_arn = each.key diff --git a/modules/nlb-listener/variables.tf b/modules/nlb-listener/variables.tf index 87c5310..286cce9 100644 --- a/modules/nlb-listener/variables.tf +++ b/modules/nlb-listener/variables.tf @@ -1,11 +1,13 @@ variable "load_balancer" { description = "(Required) The ARN of the network load balancer to add the listener." type = string + nullable = false } variable "port" { description = "(Required) The number of port on which the listener of load balancer is listening." type = number + nullable = false } variable "protocol" { @@ -22,37 +24,29 @@ variable "protocol" { variable "target_group" { description = "(Required) The ARN of the target group to which to route traffic." type = string -} - -variable "tls_certificate" { - description = "(Optional) The ARN of the default SSL server certificate. For adding additional SSL certificates, see the `tls_additional_certificates` variable. Required if `protocol` is `TLS`." - type = string - default = null -} - -variable "tls_additional_certificates" { - description = "(Optional) A set of ARNs of the certificate to attach to the listener. This is for additional certificates and does not replace the default certificate on the listener." - type = set(string) - default = [] nullable = false } # INFO: https://docs.aws.amazon.com/elasticloadbalancing/latest/network/create-tls-listener.html#describe-ssl-policies -variable "tls_security_policy" { - description = "(Optional) The name of security policy for a Secure Socket Layer (SSL) negotiation configuration. This is used to negotiate SSL connections with clients. Required if protocol is `TLS`. Recommend using the `ELBSecurityPolicy-TLS13-1-2-2021-06` security policy. This security policy includes TLS 1.3, which is optimized for security and performance, and is backward compatible with TLS 1.2." - type = string - default = "ELBSecurityPolicy-TLS13-1-2-2021-06" - nullable = false -} - -variable "tls_alpn_policy" { - description = "(Optional) The policy of the Application-Layer Protocol Negotiation (ALPN) to select. ALPN is a TLS extension that includes the protocol negotiation within the exchange of hello messages. Can be set if `protocol` is `TLS`. Valid values are `HTTP1Only`, `HTTP2Only`, `HTTP2Optional`, `HTTP2Preferred`, and `None`. Defaults to `None`." - type = string - default = "None" - nullable = false +variable "tls" { + description = < [terraform](#requirement\_terraform) | >= 1.5 | -| [aws](#requirement\_aws) | >= 3.71 | +| [terraform](#requirement\_terraform) | >= 1.6 | +| [aws](#requirement\_aws) | >= 5.30 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | 5.19.0 | +| [aws](#provider\_aws) | 5.30.0 | +| [terraform](#provider\_terraform) | n/a | ## Modules @@ -26,12 +27,14 @@ This module creates following resources. |------|--------|---------| | [listener](#module\_listener) | ../nlb-listener | n/a | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.10.0 | +| [security\_group](#module\_security\_group) | tedilabs/network/aws//modules/security-group | ~> 0.31.0 | ## Resources | Name | Type | |------|------| | [aws_lb.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb) | resource | +| [terraform_data.replace_trigger](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/resources/data) | resource | | [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | | [aws_subnet.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source | @@ -40,20 +43,23 @@ This module creates following resources. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [name](#input\_name) | (Required) The name of the load balancer. This name must be unique within your AWS account, can have a maximum of 32 characters, must contain only alphanumeric characters or hyphens, and must not begin or end with a hyphen. | `string` | n/a | yes | -| [access\_log\_enabled](#input\_access\_log\_enabled) | (Optional) Indicates whether to enable access logs. Defaults to `false`, even when bucket is specified. | `bool` | `false` | no | -| [access\_log\_s3\_bucket](#input\_access\_log\_s3\_bucket) | (Optional) The name of the S3 bucket used to store the access logs. | `string` | `null` | no | -| [access\_log\_s3\_key\_prefix](#input\_access\_log\_s3\_key\_prefix) | (Optional) The key prefix for the specified S3 bucket. | `string` | `""` | no | +| [access\_log](#input\_access\_log) | (Optional) A configuration for the access logs for the load balancer. Access logs deliver detailed logs of all requests made to your Elastic Load Balancer. `access_log` as defined below.
(Optional) `enabled` - Indicates whether to enable access logs. Defaults to `false`.
(Optional) `s3_bucket` - A configuration of the S3 Bucket for access logs. `s3_bucket` as defined below.
(Required) `name` - The name of the S3 bucket used to store the access logs.
(Optional) `key_prefix` - The key prefix for the specified S3 bucket. |
object({
enabled = optional(bool, false)
s3_bucket = optional(object({
name = optional(string)
key_prefix = optional(string, "")
}), {})
})
| `{}` | no | | [cross\_zone\_load\_balancing\_enabled](#input\_cross\_zone\_load\_balancing\_enabled) | (Optional) Cross-zone load balancing distributes traffic evenly across all targets in the Availability Zones enabled for the load balancer. Indicates whether to enable cross-zone load balancing. Defaults to `false`. Regional data transfer charges may apply when cross-zone load balancing is enabled. | `bool` | `false` | no | +| [default\_security\_group](#input\_default\_security\_group) | (Optional) The configuration of the default security group for the load balancer. `default_security_group` block as defined below.
(Optional) `enabled` - Whether to use the default security group. Defaults to `true`.
(Optional) `name` - The name of the default security group. If not provided, the load balancer name is used for the name of security group.
(Optional) `description` - The description of the default security group.
(Optional) `ingress_rules` - A list of ingress rules in a security group. Defaults to `[]`. Each block of `ingress_rules` as defined below.
(Required) `id` - The ID of the ingress rule. This value is only used internally within Terraform code.
(Optional) `description` - The description of the rule.
(Required) `protocol` - The protocol to match. Note that if `protocol` is set to `-1`, it translates to all protocols, all port ranges, and `from_port` and `to_port` values should not be defined.
(Required) `from_port` - The start of port range for the protocols.
(Required) `to_port` - The end of port range for the protocols.
(Optional) `ipv4_cidrs` - The IPv4 network ranges to allow, in CIDR notation.
(Optional) `ipv6_cidrs` - The IPv6 network ranges to allow, in CIDR notation.
(Optional) `prefix_lists` - The prefix list IDs to allow.
(Optional) `security_groups` - The source security group IDs to allow.
(Optional) `self` - Whether the security group itself will be added as a source to this ingress rule.
(Optional) `egress_rules` - A list of egress rules in a security group. Defaults to `[{ id = "default", protocol = -1, from_port = 1, to_port=65535, ipv4_cidrs = ["0.0.0.0/0"] }]`. Each block of `egress_rules` as defined below.
(Required) `id` - The ID of the egress rule. This value is only used internally within Terraform code.
(Optional) `description` - The description of the rule.
(Required) `protocol` - The protocol to match. Note that if `protocol` is set to `-1`, it translates to all protocols, all port ranges, and `from_port` and `to_port` values should not be defined.
(Required) `from_port` - The start of port range for the protocols.
(Required) `to_port` - The end of port range for the protocols.
(Optional) `ipv4_cidrs` - The IPv4 network ranges to allow, in CIDR notation.
(Optional) `ipv6_cidrs` - The IPv6 network ranges to allow, in CIDR notation.
(Optional) `prefix_lists` - The prefix list IDs to allow.
(Optional) `security_groups` - The source security group IDs to allow.
(Optional) `self` - Whether the security group itself will be added as a source to this ingress rule.
(Optional) `listener_ingress_ipv4_cidrs` - A list of IPv4 CIDR ranges to allow on the listener port. Defaults to `[]`."
(Optional) `listener_ingress_ipv6_cidrs` - A list of IPv6 CIDR ranges to allow on the listener port. Defaults to `[]`."
(Optional) `listener_ingress_prefix_lists` - A list of prefix list IDs for AWS services to allow on the listener port. Defaults to `[]`."
(Optional) `listener_ingress_security_groups` - A list of security group IDs to allow on the listener port. Defaults to `[]`." |
object({
enabled = optional(bool, true)
name = optional(string)
description = optional(string, "Managed by Terraform.")
ingress_rules = optional(
list(object({
id = string
description = optional(string, "Managed by Terraform.")
protocol = string
from_port = number
to_port = number
ipv4_cidrs = optional(list(string), [])
ipv6_cidrs = optional(list(string), [])
prefix_lists = optional(list(string), [])
security_groups = optional(list(string), [])
self = optional(bool, false)
})),
[]
)
egress_rules = optional(
list(object({
id = string
description = optional(string, "Managed by Terraform.")
protocol = string
from_port = number
to_port = number
ipv4_cidrs = optional(list(string), [])
ipv6_cidrs = optional(list(string), [])
prefix_lists = optional(list(string), [])
security_groups = optional(list(string), [])
self = optional(bool, false)
})),
[{
id = "default"
description = "Allow all outbound traffic."
protocol = "-1"
from_port = 1
to_port = 65535
ipv4_cidrs = ["0.0.0.0/0"]
}]
)
listener_ingress_ipv4_cidrs = optional(list(string), [])
listener_ingress_ipv6_cidrs = optional(list(string), [])
listener_ingress_prefix_lists = optional(list(string), [])
listener_ingress_security_groups = optional(list(string), [])
})
| `{}` | no | | [deletion\_protection\_enabled](#input\_deletion\_protection\_enabled) | (Optional) Indicates whether deletion of the load balancer via the AWS API will be protected. Defaults to `false`. | `bool` | `false` | no | | [ip\_address\_type](#input\_ip\_address\_type) | (Optional) The type of IP addresses used by the subnets for your load balancer. The possible values are `IPV4` and `DUALSTACK`. | `string` | `"IPV4"` | no | | [is\_public](#input\_is\_public) | (Optional) Indicates whether the load balancer will be public. Defaults to `false`. | `bool` | `false` | no | -| [listeners](#input\_listeners) | (Optional) A list of listener configurations of the network load balancer. Listeners listen for connection requests using their `protocol` and `port`. Each value of `listener` block as defined below.
(Required) `port` - The number of port on which the listener of load balancer is listening.
(Required) `protocol` - The protocol for connections from clients to the load balancer. Valid values are `TCP`, `TLS`, `UDP` and `TCP_UDP`. Not valid to use `UDP` or `TCP_UDP` if dual-stack mode is enabled on the load balancer.
(Required) `target_group` - The ARN of the target group to which to route traffic.
(Optional) `tls_certificate` - The ARN of the default SSL server certificate. For adding additional SSL certificates, see the `tls_additional_certificates` variable. Required if `protocol` is `TLS`.
(Optional) `tls_additional_certificates` - A set of ARNs of the certificate to attach to the listener. This is for additional certificates and does not replace the default certificate on the listener.
(Optional) `tls_security_policy` - The name of security policy for a Secure Socket Layer (SSL) negotiation configuration. This is used to negotiate SSL connections with clients. Required if protocol is `TLS`. Recommend using the `ELBSecurityPolicy-TLS13-1-2-2021-06` security policy. This security policy includes TLS 1.3, which is optimized for security and performance, and is backward compatible with TLS 1.2.
(Optional) `tls_alpn_policy` - The policy of the Application-Layer Protocol Negotiation (ALPN) to select. ALPN is a TLS extension that includes the protocol negotiation within the exchange of hello messages. Can be set if `protocol` is `TLS`. Valid values are `HTTP1Only`, `HTTP2Only`, `HTTP2Optional`, `HTTP2Preferred`, and `None`. Defaults to `None`. | `any` | `[]` | no | +| [listeners](#input\_listeners) | (Optional) A list of listener configurations of the network load balancer. Listeners listen for connection requests using their `protocol` and `port`. Each value of `listener` block as defined below.
(Required) `port` - The number of port on which the listener of load balancer is listening.
(Required) `protocol` - The protocol for connections from clients to the load balancer. Valid values are `TCP`, `TLS`, `UDP` and `TCP_UDP`. Not valid to use `UDP` or `TCP_UDP` if dual-stack mode is enabled on the load balancer.
(Required) `target_group` - The ARN of the target group to which to route traffic.
(Optional) `tls` - The configuration for TLS listener of the load balancer. Required if `protocol` is `TLS`. `tls` block as defined below.
(Optional) `certificate` - The ARN of the default SSL server certificate. For adding additional SSL certificates, see the `additional_certificates` variable.
(Optional) `additional_certificates` - A set of ARNs of the certificate to attach to the listener. This is for additional certificates and does not replace the default certificate on the listener.
(Optional) `security_policy` - The name of security policy for a Secure Socket Layer (SSL) negotiation configuration. This is used to negotiate SSL connections with clients. Required if protocol is `TLS`. Recommend using the `ELBSecurityPolicy-TLS13-1-2-2021-06` security policy. This security policy includes TLS 1.3, which is optimized for security and performance, and is backward compatible with TLS 1.2.
(Optional) `alpn_policy` - The policy of the Application-Layer Protocol Negotiation (ALPN) to select. ALPN is a TLS extension that includes the protocol negotiation within the exchange of hello messages. Can be set if `protocol` is `TLS`. Valid values are `HTTP1Only`, `HTTP2Only`, `HTTP2Optional`, `HTTP2Preferred`, and `None`. Defaults to `None`. |
list(object({
port = number
protocol = string
target_group = string
tls = optional(object({
certificate = optional(string)
additional_certificates = optional(set(string), [])
security_policy = optional(string, "ELBSecurityPolicy-TLS13-1-2-2021-06")
alpn_policy = optional(string, "None")
}), {})
}))
| `[]` | no | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | -| [network\_mapping](#input\_network\_mapping) | (Optional) The configuration for the load balancer how routes traffic to targets in which subnets, and in accordance with IP address settings. Select at least one Availability Zone and one subnet for each zone. We recommend selecting at least two Availability Zones. The load balancer will route traffic only to targets in the selected Availability Zones. Zones that are not supported by the load balancer or VPC cannot be selected. Subnets can be added, but not removed, once a load balancer is created. Each key of `network_mapping` is the availability zone id like `apne2-az1`, `use1-az1`. Each value of `network_mapping` block as defined below.
(Required) `subnet_id` - The id of the subnet of which to attach to the load balancer. You can specify only one subnet per Availability Zone.
(Optional) `private_ipv4_address` - A private ipv4 address within the subnet to assign to the internal load balancer.
(Optional) `ipv6_address` - An ipv6 address within the subnet to assign to the internet-facing load balancer.
(Optional) `allocation_id` - The allocation ID of the Elastic IP address. | `map(map(string))` | `{}` | no | +| [network\_mapping](#input\_network\_mapping) | (Optional) The configuration for the load balancer how routes traffic to targets in which subnets, and in accordance with IP address settings. Select at least one Availability Zone and one subnet for each zone. We recommend selecting at least two Availability Zones. The load balancer will route traffic only to targets in the selected Availability Zones. Zones that are not supported by the load balancer or VPC cannot be selected. Subnets can be added, but not removed, once a load balancer is created. Each key of `network_mapping` is the availability zone id like `apne2-az1`, `use1-az1`. Each value of `network_mapping` block as defined below.
(Required) `subnet` - The id of the subnet of which to attach to the load balancer. You can specify only one subnet per Availability Zone.
(Optional) `private_ipv4_address` - A private ipv4 address within the subnet to assign to the internal load balancer.
(Optional) `ipv6_address` - An ipv6 address within the subnet to assign to the internet-facing load balancer.
(Optional) `elastic_ip` - The allocation ID of the Elastic IP address. |
map(object({
subnet = string
private_ipv4_address = optional(string)
ipv6_address = optional(string)
elastic_ip = optional(string)
}))
| `{}` | no | | [resource\_group\_description](#input\_resource\_group\_description) | (Optional) The description of Resource Group. | `string` | `"Managed by Terraform."` | no | | [resource\_group\_enabled](#input\_resource\_group\_enabled) | (Optional) Whether to create Resource Group to find and group AWS resources which are created by this module. | `bool` | `true` | no | | [resource\_group\_name](#input\_resource\_group\_name) | (Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. | `string` | `""` | no | +| [route53\_resolver\_availability\_zone\_affinity](#input\_route53\_resolver\_availability\_zone\_affinity) | (Optional) A configuration to determine how traffic is distributed among the load balancer Availability Zones. Only applied to internal requests for clients resolving the load balancer DNS name using Route 53 Resolver. Valid values are `ANY`, `PARTIAL`, `ALL`. Defaults to `ANY`.
`ANY` - Client DNS queries will resolve to healthy load balancer IP addresses across all load balancer Availability Zones.
`PARTIAL` - 85% of client DNS queries will favor load balancer IP addresses in their own Availability Zone. The remaining queries will resolve to any zone. Resolving to any zone may also occur if there are no healthy load balancer IP addresses in the client's zone.
`ALL` - Client DNS queries will favor load balancer IP addresses in their own Availability Zone. Queries may resolve to other zones if there are no healthy load balancer IP addresses in their own zone.
balancer Availability Zones. | `string` | `"ANY"` | no | +| [security\_group\_evaluation\_on\_privatelink\_enabled](#input\_security\_group\_evaluation\_on\_privatelink\_enabled) | (Optional) Whether to evaluate inbound security group rules for traffic sent to a Network Load Balancer through AWS PrivateLink. Defaults to `false`. | `bool` | `false` | no | +| [security\_groups](#input\_security\_groups) | (Optional) A list of security group IDs to assign to the Load Balancer. Security groups for Network Load Balancer cannot be added if none are currently present, and cannot all be removed once added. If either of these conditions are met, this will force a recreation of the resource. | `list(string)` | `[]` | no | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | +| [timeouts](#input\_timeouts) | (Optional) How long to wait for the load balancer to be created/updated/deleted. |
object({
create = optional(string, "10m")
update = optional(string, "10m")
delete = optional(string, "10m")
})
| `{}` | no | ## Outputs @@ -64,7 +70,7 @@ This module creates following resources. | [arn\_suffix](#output\_arn\_suffix) | The ARN suffix for use with CloudWatch Metrics. | | [attributes](#output\_attributes) | Load Balancer Attributes that applied to the network load balancer. | | [availability\_zone\_ids](#output\_availability\_zone\_ids) | A list of the Availability Zone IDs which are used by the load balancer. | -| [available\_availability\_zone\_ids](#output\_available\_availability\_zone\_ids) | A list of the Availability Zone IDs available to the current account and region. | +| [default\_security\_group](#output\_default\_security\_group) | The default security group ID of the load balancer. | | [domain](#output\_domain) | The DNS name of the load balancer. | | [id](#output\_id) | The ID of the load balancer. | | [ip\_address\_type](#output\_ip\_address\_type) | The type of IP addresses used by the subnets for your load balancer. | @@ -72,6 +78,8 @@ This module creates following resources. | [listeners](#output\_listeners) | The listeners of the network load balancer. | | [name](#output\_name) | The name of the load balancer. | | [network\_mapping](#output\_network\_mapping) | The configuration for the load balancer how routes traffic to targets in which subnets and IP address settings. | +| [security\_group\_evaluation\_on\_privatelink\_enabled](#output\_security\_group\_evaluation\_on\_privatelink\_enabled) | Whether to evaluate inbound security group rules for traffic sent to a Network Load Balancer through AWS PrivateLink. | +| [security\_groups](#output\_security\_groups) | A set of security group IDs which is assigned to the load balancer. | | [subnets](#output\_subnets) | A list of subnet IDs attached to the load balancer. | | [type](#output\_type) | The type of the load balancer. Always return `NETWORK`. | | [vpc\_id](#output\_vpc\_id) | The VPC ID of the load balancer. | diff --git a/modules/nlb/main.tf b/modules/nlb/main.tf index 16bc373..dc1481a 100644 --- a/modules/nlb/main.tf +++ b/modules/nlb/main.tf @@ -22,7 +22,11 @@ data "aws_availability_zones" "available" { data "aws_subnet" "this" { for_each = var.network_mapping - id = each.value.subnet_id + id = each.value.subnet +} + +resource "terraform_data" "replace_trigger" { + input = length(local.security_groups) > 0 } locals { @@ -31,30 +35,50 @@ locals { available_availability_zone_ids = data.aws_availability_zones.available.zone_ids network_mapping = { for zone_id in local.available_availability_zone_ids : - zone_id => try(merge({ - cidr_block = data.aws_subnet.this[zone_id].cidr_block != "" ? data.aws_subnet.this[zone_id].cidr_block : null - ipv6_cidr_block = data.aws_subnet.this[zone_id].ipv6_cidr_block != "" ? data.aws_subnet.this[zone_id].ipv6_cidr_block : null - private_ipv4_address = null - ipv6_address = null - allocation_id = null - }, var.network_mapping[zone_id]), null) + zone_id => (contains(keys(var.network_mapping), zone_id) + ? { + subnet = data.aws_subnet.this[zone_id].id + + ipv4_cidr = data.aws_subnet.this[zone_id].cidr_block != "" ? data.aws_subnet.this[zone_id].cidr_block : null + ipv6_cidr = data.aws_subnet.this[zone_id].ipv6_cidr_block != "" ? data.aws_subnet.this[zone_id].ipv6_cidr_block : null + private_ipv4_address = var.network_mapping[zone_id].private_ipv4_address + ipv6_address = var.network_mapping[zone_id].ipv6_address + elastic_ip = var.network_mapping[zone_id].elastic_ip + } + : null + ) } enabled_network_mapping = { for zone_id, config in local.network_mapping : zone_id => config - if try(config.subnet_id, null) != null + if config != null } availability_zone_ids = keys(local.enabled_network_mapping) + + route53_resolver_availability_zone_affinity = { + "ANY" = "any_availability_zone" + "PARTIAL" = "partial_availability_zone_affinity" + "ALL" = "availability_zone_affinity" + } } + +################################################### +# Network Load Balancer +################################################### + # INFO: Not supported attributes # - `customer_owned_ipv4_pool` # - `desync_mitigation_mode` # - `drop_invalid_header_fields` # - `enable_http2` +# - `enable_tls_version_and_cipher_suite_headers` # - `enable_waf_fail_open` +# - `enable_xff_client_port` # - `idle_timeout` -# - `security_groups` +# - `preserve_host_header` +# - `subnets` +# - `xff_header_processing_mode` resource "aws_lb" "this" { name = var.name @@ -66,28 +90,48 @@ resource "aws_lb" "this" { for_each = local.enabled_network_mapping content { - subnet_id = subnet_mapping.value.subnet_id + subnet_id = subnet_mapping.value.subnet - private_ipv4_address = try(subnet_mapping.value.private_ipv4_address, null) - ipv6_address = try(subnet_mapping.value.ipv6_address, null) - allocation_id = try(subnet_mapping.value.allocation_id, null) + private_ipv4_address = subnet_mapping.value.private_ipv4_address + ipv6_address = subnet_mapping.value.ipv6_address + allocation_id = subnet_mapping.value.elastic_ip } } + + ## Access Control + enforce_security_group_inbound_rules_on_private_link_traffic = (length(local.security_groups) > 0 + ? (var.security_group_evaluation_on_privatelink_enabled ? "on" : "off") + : null + ) + security_groups = local.security_groups + + + ## Logging dynamic "access_logs" { - for_each = var.access_log_enabled ? ["go"] : [] + for_each = var.access_log.enabled ? [var.access_log] : [] + iterator = log content { - enabled = var.access_log_enabled - bucket = var.access_log_s3_bucket - prefix = var.access_log_s3_key_prefix + enabled = log.value.enabled + bucket = log.value.s3_bucket.name + prefix = log.value.s3_bucket.key_prefix } } + ## Attributes + dns_record_client_routing_policy = local.route53_resolver_availability_zone_affinity[var.route53_resolver_availability_zone_affinity] enable_cross_zone_load_balancing = var.cross_zone_load_balancing_enabled enable_deletion_protection = var.deletion_protection_enabled + + timeouts { + create = var.timeouts.create + update = var.timeouts.update + delete = var.timeouts.delete + } + tags = merge( { "Name" = local.metadata.name @@ -95,6 +139,12 @@ resource "aws_lb" "this" { local.module_tags, var.tags, ) + + lifecycle { + replace_triggered_by = [ + terraform_data.replace_trigger, + ] + } } @@ -117,10 +167,12 @@ module "listener" { target_group = each.value.target_group ## TLS - tls_certificate = try(each.value.tls_certificate, null) - tls_additional_certificates = try(each.value.tls_additional_certificates, []) - tls_security_policy = try(each.value.tls_security_policy, "ELBSecurityPolicy-TLS13-1-2-2021-06") - tls_alpn_policy = try(each.value.tls_alpn_policy, "None") + tls = { + certificate = each.value.tls.certificate + additional_certificates = each.value.tls.additional_certificates + security_policy = each.value.tls.security_policy + alpn_policy = each.value.tls.alpn_policy + } resource_group_enabled = false module_tags_enabled = false diff --git a/modules/nlb/outputs.tf b/modules/nlb/outputs.tf index 95cd100..14d1b0e 100644 --- a/modules/nlb/outputs.tf +++ b/modules/nlb/outputs.tf @@ -48,11 +48,6 @@ output "availability_zone_ids" { value = local.availability_zone_ids } -output "available_availability_zone_ids" { - description = "A list of the Availability Zone IDs available to the current account and region." - value = local.available_availability_zone_ids -} - output "vpc_id" { description = "The VPC ID of the load balancer." value = aws_lb.this.vpc_id @@ -68,20 +63,32 @@ output "network_mapping" { value = local.network_mapping } +output "security_group_evaluation_on_privatelink_enabled" { + description = "Whether to evaluate inbound security group rules for traffic sent to a Network Load Balancer through AWS PrivateLink." + value = aws_lb.this.enforce_security_group_inbound_rules_on_private_link_traffic == "on" +} + +output "default_security_group" { + description = "The default security group ID of the load balancer." + value = one(module.security_group[*].id) +} + +output "security_groups" { + description = "A set of security group IDs which is assigned to the load balancer." + value = aws_lb.this.security_groups +} + output "access_log" { description = "The configuration for access logs of the load balancer." - value = { - enabled = var.access_log_enabled - s3_bucket = var.access_log_s3_bucket - s3_key_prefix = var.access_log_s3_key_prefix - } + value = var.access_log } output "attributes" { description = "Load Balancer Attributes that applied to the network load balancer." value = { - cross_zone_load_balancing_enabled = aws_lb.this.enable_cross_zone_load_balancing - deletion_protection_enabled = aws_lb.this.enable_deletion_protection + cross_zone_load_balancing_enabled = aws_lb.this.enable_cross_zone_load_balancing + deletion_protection_enabled = aws_lb.this.enable_deletion_protection + route53_resolver_availability_zone_affinity = var.route53_resolver_availability_zone_affinity } } diff --git a/modules/nlb/security-groups.tf b/modules/nlb/security-groups.tf new file mode 100644 index 0000000..95d8333 --- /dev/null +++ b/modules/nlb/security-groups.tf @@ -0,0 +1,64 @@ +locals { + security_groups = concat( + (var.default_security_group.enabled + ? module.security_group[*].id + : [] + ), + var.security_groups, + ) +} + + +################################################### +# Security Group for Network Load Balancer +################################################### + +module "security_group" { + source = "tedilabs/network/aws//modules/security-group" + version = "~> 0.31.0" + + count = var.default_security_group.enabled ? 1 : 0 + + name = coalesce(var.default_security_group.name, local.metadata.name) + description = var.default_security_group.description + vpc_id = values(data.aws_subnet.this)[0].vpc_id + + ingress_rules = concat( + var.default_security_group.ingress_rules, + [ + for listener in var.listeners : { + id = "listener-${listener.port}" + description = "Default rule for the load balancer listener." + protocol = listener.protocol + from_port = listener.port + to_port = listener.port + + ipv4_cidrs = var.default_security_group.listener_ingress_ipv4_cidrs + ipv6_cidrs = var.default_security_group.listener_ingress_ipv6_cidrs + prefix_lists = var.default_security_group.listener_ingress_prefix_lists + security_groups = var.default_security_group.listener_ingress_security_groups + } + if anytrue([ + length(var.default_security_group.listener_ingress_ipv4_cidrs) > 0, + length(var.default_security_group.listener_ingress_ipv6_cidrs) > 0, + length(var.default_security_group.listener_ingress_prefix_lists) > 0, + length(var.default_security_group.listener_ingress_security_groups) > 0, + ]) + ] + ) + egress_rules = concat( + var.default_security_group.egress_rules, + # TODO: Limit egress rules + # TODO: Target Ports + # TODO: Target Health Check Ports + ) + + revoke_rules_on_delete = true + resource_group_enabled = false + module_tags_enabled = false + + tags = merge( + local.module_tags, + var.tags, + ) +} diff --git a/modules/nlb/variables.tf b/modules/nlb/variables.tf index 3253948..f5fa62b 100644 --- a/modules/nlb/variables.tf +++ b/modules/nlb/variables.tf @@ -31,34 +31,153 @@ variable "ip_address_type" { variable "network_mapping" { description = <