1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
|
# Variables ====================================================================
variable "name_prefix" {
description = "Prefix prepended to resource names created by this module."
type = string
validation {
condition = length(trimspace(var.name_prefix)) > 0
error_message = "name_prefix must not be empty."
}
}
variable "name_suffix" {
description = "Suffix appended to the load balancer resource name to distinguish it within name_prefix."
type = string
validation {
condition = length(trimspace(var.name_suffix)) > 0
error_message = "name_suffix must not be empty."
}
}
variable "tags" {
description = "Tags applied to all resources in this module."
type = map(string)
default = {}
}
variable "load_balancer_type" {
description = "Load balancer type — application or network."
type = string
default = "application"
validation {
condition = contains(["application", "network"], var.load_balancer_type)
error_message = "load_balancer_type must be application or network."
}
}
variable "internal" {
description = "Create an internal (non-internet-facing) load balancer."
type = bool
default = false
}
variable "subnet_ids" {
description = "Subnet IDs for the load balancer. Must span at least two AZs."
type = list(string)
validation {
condition = length(var.subnet_ids) >= 2
error_message = "subnet_ids must include at least two subnets across different AZs."
}
}
variable "security_group_ids" {
description = "Security group IDs to attach to the load balancer's ENIs. Supported on ALB always, and on NLB in regions/accounts with the 2023 NLB security groups feature enabled."
type = list(string)
default = []
}
variable "cross_zone_load_balancing" {
description = "Enable cross-zone load balancing. Valid for NLB only — ALB always has this enabled and it is not configurable."
type = bool
default = true
}
variable "access_logs_bucket" {
description = "S3 bucket for access logs (optional)."
type = string
default = null
}
variable "access_logs_prefix" {
description = "S3 prefix for access logs."
type = string
default = "lb"
}
variable "deletion_protection" {
description = "Enable deletion protection."
type = bool
default = true
}
variable "idle_timeout" {
description = "Idle timeout in seconds. Valid for ALB only — NLB connections do not have a configurable idle timeout."
type = number
default = 60
}
variable "waf_web_acl_arn" {
description = "WAF Web ACL ARN to associate with the load balancer. Valid for ALB only — WAFv2 does not support NLB as an association target."
type = string
default = null
}
variable "target_groups" {
description = <<-EOT
Map of target group definitions.
- name_suffix is combined with name_prefix and region to build the target group name.
- protocol must be TCP/UDP/TCP_UDP for NLB target groups, HTTP/HTTPS for ALB.
- stickiness.type must be source_ip for NLB, lb_cookie or app_cookie for ALB.
- targets is a map of instance IDs (target_type = "instance") or IP addresses
(target_type = "ip") to attach. port overrides the target group's port for
that specific target; leave null to use the target group's port.
EOT
type = map(object({
name_suffix = string
port = number
protocol = string
target_type = optional(string, "instance")
vpc_id = string
deregistration_delay = optional(number, 30)
tags = optional(map(string), {})
targets = optional(map(object({
id = string
port = optional(number, null)
})), {})
health_check = optional(object({
enabled = optional(bool, true)
path = optional(string, "/health")
protocol = optional(string, "HTTP")
matcher = optional(string, "200")
interval = optional(number, 30)
timeout = optional(number, 5)
healthy_threshold = optional(number, 2)
unhealthy_threshold = optional(number, 2)
}), {})
stickiness = optional(object({
enabled = optional(bool, false)
cookie_duration = optional(number, 86400)
type = optional(string, "lb_cookie")
}), null)
}))
default = {}
validation {
condition = alltrue([
for k, v in var.target_groups : v.deregistration_delay >= 0 && v.deregistration_delay <= 3600
])
error_message = "deregistration_delay must be between 0 and 3600 seconds."
}
validation {
condition = alltrue([
for k, v in var.target_groups : length(trimspace(v.name_suffix)) > 0
])
error_message = "Every target group must set a non-empty name_suffix."
}
validation {
condition = alltrue([
for k, v in var.target_groups :
contains(["HTTP", "HTTPS", "TCP", "UDP", "TCP_UDP", "TLS", "GENEVE"], v.protocol)
])
error_message = "Target group protocol must be one of HTTP, HTTPS, TCP, UDP, TCP_UDP, TLS, GENEVE."
}
validation {
condition = alltrue([
for k, v in var.target_groups :
v.stickiness == null || contains(["lb_cookie", "app_cookie", "source_ip"], v.stickiness.type)
])
error_message = "stickiness.type must be lb_cookie, app_cookie, or source_ip."
}
}
variable "listeners" {
description = <<-EOT
Map of load balancer listener definitions.
- redirect actions are only valid on ALB listeners — NLB does not support the redirect action type.
- certificate_arn is only meaningful for HTTPS/TLS protocol listeners.
EOT
type = map(object({
port = number
protocol = string
certificate_arn = optional(string, null)
ssl_policy = optional(string, "ELBSecurityPolicy-TLS13-1-2-2021-06")
default_action_type = optional(string, "forward")
target_group_key = optional(string, null)
redirect = optional(object({
port = optional(string, "443")
protocol = optional(string, "HTTPS")
status_code = optional(string, "HTTP_301")
}), null)
}))
default = {}
validation {
condition = alltrue([
for k, v in var.listeners :
contains(["HTTP", "HTTPS", "TCP", "UDP", "TCP_UDP", "TLS"], v.protocol)
])
error_message = "Listener protocol must be one of HTTP, HTTPS, TCP, UDP, TCP_UDP, TLS."
}
validation {
condition = alltrue([
for k, v in var.listeners :
!(v.protocol == "HTTPS" || v.protocol == "TLS") || v.certificate_arn != null
])
error_message = "Listeners using HTTPS or TLS protocol must set certificate_arn."
}
validation {
condition = alltrue([
for k, v in var.listeners :
v.redirect != null || v.target_group_key != null
])
error_message = "Every listener must set either target_group_key or redirect."
}
}
|