Provisioning¶
How the code provisions a platform environment, from the build infrastructure to the last layer. The order is described in the platform deploy flow; this page explains each part.
Infra: the job runners¶
Step by step, including the state storage account and the service connections: Prepare pipelines and agents.
The infrastructure pipeline (stage-infrastructure.yaml → Invoke-XebiaInfrastructure) deploys the build
infrastructure from src/Xebia.Mcp/Files/bicep/deploy/initialize/main.bicep into the subscription and resource group in
mcp.json → Azure.JobRunner:
| Resource | Configured by (mcp.json) |
|---|---|
| Virtual network, subnets and NAT gateway | Azure.JobRunner.IpAddressPrefixes (Vnet, AgentSubnet, PepSubnet, CaeSubnet, DnsResolverSubnet) |
| Azure Container Registry | – |
Managed DevOps Pool <AgentPool> |
AzureDevops.AgentPool, Azure.JobRunner.MaximumAgents, Azure.JobRunner.Sku |
Container Apps environment (for container jobs, pool <AgentPool>-CTR) |
– |
| DNS resolver | – |
It also creates the blob container in the hub Terraform state storage account
(Initialize-XebiaMcpTerraformStateContainer). The storage account itself, the spoke state container and the
deployment service principals are prerequisites; no pipeline creates them.
In the reference configuration repository, infrastructure.yaml is CI-triggered on changes to itself or
pipelines/variables.yaml, and runs canary and prod with sequential locking.
Container: the mcp image¶
The container pipeline (Invoke-XebiaContainer -Action Prepare|Build) builds src/Dockerfile from the pinned code
version and pushes it to the job runner ACR. The image contains PowerShell 7, Terraform, the Azure CLI, the Xebia.Mcp
module and the hub Terraform (copied from infra/ at build time). Image names come from
AzureDevops.ContainerImageName (normally ["mcp"]).
Tags are set by Get-XebiaMcpContainerTags.ps1; see Releasing. The entry
pipelines run on imageRef: mcp:latest.
Layers, per environment and region¶
The hub pipeline provisions the hub layers for one platform environment (one mcp.json) per stage:
- Environment. The platform environment name is the name of the folder that holds
mcp.json, for examplecanaryorprod. It is used in the state key and is passed to Terraform asconfig_path. -
Regions. The region matrix is built from
<env-folder>/global.auto.tfvars(Get-XebiaRegion.ps1,Set-XebiaRegionMatrix.ps1):primary_region = "weu" active_regions = ["weu", "neu"] active_regions_names = ["westeurope", "northeurope"]primary_regionmust be inactive_regions. Level 1 runs in the primary region, level 2 in the other regions, level 3 in all regions, and level 4 (postdeployment) in the primary region only. 3. Layers. For every layer and region, the module plans Terraform fromterraform/cloud-platform/deploy/<layer>with the var files of that region and environment, and stores state in<env>-<region>-<layer>.tfstate. See Deployment layers.
flowchart TB
mcp["environments/prod/mcp.json"] --> env["Environment = prod<br/>config_path = environments/prod"]
env --> g["global.auto.tfvars<br/>primary_region, active_regions"]
g --> r1["weu (primary)"]
g --> r2["neu"]
r1 --> l1["foundation, connectivity (level 1)"]
r2 --> l2["foundation, connectivity (level 2)"]
r1 --> l3["firewall, identity, management (level 3)"]
r2 --> l3
r1 --> l4["postdeployment (level 4)"]
Spokes¶
The spoke pipeline provisions spokes with the Terraform in src/Xebia.Mcp/Files/terraform/spoke-canvas/:
| Step | Terraform root | State key |
|---|---|---|
| Subscription | deploy/subscription |
<wl>_<env>_Subscription.tfstate |
| Spoke canvas | deploy/spoke |
spoke_<wl>_<env>.tfstate |
<wl> and <env> are the lower-cased workloadShortName and environment of the spoke JSON. See
Spoke config.
The subscription step comes first, so that the spoke canvas always has a subscription to work in. It is a separate step on purpose, instead of a pool of free subscriptions: see Subscription provisioning. What the canvas step creates is described in Spoke canvas; its Terraform inputs and outputs are in the module reference.
Local provisioning¶
dev/dev.ps1 runs the same functions locally, in the dev container, against MCP_CONFIG (default
config/Demo/canary/mcp.json). WhatIf is on unless you pass -SkipWhatIf.
pwsh ./dev/dev.ps1 -Action Test
pwsh ./dev/dev.ps1 -Action Infrastructure -SkipWhatIf
pwsh ./dev/dev.ps1 -Action Container -SkipTests
For hub layers and spokes, see Run a single layer locally;
the dev.ps1 hub and spoke actions don't work on main yet (Known issues).