Skip to content

Commit

Permalink
refactor: http listeners (#25)
Browse files Browse the repository at this point in the history
* refactor: http listeners

* fix unit tests
  • Loading branch information
gareda authored Aug 4, 2024
1 parent 4a8ed50 commit 1638ef3
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 49 deletions.
15 changes: 6 additions & 9 deletions .config/frontend_ip_configuration_parameters.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,19 @@
"Support": []
},
{
"Name": "private_ip_address",
"Description": "The Private IP Address to use for the Application Gateway.",
"Name": "subnet_id",
"Description": "The ID of the Subnet in which the Application Gateway should be deployed.",
"Type": "string",
"Default": "null",
"Required": "no",
"Required": "yes",
"Support": []
},
{
"Name": "private_ip_address_allocation",
"Description": "The Allocation Method for the Private IP Address. Possible values are",
"Name": "private_ip_address",
"Description": "The Private IP Address to use for the Application Gateway.",
"Type": "string",
"Default": "null",
"Required": "no",
"Support": [
"Dynamic",
"Static"
]
"Support": []
}
]
2 changes: 1 addition & 1 deletion .config/http_listener_parameters.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
},
{
"Name": "host_name",
"Description": "The Hostname which should be used for this HTTP Listener. Setting this value changes Listener Type to Multi site",
"Description": "The Hostname which should be used for this HTTP Listener. Setting this value changes Listener Type to Multi site.",
"Type": "string",
"Default": "null",
"Required": "no",
Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.92.0
rev: v1.92.1
hooks:
- id: terraform_fmt
name: Format Terraform code
Expand Down Expand Up @@ -41,7 +41,7 @@ repos:
fail_fast: true

- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.92.0
rev: v1.92.1
hooks:
- id: terraform_validate
name: Validate Terraform code
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ DEPRECATIONS:

* **Parameter**: `waf_configuration` is deprecated in favor of `firewall_policy_id`. WAF configuration must now be performed using a firewall policy. [Retirement: Support for Application Gateway Web Application Firewall v2 Configuration is ending][waf-config-deprecate].
* **Parameter**: `sku` is deprecated in favor of `sku_name`. The `Standard_Small`, `Standard_Medium`, `Standard_Large` and `WAF_Medium` sku types are also deprecated. [Application Gateway V1 will be retired on 28 April 2026– Transition to Application Gateway V2][appgw-sku-deprecate].
* **Parameter**: `private_ip_address_allocation` is deprecated.

## 1.2.0 (January 27, 2022)

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ The following parameters are supported:
|enable\_http2|Enables HTTP/2 for the Application Gateway.|`bool`|`false`|no|
|firewall\_policy\_id|The ID of the Firewall Policy to associate with the Application Gateway.|`string`|`null`|no|
|capacity|The capacity (number of instances) of the Application Gateway. Possible values are between `1` and `125`.|`number`|`null`|no|
|autoscale\_configuration|A mapping with the autoscale configuration of the application gateway.|`object({})`|`null`|no|
|autoscale\_configuration|A mapping with the autoscale configuration of the Application Gateway.|`object({})`|`null`|no|
|identity\_id|The ID of the Managed Identity to associate with the Application Gateway.|`string`|`null`|no|
|subnet\_id|The ID of the Subnet which the Application Gateway should be connected to.|`string`|n/a|yes|
|frontend\_ip\_configuration|A mapping the front ip configuration.|`object({})`|n/a|yes|
|frontend\_ip\_configuration|A mapping with the frontend ip configuration of the Application Gateway.|`object({})`|n/a|yes|
|backend\_address\_pools|List of objects that represent the configuration of each backend address pool.|`list(object({}))`|n/a|yes|
|http\_listeners|List of objects that represent the configuration of each http listener.|`list(object({}))`|n/a|yes|
|backend\_http\_settings|List of objects that represent the configuration of each backend http settings.|`list(object({}))`|n/a|yes|
Expand All @@ -50,8 +50,8 @@ The `frontend_ip_configuration` supports the following:
| Name | Description | Type | Default | Required |
| ---- | ------------| :--: | :-----: | :------: |
|public\_ip\_address\_id|The ID of a Public IP Address which the Application Gateway should use.|`string`|`null`|no|
|subnet\_id|The ID of the Subnet in which the Application Gateway should be deployed.|`string`|`null`|yes|
|private\_ip\_address|The Private IP Address to use for the Application Gateway.|`string`|`null`|no|
|private\_ip\_address\_allocation|The Allocation Method for the Private IP Address. Possible values are `Dynamic` and `Static`.|`string`|`null`|no|

The `backend_address_pools` supports the following:

Expand All @@ -78,7 +78,7 @@ The `http_listeners` supports the following:
|frontend\_ip\_configuration|The frontend ip configuration to use for this HTTP Listener. Possible values are `Public` and `Private`.|`string`|n/a|yes|
|port|The port used for this HTTP Listener.|`number`|n/a|yes|
|protocol|The Protocol to use for this HTTP Listener. Possible values are `Http` and `Https`.|`string`|n/a|yes|
|host\_name|The Hostname which should be used for this HTTP Listener. Setting this value changes Listener Type to Multi site|`string`|`null`|no|
|host\_name|The Hostname which should be used for this HTTP Listener. Setting this value changes Listener Type to Multi site.|`string`|`null`|no|
|ssl\_certificate\_name|The name of the associated SSL Certificate which should be used for this HTTP Listener.|`string`|`null`|no|

The `probes` supports the following:
Expand Down
19 changes: 9 additions & 10 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ resource "azurerm_application_gateway" "main" {
content {
name = "FrontendPrivateIpConfiguration"
subnet_id = var.subnet_id
private_ip_address_allocation = var.frontend_ip_configuration.private_ip_address_allocation
private_ip_address_allocation = var.subnet_id != null ? "Static" : null
private_ip_address = var.frontend_ip_configuration.private_ip_address
}
}
Expand All @@ -62,14 +62,13 @@ resource "azurerm_application_gateway" "main" {
}
}

frontend_port {
name = "80"
port = 80
}
dynamic "frontend_port" {
for_each = [for port in distinct(var.http_listeners[*].port) : port]

frontend_port {
name = "443"
port = 443
content {
name = tostring(frontend_port.value)
port = frontend_port.value
}
}

# dynamic "ssl_certificate" {
Expand All @@ -88,11 +87,11 @@ resource "azurerm_application_gateway" "main" {

content {
name = http_listener.value.name
frontend_ip_configuration_name = "FrontendPublicIpConfiguration"
frontend_ip_configuration_name = http_listener.value.frontend_ip_configuration == "Private" ? "FrontendPrivateIpConfiguration" : "FrontendPublicIpConfiguration"
frontend_port_name = http_listener.value.port
protocol = http_listener.value.protocol
host_name = http_listener.value.host_name
ssl_certificate_name = http_listener.value.ssl_certificate_name
# ssl_certificate_name = http_listener.value.ssl_certificate_name
}
}

Expand Down
2 changes: 1 addition & 1 deletion output.tf → outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ output "ssl_certificates" {
}

output "http_listeners" {
value = azurerm_application_gateway.main.http_listener
value = { for listener in azurerm_application_gateway.main.http_listener : listener.name => listener }
description = "Blocks containing configuration of each http listener."
}

Expand Down
94 changes: 79 additions & 15 deletions tests/testing.tftest.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,17 @@ variables {
frontend_ip_configuration = "Public"
port = 80
protocol = "Http"
},
# {
# name = "http-listener-2"
# frontend_ip_configuration = "Public"
# port = 80
# protocol = "Http"
# }
}, {
name = "http-listener-2"
frontend_ip_configuration = "Private"
port = 8080
protocol = "Http"
}, {
name = "http-listener-3"
frontend_ip_configuration = "Public"
port = 1433
protocol = "Http"
}
]
backend_http_settings = [{ name = "backend-http-setting", port = 80, protocol = "Http", request_timeout = 20 }]
request_routing_rules = [{
Expand Down Expand Up @@ -68,10 +72,9 @@ run "plan" {
identity_id = run.setup.managed_identity_id
subnet_id = run.setup.subnet_id
frontend_ip_configuration = {
subnet_id = run.setup.subnet_id
public_ip_address_id = run.setup.public_ip_id
private_ip_address_allocation = "Static"
private_ip_address = cidrhost(run.setup.subnet_address_prefix, 10)
public_ip_address_id = run.setup.public_ip_id
subnet_id = run.setup.subnet_id
private_ip_address = cidrhost(run.setup.subnet_address_prefix, 10)
}
}

Expand Down Expand Up @@ -190,6 +193,68 @@ run "plan" {
condition = tolist(azurerm_application_gateway.main.identity[0].identity_ids) == tolist([run.setup.managed_identity_id])
error_message = "The Managed Identity ID is not as expected."
}

#region HTTP Listeners

assert {
condition = { for listener in azurerm_application_gateway.main.http_listener : listener.name => listener }["http-listener-1"].name == var.http_listeners[0].name
error_message = "The name of the first HTTP Listener is not as expected."
}

assert {
condition = { for listener in azurerm_application_gateway.main.http_listener : listener.name => listener }["http-listener-1"].frontend_ip_configuration_name == "FrontendPublicIpConfiguration"
error_message = "The frontend_ip_configuration of the first HTTP Listener is not as expected."
}

assert {
condition = { for listener in azurerm_application_gateway.main.http_listener : listener.name => listener }["http-listener-1"].frontend_port_name == tostring(var.http_listeners[0].port)
error_message = "The port of the first HTTP Listener is not as expected."
}

assert {
condition = { for listener in azurerm_application_gateway.main.http_listener : listener.name => listener }["http-listener-1"].protocol == var.http_listeners[0].protocol
error_message = "The protocol of the first HTTP Listener is not as expected."
}

assert {
condition = { for listener in azurerm_application_gateway.main.http_listener : listener.name => listener }["http-listener-2"].name == var.http_listeners[1].name
error_message = "The name of the second HTTP Listener is not as expected."
}

assert {
condition = { for listener in azurerm_application_gateway.main.http_listener : listener.name => listener }["http-listener-2"].frontend_ip_configuration_name == "FrontendPrivateIpConfiguration"
error_message = "The frontend_ip_configuration of the second HTTP Listener is not as expected."
}

assert {
condition = { for listener in azurerm_application_gateway.main.http_listener : listener.name => listener }["http-listener-2"].frontend_port_name == tostring(var.http_listeners[1].port)
error_message = "The port of the second HTTP Listener is not as expected."
}

assert {
condition = { for listener in azurerm_application_gateway.main.http_listener : listener.name => listener }["http-listener-2"].protocol == var.http_listeners[1].protocol
error_message = "The protocol of the second HTTP Listener is not as expected."
}

assert {
condition = { for listener in azurerm_application_gateway.main.http_listener : listener.name => listener }["http-listener-3"].name == var.http_listeners[2].name
error_message = "The name of the third HTTP Listener is not as expected."
}

assert {
condition = { for listener in azurerm_application_gateway.main.http_listener : listener.name => listener }["http-listener-3"].frontend_ip_configuration_name == "FrontendPublicIpConfiguration"
error_message = "The frontend_ip_configuration of the third HTTP Listener is not as expected."
}

assert {
condition = { for listener in azurerm_application_gateway.main.http_listener : listener.name => listener }["http-listener-3"].frontend_port_name == tostring(var.http_listeners[2].port)
error_message = "The port of the third HTTP Listener is not as expected."
}

assert {
condition = { for listener in azurerm_application_gateway.main.http_listener : listener.name => listener }["http-listener-3"].protocol == var.http_listeners[2].protocol
error_message = "The protocol of the third HTTP Listener is not as expected."
}
}

run "apply" {
Expand All @@ -204,10 +269,9 @@ run "apply" {
identity_id = run.setup.managed_identity_id
subnet_id = run.setup.subnet_id
frontend_ip_configuration = {
subnet_id = run.setup.subnet_id
public_ip_address_id = run.setup.public_ip_id
private_ip_address_allocation = "Static"
private_ip_address = cidrhost(run.setup.subnet_address_prefix, 10)
subnet_id = run.setup.subnet_id
public_ip_address_id = run.setup.public_ip_id
private_ip_address = cidrhost(run.setup.subnet_address_prefix, 10)
}
}

Expand Down
48 changes: 41 additions & 7 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ variable "autoscale_configuration" {
max_capacity = number
})
default = null
description = "A mapping with the autoscale configuration of the application gateway."
description = "A mapping with the autoscale configuration of the Application Gateway."

validation {
condition = var.autoscale_configuration != null ? var.autoscale_configuration.min_capacity >= 0 && var.autoscale_configuration.min_capacity <= 100 : true
Expand All @@ -110,12 +110,26 @@ variable "subnet_id" {

variable "frontend_ip_configuration" {
type = object({
subnet_id = optional(string)
public_ip_address_id = string
private_ip_address_allocation = optional(string)
private_ip_address = optional(string)
subnet_id = optional(string)
public_ip_address_id = string
private_ip_address = optional(string)
})
description = "A mapping the front ip configuration."
description = "A mapping with the frontend ip configuration of the Application Gateway."

validation {
condition = var.frontend_ip_configuration.subnet_id != null ? var.frontend_ip_configuration.private_ip_address != null : true
error_message = "The private_ip_address is required when the subnet_id is provided."
}

validation {
condition = var.frontend_ip_configuration.private_ip_address != null ? var.frontend_ip_configuration.subnet_id != null : true
error_message = "The subnet_id is required when the private_ip_address is provided."
}

validation {
condition = var.frontend_ip_configuration.subnet_id != null ? can(cidrnetmask("${var.frontend_ip_configuration.private_ip_address}/32")) : true
error_message = "The private_ip_address must be formatted according to the CIDR standard without a mask."
}
}

variable "backend_address_pools" {
Expand Down Expand Up @@ -148,12 +162,32 @@ variable "http_listeners" {
type = list(object({
name = string
frontend_ip_configuration = string
port = string
port = number
protocol = string
host_name = optional(string)
ssl_certificate_name = optional(string)
}))
description = "List of objects that represent the configuration of each http listener."

validation {
condition = alltrue([for listener in var.http_listeners : contains(["Public", "Private"], listener.frontend_ip_configuration)])
error_message = "The frontend_ip_configuration must be either Public or Private."
}

validation {
condition = alltrue([for listener in var.http_listeners : var.frontend_ip_configuration.subnet_id != null if listener.frontend_ip_configuration == "Private"])
error_message = "The frontend_ip_configuration.subnet_id must be provided when the frontend_ip_configuration is Private."
}

validation {
condition = alltrue([for listener in var.http_listeners : contains(["Http", "Https"], listener.protocol)])
error_message = "The protocol must be either Http or Https."
}

# validation {
# condition = alltrue([for listener in var.http_listeners : listener.protocol == "Https" ? listener.ssl_certificate_name != null : true])
# error_message = "The ssl_certificate_name is required when the protocol is Https."
# }
}

# variable "probes" {
Expand Down

0 comments on commit 1638ef3

Please sign in to comment.