Skip to content

sEpt0r/terraform-azurerm-dns-from-yaml

Repository files navigation

Azure DNS Zone from YAML

This Terraform module creates Azure DNS zones and all record types from YAML configuration files or Terraform variables. It provides a declarative way to manage DNS records with support for all Azure DNS record types including A, AAAA, CAA, CNAME, MX, NS, PTR, SOA, SRV, and TXT records.

Features

  • Complete DNS Record Support: All 10 Azure DNS record types (A, AAAA, CAA, CNAME, MX, NS, PTR, SOA, SRV, TXT)
  • YAML Configuration: Define DNS records in human-readable YAML files
  • Per-Record Tagging: Apply Azure tags at individual record level
  • Flexible TTL: Custom TTL per record with sensible defaults
  • Zone Delegation: Automatic NS record creation for subdomain delegation
  • Multiple Input Methods: YAML files or Terraform variables
  • Production Ready: Comprehensive validation and error handling

Quick Start

module "dns_zone" {
  source = "sEpt0r/dns-from-yaml/azurerm"

  resource_group_name = "my-dns-rg"
  zone_name           = "example.com"
  dns_records         = yamldecode(file("zones/example.com.yaml"))
}

Usage Methods

The module supports two primary usage patterns:

1. Using YAML Files

Create YAML files with DNS zone configurations. The filename determines the DNS zone name (e.g., example.com.yaml creates example.com zone).

Example YAML Structure:

# Zone apex records (use "@" for root domain)
"@":
  A:
    records:
      - 203.0.113.1
      - 203.0.113.2
    ttl: 300
    tags:
      environment: production
  MX:
    records:
      - exchange: mail.example.com
        preference: 10
  TXT:
    records:
      - "v=spf1 a mx ~all"
      - "google-site-verification=abc123"

# Subdomain records
www:
  CNAME:
    record: example.com

# Additional records
api:
  A:
    records:
      - 203.0.113.3
      - 203.0.113.4

See the example/zones folder for a complete example with all the supported record types.

Terraform Configuration:

variable "zone_files" {
  type        = list(string)
  description = "List of DNS zone YAML files"
  default = [
    "zones/example.com.yaml",
    "zones/app.example.com.yaml"
  ]
}

module "dns_records_from_yaml" {
  source = "sEpt0r/dns-from-yaml/azurerm"

  for_each = {
    for zone_file in var.zone_files :
    zone_file => yamldecode(file(zone_file))
  }

  dns_records         = each.value
  zone_name           = trimsuffix(basename(each.key), ".yaml")
  resource_group_name = var.resource_group_name

  default_tags = {
    Environment = "production"
    ManagedBy   = "terraform"
  }
}

2. Using Terraform Variables

Pass DNS records directly as Terraform variables for programmatic generation:

variable "dns_records" {
  description = "Object of DNS records to create from variable instead of yaml files"
  default = {
    "example3.com" = {
      "@" = {
        A = {
          records = ["203.0.113.1", "203.0.113.2"]
          ttl     = 300
        }
        MX = {
          records = [{
            exchange   = "mail.example.com"
            preference = 10
          }]
        }
      }
      www = {
        CNAME = {
          record = "example.com"
        }
      }
    }
  }
}

module "dns_records_from_var" {
    source = "sEpt0r/dns-from-yaml/azurerm"

    for_each = var.dns_records

    dns_records         = each.value
    zone_name           = each.key
    resource_group_name = var.resource_group_name
}

Advanced Usage

Zone Delegation

Automatically create NS records in a parent zone for subdomain delegation:

module "subdomain_zone" {
  source = "sEpt0r/dns-from-yaml/azurerm"

  zone_name           = "api.example.com"
  parent_zone         = "example.com"  # Creates NS records in parent
  resource_group_name = "my-dns-rg"
  dns_records         = yamldecode(file("zones/api.example.com.yaml"))
}

Custom TTL and Tagging

module "dns_zone" {
  source = "sEpt0r/dns-from-yaml/azurerm"

  zone_name           = "example.com"
  resource_group_name = "my-dns-rg"
  dns_records         = yamldecode(file("zones/example.com.yaml"))

  # Override default TTL values
  default_ttl = {
    A     = 600    # 10 minutes
    CNAME = 7200   # 2 hours
    MX    = 86400  # 24 hours
  }

  # Apply tags to all DNS resources
  default_tags = {
    Environment = "production"
    Project     = "web-infrastructure"
    ManagedBy   = "terraform"
  }
}

Supported DNS Record Types

This module supports all Azure DNS record types with their respective configurations:

A Records

subdomain:
  A:
    records: ["203.0.113.1", "203.0.113.2"]
    ttl: 300  # optional here and all below examples
    tags:     # optional
      key: value

AAAA Records (IPv6)

subdomain:
  AAAA:
    records: ["2001:db8::1", "2001:db8::2"]
    ttl: 300

CNAME Records

www:
  CNAME:
    record: "example.com"  # single target
    ttl: 3600

MX Records

"@":
  MX:
    records:
      - exchange: "mail1.example.com"
        preference: 10
      - exchange: "mail2.example.com"
        preference: 20
    ttl: 3600

TXT Records

"@":
  TXT:
    records:
      - "v=spf1 a mx ~all"
      - "google-site-verification=abc123"
    ttl: 300

SRV Records

_service._tcp:
  SRV:
    records:
      - priority: 10
        weight: 5
        port: 443
        target: "server1.example.com"
    ttl: 3600

CAA Records (Certificate Authority Authorization)

"@":
  CAA:
    records:
      - flags: 0
        tag: "issue"
        value: "letsencrypt.org"
      - flags: 0
        tag: "iodef"
        value: "mailto:security@example.com"
    ttl: 3600

NS Records

subdomain:
  NS:
    records: ["ns1.example.com", "ns2.example.com"]
    ttl: 3600

PTR Records (Reverse DNS)

"1":
  PTR:
    records: ["server1.example.com"]
    ttl: 3600

SOA Records (Start of Authority)

"@":
  SOA:
    email: "admin.example.com"
    host_name: "ns1.example.com"
    expire_time: 2419200
    minimum_ttl: 300
    refresh_time: 3600
    retry_time: 300
    serial_number: 1
    ttl: 3600

Note

See example/zones/example.com.yaml for a complete working example with all record types.

Requirements

Name Version
terraform >= 1.5.0
azurerm ~> 4.0

Providers

Name Version
azurerm ~> 4.0

Modules

No modules.

Resources

Name Type
azurerm_dns_a_record.a resource
azurerm_dns_aaaa_record.aaaa resource
azurerm_dns_caa_record.caa resource
azurerm_dns_cname_record.cname resource
azurerm_dns_mx_record.mx resource
azurerm_dns_ns_record.delegate resource
azurerm_dns_ns_record.ns resource
azurerm_dns_ptr_record.ptr resource
azurerm_dns_srv_record.srv resource
azurerm_dns_txt_record.txt resource
azurerm_dns_zone.this resource

Inputs

Name Description Type Default Required
default_tags Default tags for DNS records map(string) {} no
default_ttl Default TTL for DNS records map(number)
{
"A": 300,
"AAAA": 300,
"CAA": 300,
"CNAME": 300,
"MX": 3600,
"NS": 3600,
"PTR": 3600,
"SRV": 3600,
"TXT": 300
}
no
dns_records DNS records data
map(
object(
{
A = optional(object({
records = list(string)
ttl = optional(number)
tags = optional(map(string))
})),
AAAA = optional(object({
records = list(string)
ttl = optional(number)
tags = optional(map(string))
})),
CAA = optional(object({
records = list(object({
flags = number
tag = string
value = string
}))
ttl = optional(number)
tags = optional(map(string))
})),
CNAME = optional(object({
record = string
ttl = optional(number)
tags = optional(map(string))
})),
MX = optional(object({
records = list(object({
exchange = string
preference = number
}))
ttl = optional(number)
tags = optional(map(string))
})),
NS = optional(object({
records = list(string)
ttl = optional(number)
tags = optional(map(string))
})),
PTR = optional(object({
records = list(string)
ttl = optional(number)
tags = optional(map(string))
})),
SOA = optional(object({
email = string
host_name = optional(string)
expire_time = optional(number)
minimum_ttl = optional(number)
refresh_time = optional(number)
retry_time = optional(number)
serial_number = optional(number)
ttl = optional(number)
tags = optional(map(string))
})),
SRV = optional(object({
records = list(object({
port = number
priority = number
target = string
weight = number
}))
ttl = optional(number)
tags = optional(map(string))
})),
TXT = optional(object({
records = list(string)
ttl = optional(number)
tags = optional(map(string))
}))
}
)
)
{} no
parent_zone Parent DNS zone name to create NS records for delegation string null no
resource_group_name Resource Group Name string n/a yes
zone_name DNS zone name string n/a yes

Outputs

Name Description
dns_zone DNS zone information including ID and name

Examples

Complete examples are available in the example/ directory:

Validation and Best Practices

DNS Record Validation

  • CNAME Restrictions: Cannot coexist with other record types for the same name
  • Zone Apex: CNAME records cannot be created at zone apex (@)
  • TTL Limits: Valid range is 1 to 2,147,483,647 seconds
  • Azure DNS Record Limits: Maximum 20 records per record set (except TXT: 400). If you need to increase these limits, contact Azure Support.

YAML Best Practices

# Use quotes for zone apex
"@":
  # Use descriptive comments
  TXT:
    records:
      - "v=spf1 a mx ~all"  # SPF record

# Use consistent indentation (2 spaces)
www:
  A:
    records:
      - 203.0.113.1
    ttl: 300
    tags:
      Environment: production

Security Considerations

  • Store YAML files in version control for audit trail
  • Use least-privilege Azure RBAC for DNS management
  • Regularly review CAA records for certificate authority restrictions
  • Implement proper SPF/DKIM/DMARC records for email security

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -am 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Issues

About

Terraform module for managing Azure DNS zones and records with flexible input methods - supports both YAML files and Terraform variables for easy DNS configuration

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages