Managing Azure Front Door with Terraform

Learn how to set up and manage Azure Front Door using Terraform, including WAF policies, routing rules, and caching

Managing Azure Front Door with Terraform

Azure Front Door is a global, scalable entry-point that uses Microsoft’s global edge network to create fast, secure, and widely available web applications. This guide demonstrates how to set up and manage Front Door using Terraform.

Video Tutorial

Learn more about managing Azure Front Door with Terraform in this comprehensive video tutorial:

Prerequisites

  • Azure CLI configured with appropriate permissions
  • Terraform installed (version 1.0.0 or later)
  • Resource group created
  • Understanding of CDN and WAF concepts

Project Structure

terraform-azure-frontdoor/
├── main.tf
├── variables.tf
├── outputs.tf
├── modules/
│   └── frontdoor/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
└── policies/
    └── waf.json

Front Door Configuration

Create modules/frontdoor/main.tf:

# Front Door Profile
resource "azurerm_cdn_frontdoor_profile" "main" {
  name                = "${var.project_name}-fd"
  resource_group_name = var.resource_group_name
  sku_name            = "Premium_AzureFrontDoor"

  tags = var.tags
}

# WAF Policy
resource "azurerm_cdn_frontdoor_firewall_policy" "main" {
  name                = "${var.project_name}-waf"
  resource_group_name = var.resource_group_name
  sku_name            = azurerm_cdn_frontdoor_profile.main.sku_name
  enabled             = true
  mode                = "Prevention"

  custom_rule {
    name                           = "BlockIPRange"
    enabled                        = true
    priority                       = 100
    rate_limit_duration_in_minutes = 1
    rate_limit_threshold          = 10
    type                          = "MatchRule"
    action                        = "Block"

    match_condition {
      match_variable     = "RemoteAddr"
      operator           = "IPMatch"
      negation_condition = false
      match_values       = ["192.168.1.0/24"]
    }
  }

  managed_rule {
    type    = "DefaultRuleSet"
    version = "1.0"
    action  = "Block"

    override {
      rule_group_name = "PHP"
      rule {
        rule_id = "933100"
        enabled = false
        action  = "Block"
      }
    }
  }

  managed_rule {
    type    = "BotProtection"
    version = "1.0"
    action  = "Block"
  }
}

# Endpoint
resource "azurerm_cdn_frontdoor_endpoint" "main" {
  name                     = "${var.project_name}-endpoint"
  cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id

  tags = var.tags
}

# Origin Group
resource "azurerm_cdn_frontdoor_origin_group" "main" {
  name                     = "${var.project_name}-origin-group"
  cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id
  session_affinity_enabled = true

  load_balancing {
    sample_size                 = 4
    successful_samples_required = 3
    additional_latency_in_ms    = 50
  }

  health_probe {
    path                = "/health"
    protocol            = "Https"
    interval_in_seconds = 100
    request_type        = "HEAD"
  }
}

# Origin
resource "azurerm_cdn_frontdoor_origin" "main" {
  name                          = "${var.project_name}-origin"
  cdn_frontdoor_origin_group_id = azurerm_cdn_frontdoor_origin_group.main.id
  enabled                       = true

  certificate_name_check_enabled = true

  host_name          = "api.example.com"
  http_port          = 80
  https_port         = 443
  origin_host_header = "api.example.com"
  priority           = 1
  weight             = 1000

  private_link {
    request_message        = "Request access for Front Door"
    target_type           = "sites"
    location              = var.location
    private_link_target_id = var.app_service_id
  }
}

# Route
resource "azurerm_cdn_frontdoor_route" "main" {
  name                          = "${var.project_name}-route"
  cdn_frontdoor_endpoint_id     = azurerm_cdn_frontdoor_endpoint.main.id
  cdn_frontdoor_origin_group_id = azurerm_cdn_frontdoor_origin_group.main.id
  enabled                       = true

  forwarding_protocol    = "HttpsOnly"
  https_redirect_enabled = true
  patterns_to_match     = ["/*"]
  supported_protocols   = ["Http", "Https"]

  cache {
    query_string_caching_behavior = "IgnoreQueryString"
    compression_enabled           = true
    content_types_to_compress    = ["text/html", "text/javascript", "text/css"]
  }

  custom_domains = [azurerm_cdn_frontdoor_custom_domain.main.id]
}

# Custom Domain
resource "azurerm_cdn_frontdoor_custom_domain" "main" {
  name                     = "${var.project_name}-custom-domain"
  cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id
  dns_zone_id              = var.dns_zone_id
  host_name               = "www.example.com"

  tls {
    certificate_type    = "ManagedCertificate"
    minimum_tls_version = "TLS12"
  }
}

# Rule Set
resource "azurerm_cdn_frontdoor_rule_set" "main" {
  name                     = "${var.project_name}-ruleset"
  cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id
}

# Rules
resource "azurerm_cdn_frontdoor_rule" "redirect" {
  name                      = "redirect-rule"
  cdn_frontdoor_rule_set_id = azurerm_cdn_frontdoor_rule_set.main.id
  order                     = 1
  behavior_on_match         = "Continue"

  conditions {
    url_path_condition {
      operator         = "BeginsWith"
      negate_condition = false
      match_values     = ["/old"]
    }
  }

  actions {
    url_redirect_action {
      redirect_type        = "Found"
      destination_path    = "/new"
      destination_fragment = "section1"
      query_string        = "path={path}"
    }
  }
}

resource "azurerm_cdn_frontdoor_rule" "cache" {
  name                      = "cache-rule"
  cdn_frontdoor_rule_set_id = azurerm_cdn_frontdoor_rule_set.main.id
  order                     = 2
  behavior_on_match         = "Continue"

  conditions {
    url_file_extension_condition {
      operator         = "Equal"
      negate_condition = false
      match_values     = ["jpg", "png", "gif"]
    }
  }

  actions {
    cache_expiration_action {
      behavior = "Override"
      duration = "08:00:00"
    }
  }
}

# Security Policy
resource "azurerm_cdn_frontdoor_security_policy" "main" {
  name                     = "${var.project_name}-security"
  cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id

  security_policies {
    firewall {
      cdn_frontdoor_firewall_policy_id = azurerm_cdn_frontdoor_firewall_policy.main.id

      association {
        patterns_to_match = ["/*"]
        domain {
          cdn_frontdoor_domain_id = azurerm_cdn_frontdoor_endpoint.main.id
        }
      }
    }
  }
}

## DNS Configuration

1. **DNS Records**
```hcl
resource "azurerm_dns_cname_record" "frontdoor" {
  name                = "www"
  zone_name           = var.dns_zone_name
  resource_group_name = var.resource_group_name
  ttl                 = 3600
  record              = azurerm_cdn_frontdoor_endpoint.main.host_name
}

resource "azurerm_dns_txt_record" "frontdoor" {
  name                = "_dnsauth.www"
  zone_name           = var.dns_zone_name
  resource_group_name = var.resource_group_name
  ttl                 = 3600

  record {
    value = azurerm_cdn_frontdoor_custom_domain.main.validation_token
  }
}

Monitoring Configuration

  1. Diagnostic Settings
resource "azurerm_monitor_diagnostic_setting" "frontdoor" {
  name                       = "${var.project_name}-diag"
  target_resource_id        = azurerm_cdn_frontdoor_profile.main.id
  log_analytics_workspace_id = var.log_analytics_workspace_id

  log {
    category = "FrontDoorAccessLog"
    enabled  = true

    retention_policy {
      enabled = true
      days    = 30
    }
  }

  metric {
    category = "AllMetrics"
    enabled  = true

    retention_policy {
      enabled = true
      days    = 30
    }
  }
}

resource "azurerm_monitor_metric_alert" "frontdoor" {
  name                = "${var.project_name}-latency-alert"
  resource_group_name = var.resource_group_name
  scopes              = [azurerm_cdn_frontdoor_profile.main.id]
  description         = "Alert when latency is high"

  criteria {
    metric_namespace = "Microsoft.Cdn/profiles"
    metric_name      = "TotalLatency"
    aggregation      = "Average"
    operator         = "GreaterThan"
    threshold        = 1000
  }

  action {
    action_group_id = var.action_group_id
  }
}

Best Practices

  1. Performance

    • Enable caching
    • Configure compression
    • Use custom domains
    • Optimize routing
  2. Security

    • Enable WAF
    • Configure SSL/TLS
    • Implement rate limiting
    • Monitor traffic
  3. High Availability

    • Use multiple origins
    • Configure health probes
    • Implement load balancing
    • Monitor endpoints
  4. Cost Optimization

    • Monitor usage
    • Configure rules

Conclusion

You’ve learned how to set up and manage Azure Front Door using Terraform. This setup provides:

  • Global load balancing
  • WAF protection
  • SSL/TLS termination
  • Caching and compression

Remember to:

  • Monitor performance
  • Review security rules
  • Update WAF policies
  • Maintain configurations