variables.tf
  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."
  }
}