Skip to content

Validate Spoke Files in a Pull Request

Spoke files are flexible and have many fields. That makes a wrong configuration easy to write and expensive to find at deployment time. A validation pipeline checks every spoke file when a pull request is created or updated, and blocks the merge when a file is wrong. This page describes what such a pipeline checks, how to set it up in Azure DevOps and which parts of the JSON schema you customise per organisation.

Relation to the config tests

The platform pipelines run config tests before every deployment (Pipelines), and you can run them locally (Run the tests locally). The validation pipeline on this page is the same idea, earlier: it runs on the pull request, so the author and the reviewer see the problem before the merge. It is part of the spoke canvas of earlier versions. Check whether your code version ships it (Scripts folder of the code repository) before you set it up.

What it checks

The script reads all spoke files below the spoke folder and tests each one:

Test Result
The file is valid JSON Fails with the position of the error
The file complies with the spoke schema Fails with the file and the part of the configuration that is wrong
The isprivatelinksubnet setting is correct for every virtual network Fails when it is not
The file name matches the intended environment Fails when, for example, a production environment is in a non-production file name

Everything else in a spoke file is not checked and needs the manual review (Spoke pull request review). The pipeline output tells you which files failed and, on selecting a failed item, the issues in it.

Components

What Repository Remark
Validation script (a PowerShell script) Code repository Part of the platform. Do not change it in your copy.
Pipeline file Configuration repository A starter file exists. Adapt it.
spokeschema.json Configuration repository A starter file exists. Adapt it, see Customise the schema.
.vscode/settings.json Configuration repository Binds the schema to the spoke files in Visual Studio Code (Edit the JSON files with schema support).

Set up the pipeline

  1. Create a pipeline from the existing YAML file in the configuration repository. Name it, for example, VerifySpokeFiles.
  2. Run it once by hand and authorise the pipeline to use the variable group, the agent pool and the service connection. Scheduled and triggered runs cannot ask for that approval.
  3. Update the pipeline file (next section).
  4. Add the pipeline as build validation to the branch policy of main (below).

Pipeline file

The file has no trigger of its own (trigger: none). It checks out the configuration repository and the code repository at a version tag, and runs the script with these arguments. A short, anonymised excerpt:

trigger: none

variables:
  - group: <variable-group-of-the-spoke-deployment>

resources:
  repositories:
    - repository: spokedeployment-tf
      type: git
      name: "<project>/<code-repository>"
      ref: refs/tags/<version>          # update with every new code version

steps:
  - checkout: self
    path: s/spokedeployment-params
  - checkout: spokedeployment-tf
    path: s/spokedeployment-tf
  - task: AzurePowerShell@5
    displayName: "Verify SpokeFiles"
    inputs:
      azureSubscription: $(ServiceConnectionName)
      ScriptType: "FilePath"
      ScriptPath: $(System.DefaultWorkingDirectory)/spokedeployment-tf/Scripts/<validation-script>.ps1
      ScriptArguments: -SpokeFilePath <spoke-folder> -SpokeSchemaPath <schema-file> -ProdEnvironmentsList "acc,prd,prod" -NonProdEnvironmentsList "dev,tst,test" -ExludedFilenames ""
      pwsh: true

Update at least these:

Item What to do
Variable group Only when its name differs from the variable group of your spoke deployment.
ref of the code repository Set to the tag of the code version you run, and change it with every upgrade (What to do when a new version is released).
SpokeFilePath The folder with the spoke files. Normally leave the default of the starter file.
SpokeSchemaPath The path of your spokeschema.json. Normally leave the default.
ProdEnvironmentsList Comma-separated environments that count as production. Default in the starter file: acc,prd,prod.
NonProdEnvironmentsList Comma-separated environments that count as non-production. Default: dev,tst,test.
ExludedFilenames Comma-separated file names that are not verified. Normally empty. The parameter name is spelled like this in the script.

Require it on main

In Azure DevOps: Repos, Branches, the main branch, Branch policies, Build validation, add the pipeline. Then every pull request, and every new commit in it, starts the check, and a failed check blocks the approval. Set it as required.

Customise the schema

The starter spokeschema.json follows the general structure of the spoke JSON. Most organisations share the same structure and differ in a few allowed values. Update these three:

Property What it holds Customise
environment An enum with the allowed environment names The environments you use, for example dev, tst, acc, prd. Keep it in line with ProdEnvironmentsList and NonProdEnvironmentsList.
managementGroupId An enum with the management groups in which a spoke subscription may be placed The names of your landing zone management groups. They must exist in the hub configuration (Spoke JSON reference).
tags An object with the allowed tags, and a required list Your tag set. Put a tag in required when a missing tag must fail the check. See Naming and tags.

An excerpt, with placeholders:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Schema for spoke canvas deployment",
  "type": "object",
  "properties": {
    "workloadShortName": { "type": "string", "maxLength": 6 },
    "environment": { "type": "string", "enum": ["dev", "tst", "acc", "prd"] },
    "managementGroupId": {
      "type": "string",
      "enum": ["<prefix>-landingzone-corp-prd", "<prefix>-landingzone-online-prd"]
    },
    "tags": {
      "type": "object",
      "properties": { "owner": { "type": "string" }, "pii": { "type": "string", "enum": ["yes", "no"] } },
      "required": ["owner", "department", "workload", "pii", "costcentre", "environment"]
    }
  }
}

The full schema also describes adInfo (member lists, PIMGroupConfig with the values nopim, self-approve and approval, and the allowed SPNGraphPermissions), targetRegions, azdo and resourceconfiguration. Take the schema of your code version as the base and keep it current after each upgrade, because the schema must accept every field the Terraform reads (Known issues).

In the editor

Bind the schema to the spoke files in .vscode/settings.json, so authors see mistakes while they type: an issue is underlined and Ctrl+Space suggests values.

{
  "json.schemas": [
    { "fileMatch": ["/**/canvas_*.json"], "url": "./spokeschema.json" }
  ]
}

More on this in Edit the JSON files with schema support.