Skip to content

Config: What Configuration Do We Have

Each customer has one configuration repository. It holds only values; the Terraform lives in the code repository. There are two kinds of configuration:

Hub Spoke
What it configures The shared platform: management groups, network, firewall, policies, monitoring One workload landing zone per file
Format *.auto.tfvars + JSON (firewall, IP groups, App Gateway, WAF, policies) One JSON file per spoke
Pipeline hub spoke
Read more Hub config Spoke config

How to change either: Making a change safely (PR + CI + CD). The most frequent changes are listed under Common requests.

Reference pages for spokes: Spoke JSON reference, Spoke common settings, RBAC and PIM for spokes and Spoke integrations.

Conventions and review: Pull request review, Firewall rule and IP group guidelines and, if you run the optional module, Managed VM module.

Layout of a configuration repository

<config-repo>/
├── environments/
│   ├── canary/                         ← <env-folder> for canary
│   │   ├── mcp.json                    Platform environment settings
│   │   ├── global.auto.tfvars          Regions, environment names, platform subscription IDs
│   │   ├── <layer>.auto.tfvars         Optional: values for one layer in every region
│   │   ├── <region>/                   One folder per active region, e.g. gwc/
│   │   │   ├── global.auto.tfvars      Region name and short name
│   │   │   ├── foundation.auto.tfvars
│   │   │   ├── connectivity.auto.tfvars
│   │   │   ├── firewall.auto.tfvars
│   │   │   ├── identity.auto.tfvars
│   │   │   ├── management.auto.tfvars
│   │   │   ├── postdeployment.auto.tfvars
│   │   │   ├── firewall_rule_configuration/*.json
│   │   │   ├── ip_groups/*.json
│   │   │   ├── application_gateways/*.json
│   │   │   └── web_application_firewall_policies/*.json
│   │   ├── firewall_rule_configuration/*.json       Optional: global rule collection groups (all regions)
│   │   ├── ip_groups/*.json                         Optional: global IP groups (all regions)
│   │   ├── policies/                                Definitions, set definitions, assignments, exemptions
│   │   └── spokes/<team>/*.json                     Spoke definitions
│   └── prod/                           ← same structure
├── pipelines/                          Azure DevOps entry pipelines + variables.yaml
└── .github/workflows/                  GitHub Actions entry workflows

Two names are fixed by the code:

  • mcp.json. Its parent folder is the <env-folder>. The folder name becomes the environment name used in state keys (for example canary).
  • spokes/ directly under the <env-folder>.

All other folder names are set by *_relative_path / *_file_location variables in the tfvars.

mcp.json

Read by src/Xebia.Mcp/Functions/Public/Config/Resolve-XebiaMcpConfiguration.ps1. Keys marked required are validated; GUID fields must be valid GUIDs.

Key Required Purpose
CustomerName yes Display name of the tenant/customer.
Azure.TenantId yes (GUID) Entra ID tenant.
Azure.TerraformState.Hub.{SubscriptionId, ResourceGroupName, StorageAccountName, ContainerName} yes Backend for all hub layers. The storage account must already exist.
Azure.TerraformState.Hub.ServicePrincipalId for GitHub, SPN login Client ID used for Azure login (GitHub OIDC) and SPN login.
Azure.TerraformState.Hub.KeyvaultName optional Key Vault that stores SPN credentials (update-spn).
Azure.TerraformState.Spoke.* yes (same fields) Backend and identity for spoke deployments.
Azure.JobRunner.{SubscriptionId, ResourceGroupName, Location} yes Where the job runners are deployed.
Azure.JobRunner.MaximumAgents, .Sku, .IpAddressPrefixes.* optional Pool size, VM size and address ranges of the runner network.
AzureDevops.OrganizationName, .AgentPool yes, unless a GitHub section is present Azure DevOps organization and Managed DevOps Pool. The container pool is <AgentPool>-CTR.
AzureDevops.ContainerImageName optional Images the container pipeline builds (normally ["mcp"]).
GitHub.{AppId, AppInstallationId, AppPrivateKeySecretName} for GitHub runners GitHub App used to register self-hosted runners.
SpokeCommon.tenantinfo for spokes AdministrativeUnitId, GroupnamePrefix (e.g. SG_SPOKE_), SPNNamePrefix (e.g. SPN-SPOKE-), SubsciptionNamingTemplate (the misspelling is the real key), SPNSecretrotationPeriod (days).
SpokeCommon.shared_services for spoke extensions Subscription and per-region resource IDs of shared APIM, ACR, Service Bus, Event Hub.

The other spoke-wide settings (regions, IPAM pools, PIM profiles, GitHub instance) are described in Spoke common settings.

How relative paths in tfvars are resolved

Every layer receives -var config_path=<env-folder>, the folder that holds mcp.json. Terraform builds file paths as "${var.config_path}/${var.<x>_relative_path}". Write relative paths from the <env-folder>:

# environments/prod/gwc/firewall.auto.tfvars
firewall_rule_collection_file_location = "gwc/firewall_rule_configuration"   # correct
ip_group_json_file_relative_path       = "gwc/ip_groups/"                    # correct

ip_group_json_file_relative_path       = "environments/prod/gwc/ip_groups/"  # wrong: resolves to environments/prod/environments/prod/…

A wrong path deletes resources

If a folder path is wrong, fileset() finds no files and does not fail. The plan then wants to delete every resource that came from that folder (rule collection groups, IP groups, policy assignments). Always read the plan summary before you apply. The reference configuration repository xms-cloud-platform-config currently uses repository-root paths; see Known issues.

Which tfvars files a layer reads

For layer <layer> in region <region>, the plan passes these var files in this order and skips files that don't exist (Invoke-XebiaHubPlanAction.ps1):

  1. <env-folder>/<region>/<layer>.auto.tfvars
  2. <env-folder>/<region>/global.auto.tfvars
  3. <env-folder>/<layer>.auto.tfvars
  4. <env-folder>/global.auto.tfvars

When a variable is set in more than one file, Terraform uses the value from the file passed last. Put each variable in exactly one file.

An undeclared variable in a tfvars file is only a warning, so a typo is silently ignored. Check names against infra/deploy/<layer>/variables*.tf.