Skip to content

Add a Region

Common request: Add a region to the hub (for example a second region for disaster recovery or data residency).

Effect: the platform environment gets a second virtual hub in the new region, connected to the same Virtual WAN, with its own firewall, DNS, identity and management networks. Spokes can then use the region.

Status of this runbook

This runbook is written from the code (Get-XebiaRegion.ps1, Set-XebiaRegionMatrix.ps1, the layer count guards) and the two-region Demo configuration (config/Demo/canary, weu + neu). Validate it on canary and refine it after the first customer region add.

Request fields

Request field Where it goes Notes
Region active_regions, active_regions_names, new <region>/ folder Azure name (swedencentral) and short name (sdc). The short name is used in every resource name and state key.
Rationale PR / change record DR, latency, data residency, …
Environment canary and/or prod Do canary first.
Address ranges <region>/connectivity.auto.tfvars, identity.auto.tfvars, management.auto.tfvars Virtual hub prefix, hub VNet subnets, identity and management VNets. Must not overlap with any other region or spoke.
Features in the region <region>/<layer>.auto.tfvars Firewall, VPN/ExpressRoute, App Gateway, Bastion, private DNS zones.
Comments –

How regions work in the code

  • Regions come from <env-folder>/global.auto.tfvars:

    primary_region       = "weu"
    active_regions       = ["weu", "neu"]
    active_regions_names = ["westeurope", "northeurope"]
    
  • Level 1 runs foundation and connectivity in the primary region; level 2 runs them in every other region; level 3 runs firewall, identity and management in all regions; level 4 runs postdeployment in the primary region only.

  • Resources that exist once per platform environment (management groups, policy definitions and assignments, alert initiatives) are guarded with count = var.scaffold_location_short_name == var.primary_region ? 1 : 0, so they are not duplicated in the new region.
  • The Virtual WAN is created by the primary region; the new region adds a virtual hub to it.
  • Never change primary_region of a running platform. That moves every primary-only resource to a new state key.

Steps

1. Allocate address ranges

Reserve from the IP plan, for example (Demo uses 10.160.0.0/… for weu and 10.160.20.0/24 as neu virtual hub):

Range Variable
Virtual hub virtual_hub_address_prefix (connectivity)
Hub VNet and subnets (Bastion, DNS resolver in/out, App Gateway) hub VNet subnet map (connectivity)
DNS server IP of the region virtual_network_dns_servers (connectivity); spokes in the region use it by default
Identity VNet identity
Management VNet management

2. Add the region to global.auto.tfvars

Append the short name to active_regions and the Azure name to active_regions_names, in the same position:

primary_region       = "gwc"
active_regions       = ["gwc", "sdc"]
active_regions_names = ["germanywestcentral", "swedencentral"]

3. Create the region folder

Create <env-folder>/<region>/. Copy an existing non-primary region folder if you have one, otherwise the primary region folder, and then adjust:

File Change
global.auto.tfvars scaffold_location = "swedencentral", scaffold_location_short_name = "sdc"
connectivity.auto.tfvars New address ranges and DNS server IP. Set feature flags for this region. Point application_gateway_json_file_relative_path / web_application_firewall_policy_json_file_relative_path at sdc/… folders.
firewall.auto.tfvars firewall_rule_collection_file_location = "sdc/firewall_rule_configuration", ip_group_json_file_relative_path = "sdc/ip_groups/". Keep the global paths.
identity.auto.tfvars, management.auto.tfvars New VNet ranges.
foundation.auto.tfvars Only the regional values (for example action group receivers). Primary-only settings such as management_group_structure have no effect outside the primary region; Demo neu uses management_group_structure = {}.
postdeployment.auto.tfvars Not read: postdeployment runs only in the primary region. You can leave it out.

Write every path relative to the <env-folder> (why).

4. Add firewall rules for the region

  • Create <region>/firewall_rule_configuration/ and <region>/ip_groups/ with the rules the region needs (at least the platform rules: job runners, DNS, Azure services).
  • With use_multiregion_fwp_rulecollectiongroup_naming = true, files in the global firewall_rule_configuration/ are merged in for every region; a regional file with the same name wins.
  • Add rules for traffic between regions where needed.

5. Validate and open a pull request

Follow Making a change safely, steps 4–5. The config tests also validate the new region's JSON.

6. Plan and apply on canary

Run hub in Plan or ApproveModifications mode. Expect:

Layer Existing region New region
foundation no changes regional resources only (action groups, …)
connectivity no changes (check the Virtual WAN) virtual hub, firewall, firewall policy, hub VNet, DNS resolver, enabled gateways
firewall no changes IP groups and rule collection groups
identity, management no changes VNets and hub connections, regional management resources
postdeployment no changes –

Stop if the existing region shows destroys or replacements. Creating a virtual hub and firewall takes 30–60 minutes.

7. Verify

  1. Connectivity subscription → Virtual WAN → Hubs: the new hub is listed and Succeeded.
  2. Firewall Manager: the new hub is a secured hub with the expected policy.
  3. The identity and management VNets of the new region show as connected.
  4. Name resolution from the new region's DNS resolver works for a private endpoint zone.

8. Repeat for prod

9. Let spokes use the region

Per spoke: add the region to targetRegions.additional and add a network[] entry with "region": "sdc" and at least one subnet with "isprivatelinksubnet": true. Run spoke for that file. See Change spoke.

Roll back

Remove the region from active_regions / active_regions_names only after destroying its resources: a region that is no longer in the matrix is not planned at all, so its resources stay in Azure with orphaned state. To remove a region, first remove all spokes from it, then destroy its layers locally with Remove-XebiaLocal -DeployName <layer> -Region <region> in reverse order, and only then remove it from global.auto.tfvars.

Common problems

Symptom Cause
primary_region '<x>' is not in active_regions primary_region missing from active_regions
Region not deployed at all Region missing from active_regions, or the regex in Get-XebiaRegion.ps1 can't parse the list (keep it on one line, double quotes)
Firewall layer plans no rules for the new region Wrong relative path in <region>/firewall.auto.tfvars
Name conflicts in Key Vault or storage Global names include the region short name; choose a short name that keeps them under the Azure limits