Skip to main content

Azure Front Door Deployment with Terraform - Part 2

· 6 min read
Hasan Gural

There are few points that I would like to address in our second article. We will cover all the configuration of Azure Front Door service. This article will be about how we can deploy Front Door service with Terraform in the most optimum way and how we can use this service more dynamically for multiple endpoint declarations. Before jumping into the article, going over the Front Door's logic and its elements might be beneficial. These elements can be listed as:

  • Front Endpoints
  • Binding SSL Certificate from KeyVault/Front Door Managed
  • Backend Pools
  • Routing Rules (https/http)
  • Web Application Firewall Policy - Managed/Custom Rules

Now let's start to create our next resource called "Web Application Firewall Policy". What we need to do is declaring the azurerm_frontdoor_firewall_policy resource block in main.tf.

# Creating Web Application Firewall policy for Frontdoor
resource "azurerm_frontdoor_firewall_policy" "fd_waf_default" {

name = local.waf_policy_name
resource_group_name = azurerm_resource_group.fd_rg.name
enabled = true

managed_rule {
type = "DefaultRuleSet"
version = "1.0"
}

managed_rule {
type = "Microsoft_BotManagerRuleSet"
version = "1.0"
}

}

As you can see, we will actualize the distribution of the resource after declaring it by using a particular name standard. We had defined the name standard of the resource that will be created in locals. Now let's use terraform plan once again and see the result. We will make the distribution of Web Application Firewall Policy that is a new resource outside of resource group.

We completed deployment process by running Web Application Firewall Policy terraform apply command. Now we need to figure out the distribution of Azure FrontDoor resource. To do this, we need to look at the terraform block I shared below. Please be sure that you have at least a familirity with these concepts.

  • FrontEnd Endpoint, HostName and Web Application Policy declarations
  • BackEnd Pool, Health Probe and Load Balancing Resource declarations
  • Routing Rule structure; Forwarding Protocol and Backend Pool
  • ForEach, Dynamic, Content structures in Terraform.
#Create Azure Front Door Resource
resource "azurerm_frontdoor" "front-door" {
name = local.front_door_name

friendly_name = var.front-door-object.friendly_name
resource_group_name = azurerm_resource_group.ampmshfdrg.name
enforce_backend_pools_certificate_name_check = var.front-door-object.enforce_backend_pools_certificate_name_check
load_balancer_enabled = var.front-door-object.load_balancer_enabled


dynamic "routing_rule" {
for_each = var.front-door-object.routing_rule

content {

name = routing_rule.value.name
accepted_protocols = routing_rule.value.accepted_protocols
patterns_to_match = routing_rule.value.patterns_to_match

frontend_endpoints = routing_rule.value.frontend_endpoints

dynamic "forwarding_configuration" {
for_each = routing_rule.value.configuration == "Forwarding" ? [routing_rule.value.forwarding_configuration] : []
content {
backend_pool_name = routing_rule.value.forwarding_configuration.backend_pool_name
cache_enabled = routing_rule.value.forwarding_configuration.cache_enabled
cache_use_dynamic_compression = routing_rule.value.forwarding_configuration.cache_use_dynamic_compression #default: false
custom_forwarding_path = routing_rule.value.forwarding_configuration.custom_forwarding_path
forwarding_protocol = routing_rule.value.forwarding_configuration.forwarding_protocol
}
}

dynamic "redirect_configuration" {
for_each = routing_rule.value.configuration == "Redirecting" ? [routing_rule.value.redirect_configuration] : []

content {
custom_host = routing_rule.value.redirect_configuration.custom_host
redirect_protocol = routing_rule.value.redirect_configuration.redirect_protocol
redirect_type = routing_rule.value.redirect_configuration.redirect_type
custom_fragment = routing_rule.value.redirect_configuration.custom_fragment
custom_path = routing_rule.value.redirect_configuration.custom_path
custom_query_string = routing_rule.value.redirect_configuration.custom_query_string
}
}
}
}

dynamic "backend_pool_load_balancing" {
for_each = var.front-door-object.backend_pool_load_balancing

content {
name = backend_pool_load_balancing.value.name
sample_size = backend_pool_load_balancing.value.sample_size
successful_samples_required = backend_pool_load_balancing.value.successful_samples_required
additional_latency_milliseconds = backend_pool_load_balancing.value.additional_latency_milliseconds
}
}

dynamic "backend_pool_health_probe" {
for_each = var.front-door-object.backend_pool_health_probe

content {
name = backend_pool_health_probe.value.name
path = backend_pool_health_probe.value.path
protocol = backend_pool_health_probe.value.protocol
interval_in_seconds = backend_pool_health_probe.value.interval_in_seconds
}
}

dynamic "frontend_endpoint" {
for_each = var.front-door-object.frontend_endpoint

content {
name = frontend_endpoint.value.name
host_name = frontend_endpoint.value.host_name
session_affinity_enabled = frontend_endpoint.value.session_affinity_enabled
session_affinity_ttl_seconds = frontend_endpoint.value.session_affinity_ttl_seconds
web_application_firewall_policy_link_id = frontend_endpoint.value.web_application_firewall_policy_link_name != "" ? azurerm_frontdoor_firewall_policy.fd_waf_default.id : ""
}
}

dynamic "backend_pool" {
for_each = var.front-door-object.backend_pool

content {
name = backend_pool.value.name
load_balancing_name = backend_pool.value.load_balancing_name
health_probe_name = backend_pool.value.health_probe_name

dynamic "backend" {
for_each = backend_pool.value.backend
content {
enabled = backend.value.enabled
address = backend.value.address
host_header = backend.value.host_header
http_port = backend.value.http_port
https_port = backend.value.https_port
priority = backend.value.priority
weight = backend.value.weight
}
}
}
}

lifecycle {
ignore_changes = [
]
}
}

As you can see, the terraform resource block above is highly detailed and lengthy. Since we want to do many things in this resource declaration, our focus is on making all the features of this resource more dynamic and reusable. We can list the actions we will execute in this resource while we are deploying Azure FrontDoor block as:

  • Adding FrontEnd Domain and configuring each domain as many as we want.
  • Declaring BackEnd Pool and connecting it to any App Service or any service as many as we want
  • Declaring Routing Rules and assigning them in related BackEnd Pool level

You can see a sample terraform.tfvars file below.

front-door-object = {
friendly_name = "talespin-fd"

enforce_backend_pools_certificate_name_check = false
load_balancer_enabled = true

routing_rule = {

rr1 = {
name = "talespin-cms-https"
frontend_endpoints = ["FrontendEndpoint1"]
accepted_protocols = ["Https"]
patterns_to_match = ["/*"]
enabled = true
configuration = "Forwarding"

redirect_configuration = {
custom_host = ""
redirect_protocol = "HttpsOnly"
redirect_type = "Found"
custom_fragment = ""
custom_path = ""
custom_query_string = ""
}
forwarding_configuration = {
backend_pool_name = "talespin-cms"
cache_enabled = false
cache_use_dynamic_compression = false
cache_query_parameter_strip_directive = "StripNone"
custom_forwarding_path = ""
forwarding_protocol = "HttpsOnly"
}
}

rr2 = {
name = "talespin-bot-https"
frontend_endpoints = ["FrontendEndpoint1"]
accepted_protocols = ["Https"]
patterns_to_match = ["/bot"]
enabled = true
configuration = "Forwarding"

redirect_configuration = {
custom_host = ""
redirect_protocol = "HttpsOnly"
redirect_type = "Found"
custom_fragment = ""
custom_path = ""
custom_query_string = ""
}
forwarding_configuration = {
backend_pool_name = "talespin-bot"
cache_enabled = false
cache_use_dynamic_compression = false
cache_query_parameter_strip_directive = "StripNone"
custom_forwarding_path = ""
forwarding_protocol = "HttpsOnly"
}
}

}

backend_pool_load_balancing = {

lb1 = {
name = "DefaultLoadBalancingSettings"
sample_size = 4
successful_samples_required = 2
additional_latency_milliseconds = 0
}
}

backend_pool_health_probe = {
hp1 = {
name = "DefaultHealthProbeSetting"
path = "/"
protocol = "Https"
interval_in_seconds = 120
}
}

backend_pool = {

bp1 = {
name = "talespin-cms"
backend = {
be1 = {
enabled = true
address = "talespin-cms-prd.azurewebsites.net"
host_header = "talespin-cms-prd.azurewebsites.net"
http_port = 80
https_port = 443
priority = 1
weight = 50
}
}
load_balancing_name = "DefaultLoadBalancingSettings"
health_probe_name = "DefaultHealthProbeSetting"
}

bp2 = {
name = "talespin-bot"
backend = {
be1 = {
enabled = true
address = "talespin-bot-prd.azurewebsites.net"
host_header = "talespin-bot-prd.azurewebsites.net"
http_port = 80
https_port = 443
priority = 1
weight = 50
}
}
load_balancing_name = "DefaultLoadBalancingSettings"
health_probe_name = "DefaultHealthProbeSetting"
}

}
frontend_endpoint = {

fe1 = {
name = "FrontendEndpoint1"
host_name = "talespin-fd.azurefd.net"
session_affinity_enabled = false
session_affinity_ttl_seconds = 0
custom_https_provisioning_enabled = false
#Required if custom_https_provisioning_enabled is true
custom_https_configuration = {
certificate_source = "FrontDoor"
#If certificate source is AzureKeyVault the below are required:
azure_key_vault_certificate_vault_id = ""
azure_key_vault_certificate_secret_name = ""
azure_key_vault_certificate_secret_version = ""
}
web_application_firewall_policy_link_name = "default"
}

fe2 = {
name = "FrontendEndpoint2"
host_name = "talespin.net"
session_affinity_enabled = false
session_affinity_ttl_seconds = 0
custom_https_provisioning_enabled = false
#Required if custom_https_provisioning_enabled is true
custom_https_configuration = {
certificate_source = "FrontDoor"
#If certificate source is AzureKeyVault the below are required:
azure_key_vault_certificate_vault_id = ""
azure_key_vault_certificate_secret_name = ""
azure_key_vault_certificate_secret_version = ""
}
web_application_firewall_policy_link_name = "default"
}
}

tags = {
Owner = ""
Environment = ""
}
}