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.
- 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
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"))
}The module supports two primary usage patterns:
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.4See 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"
}
}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
}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"))
}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"
}
}This module supports all Azure DNS record types with their respective configurations:
subdomain:
A:
records: ["203.0.113.1", "203.0.113.2"]
ttl: 300 # optional here and all below examples
tags: # optional
key: valuesubdomain:
AAAA:
records: ["2001:db8::1", "2001:db8::2"]
ttl: 300www:
CNAME:
record: "example.com" # single target
ttl: 3600"@":
MX:
records:
- exchange: "mail1.example.com"
preference: 10
- exchange: "mail2.example.com"
preference: 20
ttl: 3600"@":
TXT:
records:
- "v=spf1 a mx ~all"
- "google-site-verification=abc123"
ttl: 300_service._tcp:
SRV:
records:
- priority: 10
weight: 5
port: 443
target: "server1.example.com"
ttl: 3600"@":
CAA:
records:
- flags: 0
tag: "issue"
value: "letsencrypt.org"
- flags: 0
tag: "iodef"
value: "mailto:security@example.com"
ttl: 3600subdomain:
NS:
records: ["ns1.example.com", "ns2.example.com"]
ttl: 3600"1":
PTR:
records: ["server1.example.com"]
ttl: 3600"@":
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: 3600Note
See example/zones/example.com.yaml for a complete working example with all record types.
| Name | Version |
|---|---|
| terraform | >= 1.5.0 |
| azurerm | ~> 4.0 |
| Name | Version |
|---|---|
| azurerm | ~> 4.0 |
No modules.
| 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 |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| default_tags | Default tags for DNS records | map(string) |
{} |
no |
| default_ttl | Default TTL for DNS records | map(number) |
{ |
no |
| dns_records | DNS records data | map( |
{} |
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 |
| Name | Description |
|---|---|
| dns_zone | DNS zone information including ID and name |
Complete examples are available in the example/ directory:
- Basic YAML Usage: Creating zones from YAML files
- Variable Usage: Creating zones from Terraform variables
- Complete Record Types: All supported DNS record types
- Subdomain Example: Subdomain configuration
- 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.
# 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- 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
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -am 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.