Skip to content

Tag-based VM registration

Tag an Azure VM with a name, and DTM keeps that name’s addresses in step with your fleet. Tag-based VM registration discovers virtual machines and VM Scale Set instances carrying a registration tag and maintains the A (IPv4) and AAAA (IPv6) values of a record you created, adding addresses when VMs appear, updating them when IPs change, and removing them when VMs go away.

The division of ownership is strict, and it is the heart of the feature:

  • You own the record. You pre-create it with auto_populate: true, and everything you set on it stays yours: TTL, lb_policy, health_check_template, enabled, protected. Registration never touches them.
  • DTM owns the value list. On an auto_populate record, the reconciler adds and removes addresses as the fleet changes. Do not edit the values yourself; the next cycle would put them back the way Azure says they should be.
Who owns what: the auto_populate record api.example.internal drawn as a card split in two. The YOURS half holds TTL, lb_policy, health_check_template, enabled, and protected, with a no-entry marker on the arrow toward it: registration never touches your configuration. The DTM OWNS half is the value list: 10.30.1.12 from vm-api-01 and two VMSS instance addresses; values are present or removed, with no in-between state. Around it, the cycle: tagged VMs and a VM Scale Set are read through Azure Resource Graph (read-only, Reader role), and four fleet events drive arrows into the value list only: add when a new VM appears, update when an IP changes, remove when a VM goes away or is untagged, and drain when the whole service departs, which empties the value list while the record survives. Who owns what: the auto_populate record api.example.internal drawn as a card split in two. The YOURS half holds TTL, lb_policy, health_check_template, enabled, and protected, with a no-entry marker on the arrow toward it: registration never touches your configuration. The DTM OWNS half is the value list: 10.30.1.12 from vm-api-01 and two VMSS instance addresses; values are present or removed, with no in-between state. Around it, the cycle: tagged VMs and a VM Scale Set are read through Azure Resource Graph (read-only, Reader role), and four fleet events drive arrows into the value list only: add when a new VM appears, update when an IP changes, remove when a VM goes away or is untagged, and drain when the whole service departs, which empties the value list while the record survives.

The integration is opt-in and read-only toward Azure: it is disabled by default, and when enabled it discovers everything through Azure Resource Graph queries. DTM never modifies your Azure resources.

Create the record for the service name with auto_populate: true and no values (requires a bearer token; see the API overview):

Terminal window
curl -sk -X POST https://<dtm>:8443/api/v1/zones/<zone-id>/records \
-H "Authorization: Bearer $DTM_TOKEN" \
-H "X-DTM-CSRF: 1" \
-H "Content-Type: application/json" \
-d '{
"name": "api",
"type": "A",
"ttl": 30,
"auto_populate": true,
"lb_policy": {"method": "round-robin"},
"health_check_template": {"protocol": "https", "port": 443, "path": "/healthz"}
}'

This is the only record shape that may be created with an empty value list: an auto-populated record with no values is a service that currently has no instances, and it answers NODATA rather than NXDOMAIN, so resolvers do not cache a negative for a name that is about to come back.

Constraints on auto_populate records (all rejected with 400):

  • values must be omitted on create, and PUT may not carry values either. To take the addresses back into your own hands, send {"auto_populate": false, "values": [...]} in one request.
  • Only A and AAAA types.
  • lb_policy.value_weights is rejected: weights key on the address itself, and DTM changes those addresses as the fleet does. Every other method (round-robin, region, geo-failover, failover, latency, client affinity) copes with a changing value set.

In Terraform, use the dedicated dtm_auto_record resource, which has no values attribute at all, so a plan can never diff on a list DTM owns.

Add the registration tag to a VM or a VM Scale Set:

Terminal window
az vm update -g rg-app-prod -n vm-api-01 \
--set "tags.dtm-register=api.example.internal"

On the next cycle, DTM adds the VM’s primary NIC private IPs to your record’s values:

Terminal window
dig @<dtm> api.example.internal +short
10.30.1.12

Multiple VMs, one name. If several VMs, or the instances of a VMSS, carry the same FQDN in their tag, their addresses collapse into that one record, which DTM then load balances with the policy you configured:

Terminal window
dig @<dtm> api.example.internal +short
10.30.1.12
10.30.1.13
10.30.1.14

The tag key defaults to dtm-register and is configurable (see below). Tag names are matched the way Azure treats them, case-insensitively, so a tag typed as DTM-Register still registers.

Both VM Scale Set orchestration modes are supported: Uniform, and Flexible (the Azure default for new scale sets), whose instances are standalone VM resources that reference the scale set.

A departed service, whether scaled to zero, untagged, or deleted, empties its record rather than deleting it. The record survives with all of its configuration (TTL, LB policy, health template) and answers NODATA until the service returns. Registration never deletes a record.

Removal of individual addresses is still cycle-gated: values are only removed after a complete discovery cycle. A partial cycle (a failed Resource Graph page, an unreadable subscription) can add addresses but never removes them, so a transient Azure error cannot take live backends out of DNS.

Set the health configuration on the record itself via health_check_template, as in step 1. Per-address checks are instantiated automatically for each value and follow their address: adding or removing one VM leaves every other backend’s accumulated health state untouched.

The legacy dtm-healthcheck VM tag no longer configures anything. Health configuration belongs to the record; if a VM carries a dtm-healthcheck tag that disagrees with the record’s template, the tag is reported in the server log and otherwise ignored.

Registration remains gated by a mandatory zone allowlist. A tagged FQDN only registers if it is covered by an allowlisted DTM zone that is Active, using the most specific match when zones nest.

The node’s managed identity needs the Reader role over the compute and network resources in scope, so the Resource Graph queries can see the VMs, VMSS instances, and their NICs.

Unlike Private Endpoint automation, tag-based registration is server boot configuration (YAML or environment variables), not a runtime API:

azure:
tag_registration_enabled: true
tag_registration_interval_seconds: 300 # default 300
tag_registration_tag_key: dtm-register # default
tag_registration_zone_allowlist:
- example.internal
- db.example.internal
tag_registration_client_id: '' # optional user-assigned MI

Each setting has an environment equivalent under the DTM_AZURE_TAG_REGISTRATION_* prefix, for example DTM_AZURE_TAG_REGISTRATION_ENABLED=true.

Set tag_registration_client_id only if the node should authenticate with a specific user-assigned managed identity instead of the default.

One admin API endpoint exists, to run a discovery cycle immediately instead of waiting for the interval (requires the admin role):

Terminal window
curl -sk -X POST https://<dtm>:8443/api/v1/tag-registration/run \
-H "Authorization: Bearer $DTM_TOKEN" \
-H "X-DTM-CSRF: 1"

The response reports what the cycle actually did:

{ "updated": 2, "drained": 0, "skipped": 0, "unclaimed": 1 }
  • updated: records whose value list changed.
  • drained: records emptied because their service departed.
  • skipped: tagged resources refused by the zone allowlist or with invalid tag values.
  • unclaimed: tags naming a record nobody has pre-created.

If a tagged VM does not publish, check in this order:

  1. Pre-created record: does a record with auto_populate: true exist at the tagged name? If not, the tag counts as unclaimed and nothing is published.
  2. Allowlist: is the FQDN covered by an allowlisted zone, and is that zone Active? An empty allowlist registers nothing.
  3. Permissions: does the managed identity have Reader over the VM and its NIC? A subscription DTM cannot read degrades the cycle to incomplete, which pauses value removal until access is restored.
  4. Logs and counters: skipped and unclaimed counts in the run response, plus ignored dtm-healthcheck tags and unresolvable NICs, are logged on each cycle.