Zones and records with Terraform
The dtm_zone and dtm_record resources are the core of managing DNS as code.
See zones and records for the concepts.
resource "dtm_zone" "example" { name = "example.internal." default_ttl = 300}dtm_zone (role: editor or admin) creates an authoritative zone. Give
the name as an FQDN with a trailing dot. DTM synthesizes the SOA and NS
records; you only set the SOA fields below if you need specific values.
| Attribute | Required | Description |
|---|---|---|
name |
Yes | Zone FQDN with trailing dot, e.g. example.internal.. Changing it replaces the zone. |
default_ttl |
Yes | Default TTL (seconds) for records that do not set their own. |
lb_policy (block) |
No | Zone-wide default LB policy applied at resolve time to records without their own; same block as dtm_record.lb_policy (see health and LB). Removing the block clears the default. |
maintain_reverse |
No | Opt in to automatic reverse (PTR) maintenance for the zone. Defaults to false. |
deletion_lock |
No | Server-side deletion lock. Defaults to false: the provider always sends an explicit value, so a Terraform-managed zone is exactly what its config says, even under a locked cluster default (which applies to UI/API creates that omit the field). |
primary_ns |
No | SOA primary nameserver. Defaults to ns1.<zone>. |
admin_email |
No | SOA contact in DNS form (dots, no @). Defaults to hostmaster.<zone>. |
refresh / retry / expire / min_ttl |
No | SOA timers (seconds). |
id / serial / status |
(computed) | Server-assigned zone ID, SOA serial, and status (pending / active / deleting). |
Look up an existing zone
Section titled “Look up an existing zone”Use the dtm_zone data source to reference a zone you did not create in this
configuration:
data "dtm_zone" "corp" { name = "corp.example."}
resource "dtm_record" "api" { zone_id = data.dtm_zone.corp.id name = "api" type = "A" values { value = "10.0.2.10" }}The data-source lookup is forgiving about form: matching is case-insensitive
and the trailing dot is optional, so corp.example, Corp.Example., and
corp.example. all resolve the same zone. The name you write is kept
verbatim in state, so a non-canonical spelling never shows up as drift.
Records
Section titled “Records”resource "dtm_record" "web" { zone_id = dtm_zone.example.id name = "web" type = "A" ttl = 60
values { value = "10.0.1.10" } values { value = "10.0.1.11" }}dtm_record (role: editor or admin) manages a record in a zone.
| Attribute | Required | Description |
|---|---|---|
zone_id |
Yes | Parent zone ID, typically dtm_zone.<name>.id. Changing it replaces the record. |
name |
Yes | Name relative to the zone, e.g. web. Use @ for the zone apex. |
type |
Yes | A, AAAA, CNAME, MX, NS, PTR, SRV, TXT, CAA, or ALIAS. Changing it replaces the record. |
ttl |
No | TTL in seconds. 0 inherits the zone’s default_ttl. |
enabled |
No | Defaults to true. A disabled record is kept but not served. |
protected |
No | Defaults to false. A protected record cannot be deleted, or replaced by a colliding create, rename, or import, without an explicit force. |
values (block, 1+) |
Yes | One or more values (see below). |
lb_policy (block) |
No | Load-balancing policy. See health checks and load balancing. |
health_check_template (block) |
No | Health-check template applied per value. See health checks and load balancing. |
Value blocks
Section titled “Value blocks”Each values block holds one backend:
values { value = "10.0.1.10" region = "northeurope" # optional; used by region-aware LB}| Value attribute | Description |
|---|---|
value |
The record data: an IP for A/AAAA, a hostname for CNAME/MX/NS/ALIAS, text for TXT. |
region |
Optional region tag for region-aware load balancing. |
tag |
CAA only: issue, issuewild, or iodef. |
priority |
MX preference / SRV priority (and the CAA flag octet, 0 to 255). |
weight |
SRV weight only. The weighted load-balancing method uses lb_policy.value_weights instead, not this field. |
port |
SRV target port. |
Auto-populated records
Section titled “Auto-populated records”dtm_auto_record (role: editor or admin) manages a record whose
value list is owned by DTM: tag-based VM
registration fills in the addresses as your fleet
changes, while everything you declare here (TTL, LB policy, health template,
enabled, protected) stays yours. The resource has no values
attribute at all, so a plan can never diff on a list DTM owns.
resource "dtm_auto_record" "api" { zone_id = dtm_zone.example.id name = "api" type = "A" # A or AAAA only ttl = 30
lb_policy { method = "round-robin" }
health_check_template { protocol = "https" port = 443 path = "/healthz" }}Attributes match dtm_record minus values, and type accepts only A
and AAAA. lb_policy.value_weights is rejected: weights key on addresses
DTM will change. A freshly created record has no values and answers NODATA
until a tagged VM claims the name.
Common record types
Section titled “Common record types”# CNAME (exclusive at a name, cannot sit at the apex)resource "dtm_record" "www" { zone_id = dtm_zone.example.id name = "www" type = "CNAME" values { value = "web.example.internal." }}
# MX with prioritiesresource "dtm_record" "mail" { zone_id = dtm_zone.example.id name = "@" type = "MX" values { value = "mx1.example.internal." priority = 10 } values { value = "mx2.example.internal." priority = 20 }}
# TXTresource "dtm_record" "spf" { zone_id = dtm_zone.example.id name = "@" type = "TXT" values { value = "v=spf1 include:_spf.example.internal -all" }}
# SRVresource "dtm_record" "sip" { zone_id = dtm_zone.example.id name = "_sip._tcp" type = "SRV" values { value = "sipserver.example.internal." priority = 10 weight = 60 port = 5060 }}Import
Section titled “Import”# Records import as "<zone_id>:<record_id>"terraform import dtm_record.web 8f3a...:c21b...
# Zones import as "<zone_id>"terraform import dtm_zone.example 8f3a...Next steps
Section titled “Next steps”- Balance across regions with ALIAS records and overrides.
- Add health checks and load-balancing policies.