From 0460e99565a65b1d51c66e998f6b4eb750211234 Mon Sep 17 00:00:00 2001 From: Shikha Maheshwari Date: Fri, 19 Apr 2024 17:23:25 +0530 Subject: [PATCH] feat: functionality added to create Network Load Balancer(NLB). When the profile value in the `load_balancers` input is set as `network-fixed`, it signifies to setup an NLB. An existing subnet can be provided to provision NLB otherwise NLB uses the first subnet from the subnets list. Subsequently, a listener, a pool, and pool members are connected to the NLB. (#654) --- README.md | 5 ++-- examples/complete/README.md | 2 +- examples/complete/main.tf | 14 ++++++++++ load_balancer.tf | 53 +++++++++++++++++++++++++---------- moved_config.tf | 9 ++++++ variables.tf | 56 +++++++++++++++++++++++++++---------- 6 files changed, 108 insertions(+), 31 deletions(-) create mode 100644 moved_config.tf diff --git a/README.md b/README.md index 9c42ac32..42c0ce6c 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,8 @@ No modules. | [ibm_is_lb.lb](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_lb) | resource | | [ibm_is_lb_listener.listener](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_lb_listener) | resource | | [ibm_is_lb_pool.pool](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_lb_pool) | resource | -| [ibm_is_lb_pool_member.pool_members](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_lb_pool_member) | resource | +| [ibm_is_lb_pool_member.alb_pool_members](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_lb_pool_member) | resource | +| [ibm_is_lb_pool_member.nlb_pool_members](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_lb_pool_member) | resource | | [ibm_is_security_group.security_group](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_security_group) | resource | | [ibm_is_security_group_rule.security_group_rules](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_security_group_rule) | resource | | [ibm_is_volume.volume](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_volume) | resource | @@ -159,7 +160,7 @@ No modules. | [existing\_kms\_instance\_guid](#input\_existing\_kms\_instance\_guid) | The GUID of the Hyper Protect Crypto Services instance in which the key specified in var.boot\_volume\_encryption\_key is coming from. | `string` | `null` | no | | [image\_id](#input\_image\_id) | Image ID used for VSI. Run 'ibmcloud is images' to find available images in a region | `string` | n/a | yes | | [kms\_encryption\_enabled](#input\_kms\_encryption\_enabled) | Set this to true to control the encryption keys used to encrypt the data that for the block storage volumes for VPC. If set to false, the data is encrypted by using randomly generated keys. For more info on encrypting block storage volumes, see https://cloud.ibm.com/docs/vpc?topic=vpc-creating-instances-byok | `bool` | `false` | no | -| [load\_balancers](#input\_load\_balancers) | Load balancers to add to VSI |
list(
object({
name = string
type = string
listener_port = number
listener_protocol = string
connection_limit = number
idle_connection_timeout = optional(number)
algorithm = string
protocol = string
health_delay = number
health_retries = number
health_timeout = number
health_type = string
pool_member_port = string
profile = optional(string)
dns = optional(
object({
instance_crn = string
zone_id = string
})
)
security_group = optional(
object({
name = string
rules = list(
object({
name = string
direction = string
source = string
tcp = optional(
object({
port_max = number
port_min = number
})
)
udp = optional(
object({
port_max = number
port_min = number
})
)
icmp = optional(
object({
type = number
code = number
})
)
})
)
})
)
})
)
| `[]` | no | +| [load\_balancers](#input\_load\_balancers) | Load balancers to add to VSI |
list(
object({
name = string
type = string
listener_port = optional(number)
listener_port_max = optional(number)
listener_port_min = optional(number)
listener_protocol = string
connection_limit = optional(number)
idle_connection_timeout = optional(number)
algorithm = string
protocol = string
health_delay = number
health_retries = number
health_timeout = number
health_type = string
pool_member_port = string
profile = optional(string)
accept_proxy_protocol = optional(bool)
subnet_id_to_provision_nlb = optional(string) # Required for Network Load Balancer. If no value is provided, the first one from the VPC subnet list will be selected.
dns = optional(
object({
instance_crn = string
zone_id = string
})
)
security_group = optional(
object({
name = string
rules = list(
object({
name = string
direction = string
source = string
tcp = optional(
object({
port_max = number
port_min = number
})
)
udp = optional(
object({
port_max = number
port_min = number
})
)
icmp = optional(
object({
type = number
code = number
})
)
})
)
})
)
})
)
| `[]` | no | | [machine\_type](#input\_machine\_type) | VSI machine type. Run 'ibmcloud is instance-profiles' to get a list of regional profiles | `string` | n/a | yes | | [placement\_group\_id](#input\_placement\_group\_id) | Unique Identifier of the Placement Group for restricting the placement of the instance, default behaviour is placement on any host | `string` | `null` | no | | [prefix](#input\_prefix) | The IBM Cloud platform API key needed to deploy IAM enabled resources | `string` | n/a | yes | diff --git a/examples/complete/README.md b/examples/complete/README.md index 27e3fdb1..ebdec843 100644 --- a/examples/complete/README.md +++ b/examples/complete/README.md @@ -9,4 +9,4 @@ It will provision the following: - A VSI in each subnet placed in the placement group. - A floating IP for each virtual server created. - A secondary VSI with secondary subnets and secondary security group. -- A new Application Load Balancer to balance traffic between all virtual servers that are created by this example. +- A new Application Load Balancer and Network Load Balancer to balance traffic between all virtual servers that are created by this example. diff --git a/examples/complete/main.tf b/examples/complete/main.tf index 991dca5e..052b3cc9 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -203,6 +203,20 @@ module "slz_vsi" { health_timeout = 30 health_type = "http" pool_member_port = 8080 + }, + { + name = "${var.prefix}-nlb" + type = "public" + profile = "network-fixed" + listener_port = 3128 + listener_protocol = "tcp" + algorithm = "round_robin" + protocol = "tcp" + health_delay = 60 + health_retries = 5 + health_timeout = 30 + health_type = "tcp" + pool_member_port = 3120 } ] } diff --git a/load_balancer.tf b/load_balancer.tf index 4c733bd4..bc9a539f 100644 --- a/load_balancer.tf +++ b/load_balancer.tf @@ -7,12 +7,14 @@ locals { for load_balancer in var.load_balancers : (load_balancer.name) => load_balancer } + + subnets_id = var.subnets[*].id } resource "ibm_is_lb" "lb" { for_each = local.load_balancer_map name = "${var.prefix}-${each.value.name}-lb" - subnets = var.subnets[*].id + subnets = (each.value.profile == "network-fixed") ? (each.value.subnet_id_to_provision_nlb != null ? [each.value.subnet_id_to_provision_nlb] : [local.subnets_id[0]]) : local.subnets_id type = each.value.type #checkov:skip=CKV2_IBM_1:See https://github.com/bridgecrewio/checkov/issues/5824# profile = each.value.profile security_groups = each.value.security_group == null ? null : [ibm_is_security_group.security_group[each.value.security_group.name].id] @@ -53,7 +55,7 @@ resource "ibm_is_lb_pool" "pool" { ############################################################################## locals { - pool_members = flatten([ + alb_pool_members = flatten([ for load_balancer in var.load_balancers : [ for ipv4_address in [ @@ -64,22 +66,42 @@ locals { port = load_balancer.pool_member_port target_address = ipv4_address lb = load_balancer.name - } + profile = load_balancer.profile + } if(load_balancer.profile != "network-fixed") ] ]) -} -resource "ibm_is_lb_pool_member" "pool_members" { - count = length(local.pool_members) - port = local.pool_members[count.index].port - lb = ibm_is_lb.lb[local.pool_members[count.index].lb].id - pool = element(split("/", ibm_is_lb_pool.pool[local.pool_members[count.index].lb].id), 1) - target_address = local.pool_members[count.index].target_address + nlb_pool_members = flatten([ + for load_balancer in var.load_balancers : + [ + for server in ibm_is_instance.vsi : + { + port = load_balancer.pool_member_port + lb = load_balancer.name + target_id = server.id + profile = load_balancer.profile + } if(load_balancer.profile == "network-fixed") + ] + ]) } -############################################################################## +resource "ibm_is_lb_pool_member" "alb_pool_members" { + count = length(local.alb_pool_members) + port = local.alb_pool_members[count.index].port + lb = ibm_is_lb.lb[local.alb_pool_members[count.index].lb].id + pool = element(split("/", ibm_is_lb_pool.pool[local.alb_pool_members[count.index].lb].id), 1) + target_address = local.alb_pool_members[count.index].target_address +} +resource "ibm_is_lb_pool_member" "nlb_pool_members" { + count = length(local.nlb_pool_members) + port = local.nlb_pool_members[count.index].port + lb = ibm_is_lb.lb[local.nlb_pool_members[count.index].lb].id + pool = element(split("/", ibm_is_lb_pool.pool[local.nlb_pool_members[count.index].lb].id), 1) + target_id = local.nlb_pool_members[count.index].target_id +} +############################################################################## ############################################################################## # Load Balancer Listener @@ -90,10 +112,13 @@ resource "ibm_is_lb_listener" "listener" { lb = ibm_is_lb.lb[each.value.name].id default_pool = ibm_is_lb_pool.pool[each.value.name].id port = each.value.listener_port + port_min = (each.value.listener_port == null && each.value.profile == "network-fixed") ? each.value.listener_port_min : null + port_max = (each.value.listener_port == null && each.value.profile == "network-fixed") ? each.value.listener_port_max : null protocol = each.value.listener_protocol - connection_limit = each.value.connection_limit > 0 ? each.value.connection_limit : null - idle_connection_timeout = each.value.idle_connection_timeout - depends_on = [ibm_is_lb_pool_member.pool_members] + connection_limit = each.value.profile != "network-fixed" ? (each.value.connection_limit > 0 ? each.value.connection_limit : null) : null + idle_connection_timeout = each.value.profile != "network-fixed" ? each.value.idle_connection_timeout : null + accept_proxy_protocol = each.value.accept_proxy_protocol + depends_on = [ibm_is_lb_pool_member.alb_pool_members, ibm_is_lb_pool_member.nlb_pool_members] } ############################################################################## diff --git a/moved_config.tf b/moved_config.tf new file mode 100644 index 00000000..43321341 --- /dev/null +++ b/moved_config.tf @@ -0,0 +1,9 @@ +############################################################################################################################################ +# The following moved blocks allow consumers to upgrade the module from v3.2.4 or older without destroying the existing ALB pool members +# For more details, please refer - https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone-vsi/issues/649 +############################################################################################################################################ + +moved { + from = ibm_is_lb_pool_member.pool_members + to = ibm_is_lb_pool_member.alb_pool_members +} diff --git a/variables.tf b/variables.tf index 61e32ee2..4333aff0 100644 --- a/variables.tf +++ b/variables.tf @@ -233,20 +233,24 @@ variable "load_balancers" { description = "Load balancers to add to VSI" type = list( object({ - name = string - type = string - listener_port = number - listener_protocol = string - connection_limit = number - idle_connection_timeout = optional(number) - algorithm = string - protocol = string - health_delay = number - health_retries = number - health_timeout = number - health_type = string - pool_member_port = string - profile = optional(string) + name = string + type = string + listener_port = optional(number) + listener_port_max = optional(number) + listener_port_min = optional(number) + listener_protocol = string + connection_limit = optional(number) + idle_connection_timeout = optional(number) + algorithm = string + protocol = string + health_delay = number + health_retries = number + health_timeout = number + health_type = string + pool_member_port = string + profile = optional(string) + accept_proxy_protocol = optional(bool) + subnet_id_to_provision_nlb = optional(string) # Required for Network Load Balancer. If no value is provided, the first one from the VPC subnet list will be selected. dns = optional( object({ instance_crn = string @@ -356,6 +360,30 @@ variable "load_balancers" { error_message = "Each load balancer must have a unique name." condition = length(distinct(var.load_balancers[*].name)) == length(var.load_balancers[*].name) } + + validation { + error_message = "Application load balancer connection_limit can not be null." + condition = length( + flatten([ + for load_balancer in var.load_balancers : + load_balancer.profile != "network-fixed" ? + (load_balancer.connection_limit == null) ? [true] : [] + : [] + ]) + ) == 0 + } + + validation { + error_message = "Application load balancer listener_port can not be null." + condition = length( + flatten([ + for load_balancer in var.load_balancers : + load_balancer.profile != "network-fixed" ? + (load_balancer.listener_port == null) ? [true] : [] + : [] + ]) + ) == 0 + } } ##############################################################################