Skip to content

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.

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.

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.

How a query's source IP becomes a region: the source address is matched against the subnet mappings, the most specific prefix wins and an explicit priority breaks overlaps, manual and discovered mappings feed the same table, and the resulting region drives the region-aware load-balancing decision. With no matching mapping the client is assumed to be in the serving node's own region. How a query's source IP becomes a region: the source address is matched against the subnet mappings, the most specific prefix wins and an explicit priority breaks overlaps, manual and discovered mappings feed the same table, and the resulting region drives the region-aware load-balancing decision. With no matching mapping the client is assumed to be in the serving node's own region.

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.

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: true applies membership drift on the timer too; the default is manual for both.

Subscription scopes keep working unchanged, and both kinds can coexist.

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.

  • Web UI: the settings pages cover subnet mappings and discovery scopes.
  • REST API: /api/v1/subnet-mappings, /api/v1/discovery/scopes, and /api/v1/regions.
  • Terraform: dtm_subnet_mapping and dtm_discovery_scope.