Platform: What It Is and How It Works¶
This section is for platform engineers: XMMS engineers in the managed model, or the customer's own platform team in the self-managed model (Operating models). It explains what the platform is technically, how it is deployed, and how to build, release and configure it.
| I want to… | Read |
|---|---|
| Understand the architecture | This page. For the reasoning behind it, see the Architecture reference |
| Understand the order in which everything is deployed | Platform deploy flow |
| Know which repository holds what | Repo setup |
| Work on the code: tools, provisioning, new features, releases | Code |
| Change a customer's configuration (hub or spoke) | Config |
| Upgrade a customer to a new version | What to do when a new version is released |
| Run the platform day to day | Operations, Incident management, Monthly health check, Maintenance calendar |
| Organise firewall rules, regions, DNS, agents and VPN access | Design guidance |
| Implement the platform in a new tenant (plan, prepare, deploy, validate, hand over) | Implementing the platform |
| Handle a standard change step by step (operator and reviewer runbooks) | Reviewer and operator runbooks |
| Maintain the template library | Managed Templates |
| See what changed per version, do version-specific upgrade steps | Releases and upgrade guidance, Release history |
| Understand and set up alerting | Monitoring and alerting |
| Troubleshoot pipelines, agents and local access | Troubleshooting how-tos |
| Know which Azure resources the platform deploys, and the switch for each | What the platform deploys |
| Understand how customer copies of the code repository stay current | Code synchronisation |
| Understand governance, roles and the DNS design | Governance, Identity and access model, DNS design |
What gets deployed¶
The platform consists of:
- a hub: shared networking, security, governance and monitoring that every workload uses, and
- spokes: one subscription per workload and spoke environment, pre-wired to the hub.
Everything is deployed with Terraform, orchestrated by a PowerShell module (src/Xebia.Mcp), and run from Azure DevOps
pipelines or GitHub Actions workflows.
Management group structure¶
Tenant root
└── <organization management group> (management_group_organization_id)
└── cloudplatform
├── platform
│ ├── platform_management → Management subscription (Log Analytics, Automation, Key Vault, storage)
│ ├── platform_connectivity → Connectivity subscription (Virtual WAN, hub, Azure Firewall, gateways, DNS, App Gateway)
│ └── platform_identity → Identity subscription (identity virtual network)
├── landingzones
│ ├── landingzones_corp → corp spoke subscriptions (+ optional child groups)
│ └── landingzones_online → online spoke subscriptions (+ optional child groups)
├── shared (+ optional child groups)
├── sandbox
└── decommissioned
The meaning of each level is explained in Architecture reference.
The tree is created by infra/templates/management_groups/main.tf. Names and IDs are set in
<env-folder>/<primary region>/foundation.auto.tfvars (management_group_structure and the
mgmtgrp_structure_under_customer_area_* maps).
Network¶
- Virtual WAN with a virtual hub per region. The Virtual WAN is always created in the primary region; every region gets its own virtual hub. Azure Firewall is deployed into the virtual hub, which makes it a secured virtual hub.
- Spokes connect with a virtual hub connection, not with classic VNet peering. With
internet_security_enabled = true(the default), spoke internet traffic goes through the hub firewall. - Firewall rules and IP groups are JSON files in the configuration repository, deployed by the
firewalllayer. See Firewall rule organisation for how to structure them. - Private DNS: central private DNS zones for Private Endpoints and a Private DNS Resolver. An Azure Policy initiative registers Private Endpoint DNS records in the central zones automatically. See DNS design.
- Optional: site-to-site VPN, point-to-site VPN, ExpressRoute, Application Gateway with WAF, Azure Bastion, DDoS protection, Azure Virtual Network Manager with IPAM. All switches are in the feature flags reference.
Governance¶
- Built-in compliance initiatives at the
cloudplatformmanagement group: ISO 27001:2013, CIS 1.4.0, CIS 3.0.0, Microsoft cloud security benchmark and NIS2 (postdeploymentlayer). - Custom policy definitions and initiatives from JSON in the configuration repository (
foundationlayer). - Policy assignments and exemptions from JSON in the configuration repository (
postdeploymentlayer). - Microsoft Defender for Cloud for the platform subscriptions, and optionally for spokes.
Monitoring¶
- Action groups (
ag-<company>-mgmtgeneral-…andag-<company>-mgmtxpirit-…), created by thefoundationlayer. - Azure Monitor Baseline Alerts (AMBA): five initiatives (Service Health, Management, Connectivity, Identity, Landing
Zone) assigned per management group by the
postdeploymentlayer. - Alert processing rules route platform alerts to the action groups.
- Optional hub 24/7 routing (
hub_247_support) sends selected hub alerts to a webhook outside office hours (ADR-0007).
Spokes¶
For each spoke JSON file, the spoke pipeline creates or updates, in the spoke subscription:
- subscription tags, name and management group placement
- resource groups, virtual networks, subnets, NSGs and route tables
- the virtual hub connection
- a Log Analytics workspace, Key Vault and storage account (on by default)
- Entra ID groups (Owners, Contributors, Readers, Support) with PIM settings
- a service principal with role assignments
- optionally an Azure DevOps repository and service connection
Deployment tooling¶
The pipelines run inside a container image (mcp) on self-hosted runners in the JobRunner subscription from
mcp.json: a virtual network with NAT gateway, an Azure Container Registry, a Managed DevOps Pool, a Container Apps
environment and a DNS resolver. Terraform state is stored in the storage accounts in mcp.json
(Azure.TerraformState.Hub and .Spoke).
How it works, in one picture¶
flowchart LR
subgraph code["xms-cloud-platform-code (one for all customers)"]
tf["Terraform layers + modules"]
mod["Xebia.Mcp PowerShell module"]
tpl["Pipeline templates"]
end
subgraph cfg["Configuration repository (one per customer)"]
json["mcp.json, tfvars, JSON"]
entry["Entry pipelines (pinned version)"]
end
entry -- "checks out release/vX.Y" --> tpl
tpl --> mod
mod -- "terraform plan / apply" --> tf
json -- "values" --> mod
mod --> azure["Azure: canary, then prod"]
Why it is built this way¶
| Design choice | Reason |
|---|---|
| Code repository separate from configuration repository | One shared, versioned codebase serves many tenants. Each tenant keeps only its own values. |
| Layers instead of one Terraform root | Smaller blast radius, faster plans, a clear dependency order. See Deployment layers. |
| JSON files for rules, policies and spokes | People who don't write Terraform can review changes. Schemas and config tests validate files before deployment. |
| Logic in a PowerShell module, thin YAML | Azure DevOps and GitHub Actions behave the same, and every step can run locally (ADR-0011). |
| Canary platform environment | Every code or config change is proven on a full copy of the hub before it reaches production. |
See Platform philosophy for the principles behind these choices. The service types (core, direct, additional) and the spoke variants are described in Service types.