Skip to content

Spoke Config

Intro: a spoke is the product of the hub

The hub exists to host spokes. A spoke is what a consumer team actually receives: a subscription that is placed, tagged, connected and secured. Everything in the hub — firewall, DNS, policies, monitoring — is there so that spokes work safely. For the consumer's view, see What you get. For what a spoke deployment creates and how its service principal and groups behave, see Spoke canvas.

One spoke = one JSON file = one workload in one spoke environment (dev, tst, acc, prd, shared).

Structure

Spoke files live under <env-folder>/spokes/, in a sub-folder (the default glob **/*.json needs a /):

environments/prod/spokes/
└── data_platform/
    ├── canvas_nonprod_data_platform_dev.json
    └── canvas_prod_data_platform_shrd.json

Rules:

  • Use a sub-folder per team or workload.
  • Use a unique file name across all sub-folders. The file name (without .json) is used in job and artifact names.
  • Use a pattern such as canvas_<workload>_<environment>.json, so spokeGlob is easy to write.
  • If you run the separate spoke canvas pipeline, it also expects the name to start with canvas_, end with .json and contain _prod_ or _nonprod_.
  • The spoke's identity is its state key spoke_<workloadShortName>_<environment>.tfstate, not its file name. Renaming or moving the file is safe; changing workloadShortName or environment creates a new spoke.

JSON content

Schema: src/Xebia.Mcp/Files/json/spokeschema.json (draft-07). Top-level blocks:

Block Holds
workloadShortName, environment Identity of the spoke (max 6 characters; never change after creation)
azureSubscriptionId, azureSubscriptionName, managementGroupId Which existing subscription, its name, and where it is placed
tags Required: owner, department, workload, pii, costcentre, environment
adInfo Team groups (ProductTeamGroupNames) and PIM behavior (PIMGroupConfig)
targetRegions primary and additional regions
resourceconfiguration.network[] VNets, subnets, NSGs, route tables, hub connection
resourceconfiguration.{storageaccount, keyvault, loganalytics, networkwatcher, privatedns} Optional resources (storage, Key Vault and Log Analytics are on by default)
azdo Optional Azure DevOps repo and service connection (Spoke integrations)
extensions Optional use of shared APIM, ACR, Service Bus, Event Hub, and the GitHub repository extension (Spoke integrations)

Every field: Spoke JSON reference. A complete minimal example: New spoke.

Spoke-wide defaults (group and SPN name prefixes, subscription naming template, administrative unit, SPN secret rotation) come from mcp.json → SpokeCommon.tenantinfo. In the separate spoke canvas deployment they are in a common.tf file, together with the regions, IPAM pools and PIM profiles. All of them are described in Spoke common settings.

Page Content
Spoke JSON reference Every field of the spoke file, including address space from an IPAM pool
Spoke common settings Settings shared by all spokes (common.tf and SpokeCommon)
RBAC and PIM for spokes Groups, PIM profiles and the role map for Key Vault and Storage data-plane roles
Spoke integrations Azure DevOps repository and service connection, GitHub repository and federated credentials

Preparing the pipelines, identities and integrations for spokes: Prepare the spoke pipeline and Prepare spoke integrations.

How to change or add a spoke

flowchart LR
    r["Request<br/>(consumer)"] --> b[Branch] --> e["Add / edit<br/>spoke JSON"] --> pr["PR + review"]
    pr --> ci["CI: spoke run in Plan<br/>spokeGlob = one file"] --> cd["CD: apply<br/>dev → tst → acc → prd"] --> m[Merge]
I want to… Runbook
Add a spoke New spoke
Change a spoke (subnets, NSG rules, access, tags, regions, …) Change spoke
Remove a spoke Delete spoke

Always run spoke with spokeGlob set to one file. A broad glob plans every spoke, and any spoke whose environment is not in the pipeline's environments list (for example shared/shrd) fails the run.

Spoke pipeline in the reference repository

spoke.yaml in xms-cloud-platform-config uses whatIf: true: the spoke canvas is only planned. Set whatIf: false (or expose it) when you want to apply. See Pipelines.