Managing Azure Traffic Manager with Terraform

Learn how to set up and manage Azure Traffic Manager using Terraform, including routing methods and endpoint configurations

Azure Traffic Manager is a DNS-based traffic load balancer that enables you to distribute traffic optimally to services across global Azure regions. This guide demonstrates how to set up and manage Traffic Manager using Terraform.

Video Tutorial

Learn more about managing Azure Traffic Manager 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 DNS and routing concepts

Project Structure

terraform-azure-trafficmanager/
├── main.tf
├── variables.tf
├── outputs.tf
├── modules/
│   └── trafficmanager/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
└── configs/
    └── endpoints.json

Traffic Manager Configuration

Create modules/trafficmanager/main.tf:

# Traffic Manager Profile
resource "azurerm_traffic_manager_profile" "main" {
  name                   = "${var.project_name}-tm"
  resource_group_name    = var.resource_group_name
  traffic_routing_method = "Performance"

  dns_config {
    relative_name = var.project_name
    ttl          = 60
  }

  monitor_config {
    protocol                     = "HTTPS"
    port                        = 443
    path                        = "/health"
    interval_in_seconds         = 30
    timeout_in_seconds          = 10
    tolerated_number_of_failures = 3
    custom_header {
      name  = "host"
      value = var.monitor_host
    }
    expected_status_code_ranges = ["200-299"]
  }

  traffic_view_enabled = true

  tags = var.tags
}

# Azure Endpoints
resource "azurerm_traffic_manager_azure_endpoint" "primary" {
  name               = "primary"
  profile_id         = azurerm_traffic_manager_profile.main.id
  target_resource_id = var.primary_app_id
  weight             = 100
  priority           = 1

  custom_header {
    name  = "x-region"
    value = "primary"
  }

  geo_mappings = ["WORLD"]
}

resource "azurerm_traffic_manager_azure_endpoint" "secondary" {
  name               = "secondary"
  profile_id         = azurerm_traffic_manager_profile.main.id
  target_resource_id = var.secondary_app_id
  weight             = 100
  priority           = 2

  custom_header {
    name  = "x-region"
    value = "secondary"
  }

  geo_mappings = ["WORLD"]
}

# External Endpoint
resource "azurerm_traffic_manager_external_endpoint" "backup" {
  name       = "backup"
  profile_id = azurerm_traffic_manager_profile.main.id
  target     = "backup.example.com"
  weight     = 100
  priority   = 3

  endpoint_location = var.backup_location

  custom_header {
    name  = "x-region"
    value = "backup"
  }

  geo_mappings = ["WORLD"]
}

# Nested Profile - a child Traffic Manager profile that gets referenced as
# an endpoint of the parent profile below
resource "azurerm_traffic_manager_profile" "nested" {
  name                   = "${var.project_name}-tm-nested"
  resource_group_name    = var.resource_group_name
  traffic_routing_method = "Priority"

  dns_config {
    relative_name = "${var.project_name}-nested"
    ttl          = 60
  }

  monitor_config {
    protocol                     = "HTTPS"
    port                        = 443
    path                        = "/health"
    interval_in_seconds         = 30
    timeout_in_seconds          = 10
    tolerated_number_of_failures = 3
  }

  tags = var.tags
}

resource "azurerm_traffic_manager_nested_endpoint" "nested" {
  name                      = "nested"
  profile_id                = azurerm_traffic_manager_profile.main.id
  target_resource_id        = azurerm_traffic_manager_profile.nested.id
  weight                    = 100
  priority                  = 4
  minimum_child_endpoints   = 1
  
  custom_header {
    name  = "x-region"
    value = "nested"
  }

  geo_mappings = ["WORLD"]
}

# Geographic Routing Profile
resource "azurerm_traffic_manager_profile" "geo" {
  name                   = "${var.project_name}-tm-geo"
  resource_group_name    = var.resource_group_name
  traffic_routing_method = "Geographic"

  dns_config {
    relative_name = "${var.project_name}-geo"
    ttl          = 60
  }

  monitor_config {
    protocol                     = "HTTPS"
    port                        = 443
    path                        = "/health"
    interval_in_seconds         = 30
    timeout_in_seconds          = 10
    tolerated_number_of_failures = 3
  }

  tags = var.tags
}

resource "azurerm_traffic_manager_azure_endpoint" "us" {
  name               = "us"
  profile_id         = azurerm_traffic_manager_profile.geo.id
  target_resource_id = var.us_app_id
  weight             = 100

  geo_mappings = ["US", "CA"]
}

resource "azurerm_traffic_manager_azure_endpoint" "eu" {
  name               = "eu"
  profile_id         = azurerm_traffic_manager_profile.geo.id
  target_resource_id = var.eu_app_id
  weight             = 100

  geo_mappings = ["GB", "FR", "DE"]
}

# Weighted Routing Profile
resource "azurerm_traffic_manager_profile" "weighted" {
  name                   = "${var.project_name}-tm-weighted"
  resource_group_name    = var.resource_group_name
  traffic_routing_method = "Weighted"

  dns_config {
    relative_name = "${var.project_name}-weighted"
    ttl          = 60
  }

  monitor_config {
    protocol                     = "HTTPS"
    port                        = 443
    path                        = "/health"
    interval_in_seconds         = 30
    timeout_in_seconds          = 10
    tolerated_number_of_failures = 3
  }

  tags = var.tags
}

resource "azurerm_traffic_manager_azure_endpoint" "prod" {
  name               = "prod"
  profile_id         = azurerm_traffic_manager_profile.weighted.id
  target_resource_id = var.prod_app_id
  weight             = 80
}

resource "azurerm_traffic_manager_azure_endpoint" "canary" {
  name               = "canary"
  profile_id         = azurerm_traffic_manager_profile.weighted.id
  target_resource_id = var.canary_app_id
  weight             = 20
}

DNS Configuration

  1. DNS Zone Setup
resource "azurerm_dns_zone" "main" {
  name                = "example.com"
  resource_group_name = var.resource_group_name

  tags = var.tags
}

resource "azurerm_dns_cname_record" "tm" {
  name                = "www"
  zone_name           = azurerm_dns_zone.main.name
  resource_group_name = var.resource_group_name
  ttl                 = 300
  record              = azurerm_traffic_manager_profile.main.fqdn
}

Monitoring Configuration

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

  metric {
    category = "AllMetrics"
    enabled  = true

    retention_policy {
      enabled = true
      days    = 30
    }
  }
}

resource "azurerm_monitor_metric_alert" "tm" {
  name                = "${var.project_name}-endpoint-alert"
  resource_group_name = var.resource_group_name
  scopes              = [azurerm_traffic_manager_profile.main.id]
  description         = "Alert when endpoint is degraded"

  criteria {
    metric_namespace = "Microsoft.Network/trafficManagerProfiles"
    metric_name      = "ProbeAgentCurrentEndpointStateByProfileResourceId"
    aggregation      = "Maximum"
    operator         = "GreaterThan"
    threshold        = 0
  }

  action {
    action_group_id = var.action_group_id
  }
}

Advanced Features

  1. Real User Measurements

Real User Measurements (RUM) is a distinct feature from Traffic View (the traffic_view_enabled flag below) - RUM scores individual Azure regions’ latency from real end users via a JavaScript SDK embedded in your pages, using a per-subscription key from the Azure portal; it isn’t something a profile resource toggles on its own. traffic_view_enabled here only enables the separate Traffic View analytics/heatmap feature.

resource "azurerm_traffic_manager_profile" "rum" {
  name                   = "${var.project_name}-tm-rum"
  resource_group_name    = var.resource_group_name
  traffic_routing_method = "Performance"

  dns_config {
    relative_name = "${var.project_name}-rum"
    ttl          = 60
  }

  monitor_config {
    protocol                     = "HTTPS"
    port                        = 443
    path                        = "/health"
    interval_in_seconds         = 30
    timeout_in_seconds          = 10
    tolerated_number_of_failures = 3
  }

  traffic_view_enabled = true

  tags = var.tags
}
  1. Subnet Routing
resource "azurerm_traffic_manager_profile" "subnet" {
  name                   = "${var.project_name}-tm-subnet"
  resource_group_name    = var.resource_group_name
  traffic_routing_method = "Subnet"

  dns_config {
    relative_name = "${var.project_name}-subnet"
    ttl          = 60
  }

  monitor_config {
    protocol                     = "HTTPS"
    port                        = 443
    path                        = "/health"
    interval_in_seconds         = 30
    timeout_in_seconds          = 10
    tolerated_number_of_failures = 3
  }

  tags = var.tags
}

Best Practices

  1. Performance

    • Choose appropriate routing method
    • Configure monitoring
    • Optimize TTL settings
    • Enable Traffic View
  2. High Availability

    • Use multiple endpoints
    • Configure health checks
    • Implement failover
    • Monitor endpoint status
  3. Global Distribution

    • Use geographic routing
    • Configure regional endpoints
    • Implement load balancing
    • Monitor regional performance
  4. Cost Optimization

    • Use appropriate routing
    • Monitor endpoints

Conclusion

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

  • Global traffic routing
  • Multiple routing methods
  • High availability
  • Performance optimization

Remember to:

  • Monitor endpoint health
  • Review routing settings
  • Update DNS records
  • Maintain endpoint configurations