Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: http listeners #25

Merged
merged 2 commits into from
Aug 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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