main.tf (415 lines of code) (raw):
#----------About Terraform Module -------------
# Azure Application Gateway Terraform Module
# Azure Application Gateway is a load balancer that enables you to manage and optimize the traffic to your web applications.
#----------All Required Provider Section-----------
#----------Public IP for application gateway-----------
# The AVM specification generally recommends that resources outside the scope of the resource provider should be supplied by the user.
# In this instance, the public IP address resource is included to align with the experience in the Azure portal.
# See decision noted here: https://github.com/Azure/terraform-azurerm-avm-res-network-applicationgateway/issues/67
resource "azurerm_public_ip" "this" {
count = var.create_public_ip == true ? 1 : 0
allocation_method = var.sku.tier == "Standard" ? "Dynamic" : "Static" # Allocation method for the public ip //var.public_ip_allocation_method
location = var.location
name = var.public_ip_name == null ? local.public_ip_name : var.public_ip_name
resource_group_name = var.resource_group_name
sku = var.sku.tier == "Standard" ? "Basic" : "Standard" # SKU for the public ip //var.public_ip_sku_tier
tags = var.tags
# WAF : Deploy Application Gateway in a zone-redundant configuration
zones = var.zones
}
#----------Application Gateway resource creation provider block-----------
resource "azurerm_application_gateway" "this" {
location = var.location
name = var.name
resource_group_name = var.resource_group_name
enable_http2 = var.http2_enable
fips_enabled = var.fips_enabled
firewall_policy_id = var.app_gateway_waf_policy_resource_id
force_firewall_policy_association = true
tags = var.tags
zones = var.zones
#----------Backend Address Pool Configuration for the application gateway -----------
dynamic "backend_address_pool" {
for_each = var.backend_address_pools
content {
name = backend_address_pool.value.name
fqdns = backend_address_pool.value.fqdns != null ? backend_address_pool.value.fqdns : null
ip_addresses = backend_address_pool.value.ip_addresses != null ? backend_address_pool.value.ip_addresses : null
}
}
#----------Backend Http Settings Configuration for the application gateway -----------
dynamic "backend_http_settings" {
for_each = var.backend_http_settings
content {
cookie_based_affinity = backend_http_settings.value.cookie_based_affinity
name = backend_http_settings.value.name
port = backend_http_settings.value.port
protocol = backend_http_settings.value.protocol
affinity_cookie_name = backend_http_settings.value.affinity_cookie_name
host_name = backend_http_settings.value.host_name
path = backend_http_settings.value.path
pick_host_name_from_backend_address = backend_http_settings.value.pick_host_name_from_backend_address
probe_name = backend_http_settings.value.probe_name
request_timeout = backend_http_settings.value.request_timeout
trusted_root_certificate_names = backend_http_settings.value.trusted_root_certificate_names
dynamic "authentication_certificate" {
for_each = backend_http_settings.value.authentication_certificate == null ? [] : backend_http_settings.value.authentication_certificate
content {
name = authentication_certificate.value.name
}
}
dynamic "connection_draining" {
for_each = backend_http_settings.value.connection_draining == null ? [] : [backend_http_settings.value.connection_draining]
content {
drain_timeout_sec = connection_draining.value.drain_timeout_sec
enabled = connection_draining.value.enable_connection_draining
}
}
}
}
dynamic "frontend_ip_configuration" {
for_each = var.create_public_ip == false && var.public_ip_resource_id == null ? [] : [1]
content {
name = coalesce(var.frontend_ip_configuration_public_name, local.frontend_ip_configuration_name)
public_ip_address_id = var.create_public_ip == true ? azurerm_public_ip.this[0].id : var.public_ip_resource_id
}
}
# Private Frontend IP configuration
dynamic "frontend_ip_configuration" {
for_each = var.frontend_ip_configuration_private.private_ip_address == null ? [] : [var.frontend_ip_configuration_private]
content {
name = coalesce(frontend_ip_configuration.value.name, local.frontend_ip_configuration_private_name)
private_ip_address = frontend_ip_configuration.value.private_ip_address
private_ip_address_allocation = frontend_ip_configuration.value.private_ip_address_allocation
private_link_configuration_name = frontend_ip_configuration.value.private_link_configuration_name
subnet_id = var.gateway_ip_configuration.subnet_id
}
}
# Frontend IP Port configuration
dynamic "frontend_port" {
for_each = var.frontend_ports
content {
name = lookup(frontend_port.value, "name", null)
port = lookup(frontend_port.value, "port", null)
}
}
#----------Gateway configuration for the application gateway-----------
gateway_ip_configuration {
name = coalesce(var.gateway_ip_configuration.name, local.gateway_ip_configuration_name)
subnet_id = var.gateway_ip_configuration.subnet_id
}
#----------Http Listener Configuration for the application gateway -----------
dynamic "http_listener" {
for_each = var.http_listeners
content {
frontend_ip_configuration_name = coalesce(http_listener.value.frontend_ip_configuration_name, local.frontend_ip_configuration_name, local.frontend_ip_configuration_private_name)
#frontend_ip_configuration_name = coalesce(http_listener.value.frontend_ip_configuration_name, var.frontend_ip_configuration_public_name, var.frontend_ip_configuration_private.name)
frontend_port_name = http_listener.value.frontend_port_name
name = http_listener.value.name
protocol = http_listener.value.ssl_certificate_name == null ? "Http" : "Https"
firewall_policy_id = http_listener.value.firewall_policy_id
host_name = http_listener.value.host_name
host_names = http_listener.value.host_names
require_sni = http_listener.value.require_sni
ssl_certificate_name = http_listener.value.ssl_certificate_name
ssl_profile_name = http_listener.value.ssl_profile_name
dynamic "custom_error_configuration" {
for_each = http_listener.value.custom_error_configuration != null ? lookup(http_listener.value, "custom_error_configuration", {}) : []
content {
custom_error_page_url = lookup(custom_error_configuration.value, "custom_error_page_url", null)
status_code = lookup(custom_error_configuration.value, "status_code", null)
}
}
}
}
#----------Rules Configuration for the application gateway -----------
dynamic "request_routing_rule" {
for_each = var.request_routing_rules
content {
http_listener_name = request_routing_rule.value.http_listener_name
name = request_routing_rule.value.name
rule_type = request_routing_rule.value.rule_type
backend_address_pool_name = request_routing_rule.value.backend_address_pool_name
backend_http_settings_name = request_routing_rule.value.backend_http_settings_name
priority = request_routing_rule.value.priority
redirect_configuration_name = request_routing_rule.value.redirect_configuration_name
rewrite_rule_set_name = request_routing_rule.value.rewrite_rule_set_name
url_path_map_name = request_routing_rule.value.url_path_map_name
}
}
#----------SKU and configuration for the application gateway-----------
# WAF : Azure Application Gateways v2 are always deployed in a highly available fashion with multiple instances by default. Enabling autoscale ensures the service is not reliant on manual intervention for scaling.
sku {
name = var.sku.name
tier = var.sku.tier
capacity = var.autoscale_configuration == null ? var.sku.capacity : null
}
dynamic "authentication_certificate" {
for_each = var.authentication_certificate == null ? {} : var.authentication_certificate
content {
data = authentication_certificate.value.data
name = authentication_certificate.value.name
}
}
dynamic "autoscale_configuration" {
for_each = var.autoscale_configuration != null ? [var.autoscale_configuration] : []
content {
min_capacity = lookup(autoscale_configuration.value, "min_capacity", 1)
max_capacity = lookup(autoscale_configuration.value, "max_capacity", 2)
}
}
dynamic "custom_error_configuration" {
for_each = var.custom_error_configuration == null ? {} : var.custom_error_configuration
content {
custom_error_page_url = custom_error_configuration.value.custom_error_page_url
status_code = custom_error_configuration.value.status_code
}
}
dynamic "global" {
for_each = var.global == null ? [] : [var.global]
content {
request_buffering_enabled = global.value.request_buffering_enabled
response_buffering_enabled = global.value.response_buffering_enabled
}
}
dynamic "identity" {
for_each = local.managed_identities.user_assigned
content {
type = identity.value.type
identity_ids = identity.value.user_assigned_resource_ids
}
}
dynamic "private_link_configuration" {
for_each = var.private_link_configuration == null ? [] : var.private_link_configuration
content {
name = private_link_configuration.value.name
dynamic "ip_configuration" {
for_each = private_link_configuration.value.ip_configuration
content {
name = ip_configuration.value.name
primary = ip_configuration.value.primary
private_ip_address_allocation = ip_configuration.value.private_ip_address_allocation
subnet_id = ip_configuration.value.subnet_id
private_ip_address = ip_configuration.value.private_ip_address
}
}
}
}
#----------Prod Rules Configuration for the application gateway -----------
# WAF : Use Health Probes to detect backend availability
#----------Optional Configuration -----------
dynamic "probe" {
for_each = var.probe_configurations != null ? var.probe_configurations : {}
content {
interval = probe.value.interval
name = probe.value.name
path = probe.value.path
protocol = probe.value.protocol
timeout = probe.value.timeout
unhealthy_threshold = probe.value.unhealthy_threshold
host = probe.value.host
minimum_servers = probe.value.minimum_servers
pick_host_name_from_backend_http_settings = probe.value.pick_host_name_from_backend_http_settings
port = probe.value.port
dynamic "match" {
for_each = probe.value.match == null ? [] : [probe.value.match]
content {
status_code = match.value.status_code
body = match.value.body
}
}
}
}
#----------Redirect Configuration for the application gateway -----------
#----------Optionl Configuration -----------
dynamic "redirect_configuration" {
for_each = var.redirect_configuration != null ? var.redirect_configuration : {}
content {
name = redirect_configuration.value.name
redirect_type = redirect_configuration.value.redirect_type
include_path = redirect_configuration.value.include_path
include_query_string = redirect_configuration.value.include_query_string
target_listener_name = redirect_configuration.value.target_listener_name
target_url = redirect_configuration.value.target_url
}
}
dynamic "rewrite_rule_set" {
for_each = var.rewrite_rule_set == null ? {} : var.rewrite_rule_set
content {
name = rewrite_rule_set.value.name
dynamic "rewrite_rule" {
for_each = rewrite_rule_set.value.rewrite_rules == null ? {} : rewrite_rule_set.value.rewrite_rules
content {
name = rewrite_rule.value.name
rule_sequence = rewrite_rule.value.rule_sequence
dynamic "condition" {
for_each = rewrite_rule.value.conditions == null ? {} : rewrite_rule.value.conditions
content {
pattern = condition.value.pattern
variable = condition.value.variable
ignore_case = condition.value.ignore_case
negate = condition.value.negate
}
}
dynamic "request_header_configuration" {
for_each = rewrite_rule.value.request_header_configurations == null ? {} : rewrite_rule.value.request_header_configurations
content {
header_name = request_header_configuration.value.header_name
header_value = request_header_configuration.value.header_value
}
}
dynamic "response_header_configuration" {
for_each = rewrite_rule.value.response_header_configurations == null ? {} : rewrite_rule.value.response_header_configurations
content {
header_name = response_header_configuration.value.header_name
header_value = response_header_configuration.value.header_value
}
}
dynamic "url" {
for_each = rewrite_rule.value.url == null ? [] : [rewrite_rule.value.url]
content {
components = url.value.components
path = url.value.path
query_string = url.value.query_string
reroute = url.value.reroute
}
}
}
}
}
}
#----------SSL Certificate Configuration for the application gateway -----------
#----------Optionl Configuration -----------
dynamic "ssl_certificate" {
for_each = var.ssl_certificates == null ? {} : var.ssl_certificates
content {
name = ssl_certificate.value.name
data = ssl_certificate.value.data
key_vault_secret_id = ssl_certificate.value.key_vault_secret_id
password = ssl_certificate.value.password
}
}
dynamic "ssl_policy" {
for_each = var.ssl_policy == null ? [] : [var.ssl_policy]
content {
cipher_suites = ssl_policy.value.cipher_suites
disabled_protocols = ssl_policy.value.disabled_protocols
min_protocol_version = ssl_policy.value.min_protocol_version
policy_name = ssl_policy.value.policy_name
policy_type = ssl_policy.value.policy_type
}
}
dynamic "ssl_profile" {
for_each = var.ssl_profile == null ? {} : var.ssl_profile
content {
name = ssl_profile.value.name
trusted_client_certificate_names = ssl_profile.value.trusted_client_certificate_names
verify_client_cert_issuer_dn = ssl_profile.value.verify_client_cert_issuer_dn
verify_client_certificate_revocation = ssl_profile.value.verify_client_certificate_revocation
dynamic "ssl_policy" {
for_each = ssl_profile.value.ssl_policy == null ? [] : [ssl_profile.value.ssl_policy]
content {
cipher_suites = ssl_policy.value.cipher_suites
disabled_protocols = ssl_policy.value.disabled_protocols
min_protocol_version = ssl_policy.value.min_protocol_version
policy_name = ssl_policy.value.policy_name
policy_type = ssl_policy.value.policy_type
}
}
}
}
dynamic "timeouts" {
for_each = var.timeouts == null ? [] : [var.timeouts]
content {
create = timeouts.value.create
delete = timeouts.value.delete
read = timeouts.value.read
update = timeouts.value.update
}
}
dynamic "trusted_client_certificate" {
for_each = var.trusted_client_certificate == null ? {} : var.trusted_client_certificate
content {
data = trusted_client_certificate.value.data
name = trusted_client_certificate.value.name
}
}
dynamic "trusted_root_certificate" {
for_each = var.trusted_root_certificate == null ? {} : var.trusted_root_certificate
content {
name = trusted_root_certificate.value.name
data = trusted_root_certificate.value.data
key_vault_secret_id = trusted_root_certificate.value.key_vault_secret_id
}
}
dynamic "url_path_map" {
for_each = var.url_path_map_configurations != null ? var.url_path_map_configurations : {}
content {
name = url_path_map.value.name
default_backend_address_pool_name = url_path_map.value.default_backend_address_pool_name
default_backend_http_settings_name = url_path_map.value.default_backend_http_settings_name
default_redirect_configuration_name = url_path_map.value.default_redirect_configuration_name
default_rewrite_rule_set_name = url_path_map.value.default_rewrite_rule_set_name
dynamic "path_rule" {
for_each = url_path_map.value.path_rules != null ? url_path_map.value.path_rules : {}
content {
name = path_rule.value.name
paths = path_rule.value.paths
backend_address_pool_name = path_rule.value.backend_address_pool_name
backend_http_settings_name = path_rule.value.backend_http_settings_name
# WAF Enable Web Application Firewall policies
firewall_policy_id = path_rule.value.firewall_policy_id
redirect_configuration_name = path_rule.value.redirect_configuration_name
rewrite_rule_set_name = path_rule.value.rewrite_rule_set_name
}
}
}
}
#----------Classic WAF Configuration for the application gateway -----------
# A separate WAF configuration policy is recommended, this allows for the configuration 'classic' policy inline with the application gateway.
# To Enable Web Application Firewall policies provide a the WAF configuration block.
#----------Optional Configuration -----------
dynamic "waf_configuration" {
for_each = var.waf_configuration == null ? [] : [var.waf_configuration]
content {
enabled = waf_configuration.value.enabled
firewall_mode = waf_configuration.value.firewall_mode
rule_set_version = waf_configuration.value.rule_set_version
file_upload_limit_mb = waf_configuration.value.file_upload_limit_mb
max_request_body_size_kb = waf_configuration.value.max_request_body_size_kb
request_body_check = waf_configuration.value.request_body_check
rule_set_type = waf_configuration.value.rule_set_type
dynamic "disabled_rule_group" {
for_each = waf_configuration.value.disabled_rule_group == null ? [] : waf_configuration.value.disabled_rule_group
content {
rule_group_name = disabled_rule_group.value.rule_group_name
rules = disabled_rule_group.value.rules
}
}
dynamic "exclusion" {
for_each = waf_configuration.value.exclusion == null ? [] : waf_configuration.value.exclusion
content {
match_variable = exclusion.value.match_variable
selector = exclusion.value.selector
selector_match_operator = exclusion.value.selector_match_operator
}
}
}
}
}
# Example resource implementation
resource "azurerm_management_lock" "this" {
count = var.lock != null ? 1 : 0
lock_level = var.lock.kind
name = coalesce(var.lock.name, "lock-${var.lock.kind}")
scope = azurerm_application_gateway.this.id
notes = var.lock.kind == "CanNotDelete" ? "Cannot delete the resource or its child resources." : "Cannot delete or modify the resource or its child resources."
}
#----------role assignment settings for the application gateway -----------
resource "azurerm_role_assignment" "this" {
for_each = var.role_assignments
principal_id = each.value.principal_id
scope = azurerm_application_gateway.this.id
condition = each.value.condition
condition_version = each.value.condition_version
delegated_managed_identity_resource_id = each.value.delegated_managed_identity_resource_id
principal_type = each.value.principal_type
role_definition_id = strcontains(lower(each.value.role_definition_id_or_name), lower(local.role_definition_resource_substring)) ? each.value.role_definition_id_or_name : null
role_definition_name = strcontains(lower(each.value.role_definition_id_or_name), lower(local.role_definition_resource_substring)) ? null : each.value.role_definition_id_or_name
skip_service_principal_aad_check = each.value.skip_service_principal_aad_check
}
#----------Diagnostic logs settings for the application gateway -----------
# WAF : Monitor and Log the configurations and traffic
resource "azurerm_monitor_diagnostic_setting" "this" {
for_each = var.diagnostic_settings
name = each.value.name != null ? each.value.name : "diag-${var.name}"
target_resource_id = azurerm_application_gateway.this.id
eventhub_authorization_rule_id = each.value.event_hub_authorization_rule_resource_id
eventhub_name = each.value.event_hub_name
log_analytics_destination_type = each.value.log_analytics_destination_type
log_analytics_workspace_id = each.value.workspace_resource_id
partner_solution_id = each.value.marketplace_partner_resource_id
storage_account_id = each.value.storage_account_resource_id
dynamic "enabled_log" {
for_each = each.value.log_categories
content {
category = enabled_log.value
}
}
dynamic "enabled_log" {
for_each = each.value.log_groups
content {
category_group = enabled_log.value
}
}
dynamic "metric" {
for_each = each.value.metric_categories
content {
category = metric.value
}
}
}
# Other configurations for your environment