Regions and subnet mapping
Region-aware load balancing (region and
geo-failover) needs to know which region a client and each backend belong
to. DTM works this out from IP addresses using subnet mappings.
Regions
Section titled “Regions”A region in DTM is simply a string identifier you choose, such as northeurope
or westeurope. Regions are not separate objects to manage; they come into
existence when you reference them on a subnet mapping or a record value. The set
of known regions is derived from those references.
Region matching is case-insensitive, the way Azure treats locations: a
value tagged EastUS matches a mapping or discovery result that says
eastus. A genuinely different region is still remote; only case is folded.
Subnet mappings
Section titled “Subnet mappings”A subnet mapping ties a CIDR block to a region:
{ "subnet": "10.100.0.0/16", "region_id": "northeurope" }DTM uses these to:
- Determine a client’s region from the source address of a DNS query.
- Infer a value’s region when you have not tagged it explicitly.
When CIDRs overlap, a mapping’s optional priority decides which one wins (higher priority wins); otherwise the most specific prefix match applies.
Azure region discovery (optional)
Section titled “Azure region discovery (optional)”Rather than maintain subnet mappings by hand, you can let DTM discover your Azure VNet address space and generate region mappings for you. You define one or more discovery scopes (a subscription, optionally narrowed to a resource group), and DTM walks them using its managed identity’s read access to build subnet-to-region mappings.
Discovery detects on a timer; you apply. The periodic loop queries Azure,
diffs the result against the stored mappings, and reports what changed. It
does not write mappings on its own: an administrator reviews the pending diff
(in the web UI or via
POST /api/v1/discovery/run?dry_run=true) and applies it. An unapplied
mapping is benign; an unmapped client is treated as being in the serving
node’s region, so resolution keeps working while a change waits for review.
Set azure.region_discovery_auto_apply: true to restore the previous
apply-on-a-timer behaviour.
Two safety rules apply to every cycle:
- Removals are only ever proposed from a complete observation. Azure filters by RBAC rather than erroring, so a subscription the identity has lost Reader on looks identical to an empty one. If a cycle could not see the whole configured estate, additions are still offered but no removals are, because the likeliest cause of a large proposed removal is a lapsed role assignment, not a deleted estate.
- Newly observed Azure regions are surfaced in the diff, so an estate expanding into a region with no DTM presence is visible to whoever plans capacity, instead of being absorbed silently.
Discovered mappings are marked as coming from Azure so you can tell them
apart from ones you created manually, and manual mappings always carry
source: manual (the API refuses any other value, so automation cannot
disguise a hand-made mapping as a discovered one, or vice versa). You can add
manual mappings alongside discovered ones.
Management-group scopes
Section titled “Management-group scopes”For estates spanning many subscriptions, a discovery scope can name an Azure
management group instead of a single subscription
(kind: "management-group"). Grant the DTM identity Reader once at the
management group and every subscription underneath becomes usable, instead
of managing a role assignment per subscription.
A management-group scope is a reviewed shortcut, not an open-ended grant of trust:
- DTM never expands a management group on its own. Creating the scope
requires the explicit list of subscriptions it covers. You preview the
group first (in the UI or via
GET /api/v1/discovery/available-management-groups), see which member subscriptions the identity can and cannot read, and confirm the set you are authorising. A subscription that joins the group later cannot start being scanned without someone approving it. - Preview with verification. The quick preview infers readability from the roles the identity holds anywhere in each subscription, which can be optimistic (Reader on one resource group makes the whole subscription appear). The verified preview issues the same subscription-wide read discovery actually performs, so what it reports readable really is.
- Membership drift is detected, reviewed, and applied, like mapping
changes: each cycle re-reads the group and reports subscriptions that
joined (proposed additions), left the group (proposed removals), or are
still in the group but no longer readable. That last case is a revoked
role assignment, not a membership change, and is deliberately never
proposed for removal: dropping it would turn a permissions problem into
deleted routing state. Instead, cycles report themselves incomplete until
you restore the grant or remove the subscription deliberately.
region_discovery_auto_apply: trueapplies membership drift on the timer too; the default is manual for both.
Subscription scopes keep working unchanged, and both kinds can coexist.
Resource Graph backend (optional)
Section titled “Resource Graph backend (optional)”Discovery has two interchangeable backends. The default walks each scope with
per-resource ARM calls. The resource-graph mode instead runs a single Azure
Resource Graph query across every enabled scope, which is faster and scales
better across many subscriptions. Your discovery scopes and the resulting
mappings are identical either way.
Switch the mode at runtime, no restart needed, from the Region Discovery
settings tab in the web UI, the
dtm_region_discovery_config Terraform
resource, or PUT /api/v1/config/region-discovery (admin). A persisted value
wins over the azure.region_discovery_mode server-config seed, which just sets
the initial default. For resource-graph, the managed identity needs Resource
Graph Reader covering every subscription in scope; an unentitled switch
fails closed and is reversible by switching back. Discovery is conservative
with cleanup on both backends: stale mappings are pruned only after a fully
successful cycle, so a partial or truncated query never removes mappings.