Managing Azure DNS with Terraform

Learn how to set up and manage Azure DNS using Terraform, including zones, records, and private DNS

Managing Azure DNS with Terraform

Azure DNS is a hosting service for DNS domains that provides name resolution using Microsoft Azure infrastructure. This guide demonstrates how to set up and manage Azure DNS using Terraform.

Video Tutorial

Learn more about managing Azure DNS 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 concepts

Project Structure

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

DNS Configuration

Create modules/dns/main.tf:

# Public DNS Zone
resource "azurerm_dns_zone" "public" {
  name                = var.domain_name
  resource_group_name = var.resource_group_name

  soa_record {
    email         = "admin.${var.domain_name}"
    expire_time   = 2419200
    minimum_ttl   = 300
    refresh_time  = 3600
    retry_time    = 300
    serial_number = 1
    ttl           = 3600
  }

  tags = var.tags
}

# Private DNS Zone
resource "azurerm_private_dns_zone" "private" {
  name                = "internal.${var.domain_name}"
  resource_group_name = var.resource_group_name

  soa_record {
    email        = "admin.internal.${var.domain_name}"
    expire_time  = 2419200
    minimum_ttl  = 300
    refresh_time = 3600
    retry_time   = 300
    ttl          = 3600
  }

  tags = var.tags
}

# Virtual Network Link
resource "azurerm_private_dns_zone_virtual_network_link" "main" {
  name                  = "${var.project_name}-link"
  resource_group_name   = var.resource_group_name
  private_dns_zone_name = azurerm_private_dns_zone.private.name
  virtual_network_id    = var.virtual_network_id
  registration_enabled  = true

  tags = var.tags
}

# A Records
resource "azurerm_dns_a_record" "www" {
  name                = "www"
  zone_name           = azurerm_dns_zone.public.name
  resource_group_name = var.resource_group_name
  ttl                 = 300
  records             = ["20.0.0.4"]

  tags = var.tags
}

resource "azurerm_dns_a_record" "app" {
  name                = "app"
  zone_name           = azurerm_dns_zone.public.name
  resource_group_name = var.resource_group_name
  ttl                 = 300
  target_resource_id  = var.app_service_id

  tags = var.tags
}

# CNAME Records
resource "azurerm_dns_cname_record" "blog" {
  name                = "blog"
  zone_name           = azurerm_dns_zone.public.name
  resource_group_name = var.resource_group_name
  ttl                 = 300
  record              = "blog.example.com"

  tags = var.tags
}

# MX Records
resource "azurerm_dns_mx_record" "mail" {
  name                = "@"
  zone_name           = azurerm_dns_zone.public.name
  resource_group_name = var.resource_group_name
  ttl                 = 300

  record {
    preference = 10
    exchange   = "mail1.example.com"
  }

  record {
    preference = 20
    exchange   = "mail2.example.com"
  }

  tags = var.tags
}

# TXT Records
resource "azurerm_dns_txt_record" "spf" {
  name                = "@"
  zone_name           = azurerm_dns_zone.public.name
  resource_group_name = var.resource_group_name
  ttl                 = 300

  record {
    value = "v=spf1 include:spf.protection.outlook.com -all"
  }

  tags = var.tags
}

# NS Records
resource "azurerm_dns_ns_record" "subdomain" {
  name                = "subdomain"
  zone_name           = azurerm_dns_zone.public.name
  resource_group_name = var.resource_group_name
  ttl                 = 300

  records = [
    "ns1.example.com",
    "ns2.example.com"
  ]

  tags = var.tags
}

# PTR Records
resource "azurerm_dns_ptr_record" "ptr" {
  name                = "ptr"
  zone_name           = azurerm_dns_zone.public.name
  resource_group_name = var.resource_group_name
  ttl                 = 300
  records             = ["contoso.com"]

  tags = var.tags
}

# SRV Records
resource "azurerm_dns_srv_record" "service" {
  name                = "_sip._tcp"
  zone_name           = azurerm_dns_zone.public.name
  resource_group_name = var.resource_group_name
  ttl                 = 300

  record {
    priority = 1
    weight   = 5
    port     = 8080
    target   = "sip.example.com"
  }

  tags = var.tags
}

# CAA Records
resource "azurerm_dns_caa_record" "caa" {
  name                = "@"
  zone_name           = azurerm_dns_zone.public.name
  resource_group_name = var.resource_group_name
  ttl                 = 300

  record {
    flags = 0
    tag   = "issue"
    value = "letsencrypt.org"
  }

  record {
    flags = 0
    tag   = "issuewild"
    value = ";"
  }

  tags = var.tags
}

# Private A Records
resource "azurerm_private_dns_a_record" "internal" {
  name                = "internal"
  zone_name           = azurerm_private_dns_zone.private.name
  resource_group_name = var.resource_group_name
  ttl                 = 300
  records             = ["10.0.0.4"]

  tags = var.tags
}

# Private CNAME Records
resource "azurerm_private_dns_cname_record" "db" {
  name                = "db"
  zone_name           = azurerm_private_dns_zone.private.name
  resource_group_name = var.resource_group_name
  ttl                 = 300
  record              = "database.internal.${var.domain_name}"

  tags = var.tags
}

Monitoring Configuration

  1. Diagnostic Settings
resource "azurerm_monitor_diagnostic_setting" "dns" {
  name                       = "${var.project_name}-diag"
  target_resource_id        = azurerm_dns_zone.public.id
  log_analytics_workspace_id = var.log_analytics_workspace_id

  metric {
    category = "AllMetrics"
    enabled  = true

    retention_policy {
      enabled = true
      days    = 30
    }
  }
}

# DNS Alerts
resource "azurerm_monitor_metric_alert" "query_volume" {
  name                = "${var.project_name}-query-volume-alert"
  resource_group_name = var.resource_group_name
  scopes              = [azurerm_dns_zone.public.id]
  description         = "Alert when DNS query volume is high"

  criteria {
    metric_namespace = "Microsoft.Network/dnszones"
    metric_name      = "QueryVolume"
    aggregation      = "Total"
    operator         = "GreaterThan"
    threshold        = 10000
  }

  action {
    action_group_id = var.action_group_id
  }
}

Advanced Features

  1. DNS Alias Records
resource "azurerm_dns_a_record" "alias" {
  name                = "alias"
  zone_name           = azurerm_dns_zone.public.name
  resource_group_name = var.resource_group_name
  ttl                 = 300
  target_resource_id  = var.public_ip_id

  tags = var.tags
}
  1. Traffic Manager Integration
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          = 100
  }

  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_dns_cname_record" "tm" {
  name                = "tm"
  zone_name           = azurerm_dns_zone.public.name
  resource_group_name = var.resource_group_name
  ttl                 = 300
  record              = "${azurerm_traffic_manager_profile.main.name}.trafficmanager.net"

  tags = var.tags
}

Best Practices

  1. Security

    • Use DNSSEC
    • Configure CAA records
    • Monitor queries
    • Review access
  2. Performance

    • Set appropriate TTLs
    • Use aliases
    • Monitor latency
    • Configure caching
  3. High Availability

    • Use Traffic Manager
    • Configure failover
    • Monitor health
    • Review metrics
  4. Cost Optimization

    • Monitor usage
    • Optimize queries
    • Review records
    • Clean up unused

Conclusion

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

  • Public DNS zones
  • Private DNS zones
  • Record management
  • Traffic routing

Remember to:

  • Monitor queries
  • Update records
  • Review configurations
  • Maintain zones