Skip to content

Build Environments standard

Standard scope

Applies to: All HCS-governed repos, across all scopes — build hosts invoked directly, not CI-registered runners Domain: build-environments Status: Active

This standard defines how HCS projects provision and use build environments — hosts you invoke directly, by name, outside of CI dispatch. It does not cover the registered CI runner fleet (GitHub Actions and GitLab CI self-hosted agents); that fleet is governed by CI Runners. See that standard's "Runner vs. build environment" section for the distinction if you're unsure which one applies to your case.


Build environment priority order

Always use the highest-priority available option. Do not reach for a Windows VM when the work runs fine on Linux.

PriorityEnvironmentWhen to useCost
1 — WSL (local VM)Ubuntu 22.04 on the platform VMLinux builds, scripts, containers — whenever the work is not Windows-onlyFree
2 — bld-01 (nested Hyper-V)Windows Server 2025, 10.10.10.10, this host onlyWindows-only work: WinPE ISOs, PowerShell modules, ADK buildsFree (shared)
3 — Quick-deploy Windows server (fallback)Ephemeral Windows VMTier 2 unavailable (off-host session or bld-01 unusable); Windows-only work onlyVM cost while running
4 — AWS EC2 MacActive on demand in ProdTechiOS/macOS development and buildsSee EC2 Mac runbook

Decision rule: if the work does not explicitly require Windows APIs, ADK, COM objects, or Apple tooling, it runs on Tier 1. Reserve Tier 2 and Tier 3 for true Windows-only builds. Tier 4 is the Apple-only path and is provisioned only for approved work windows.


Tier 1 — WSL (Ubuntu 22.04 on the platform VM)

Use directly on the platform VM host. No VM startup, no cost.

powershell
# Run a Linux build directly from PowerShell on the host
wsl --distribution Ubuntu-22.04 -- bash -c "cd /mnt/d/git/myrepo && make build"

Works for: bash scripts, make, cargo, go build, docker buildx, npm/pnpm, Python, az CLI.


Tier 2 — bld-01 (nested Hyper-V Windows build box)

Access details

PropertyValue
VM namebld-01
OSWindows Server 2025 Standard (Desktop Experience)
vCPU4
RAMDynamic 2–16 GB
VHDXD:\Data\VMs\bld-01.vhdx
Switchbeacon-nat (Internal NAT, 10.10.10.0/24)
IP10.10.10.10 (static)
CredentialsAdministrator / kv-hcs-vault-01hcs-bld-vm-password
Host onlyThis specific platform VM (nested Hyper-V)

Power management is required. bld-01 is powered off when idle. Every build script must power it on, run the build, and restore the original power state. Use the standard wrapper below.

When to use Tier 2

  • WinPE ISO generation (Windows ADK)
  • PowerShell modules that use Windows-only APIs
  • .NET Framework (not .NET 8+ Core) builds
  • COM/WMI/Windows-specific testing

For anything that works on Linux, use Tier 1 instead.

Standard build wrapper

The power-on/run/power-off wrapper is a template script, not something to inline in each repo's own scripts: D:\git\platform\templates\scripts\Invoke-BuildOnVm.ps1. It verifies bld-01 exists (deploying it if not), records its power state, powers it on and waits for WinRM if needed, runs the supplied script block or file over PS Remoting, then restores the original power state.

powershell
# Copy the template into your repo, then call it:
.\scripts\Invoke-BuildOnVm.ps1 -ScriptBlock { cd C:\build\myrepo; git pull; .\build.ps1 }
.\scripts\Invoke-BuildOnVm.ps1 -ScriptFile .\scripts\build.ps1

Tier 3 — Quick-deploy Windows build server (fallback)

When bld-01 is unavailable (off-host session, VHDX corruption, different machine), deploy an ephemeral Windows VM:

PropertyValue
OSWindows Server 2025
CredentialsAdministrator / kv-hcs-vault-01hcs-winbuild-vm-password
Deploy scriptD:\git\platform\.ado\cicd\Deploy-CicdPlatform.ps1 -DeployWindowsRunner
LifecycleEphemeral — deallocate or delete after the build completes
powershell
# Deploy the fallback VM and run a build
D:\git\platform\.ado\cicd\Deploy-CicdPlatform.ps1 -DeployWindowsRunner -ScriptFile scripts\build.ps1

Tier 4 — AWS EC2 Mac (active on demand)

The reusable foundation and the first ProdTech M4 host are deployed. This tier provides an interactive Xcode environment and can optionally become a trusted registered CI runner. The full host lifecycle, cost rules, and runner trust boundary live in CI Runners.

Access follows this chain:

  1. The operator signs in through AWS IAM Identity Center with temporary credentials.
  2. AWS Systems Manager establishes SSH or Screen Sharing tunnels; no inbound management port is exposed publicly.
  3. The EC2 Mac uses an IAM instance profile for AWS API access.
  4. Apple signing material is retrieved from the scope's approved vault only when required and is never baked into an AMI.

The SSO directory Region is not the Mac deployment Region. Deployment automation must choose the Mac Region and Availability Zone from live offerings, quotas, capacity, and price.

For the deployed ProdTech host, connection commands, the IAM Identity Center assignment, current Region, RDM setup, Key Vault secret names, lifecycle controls, and cost boundaries are in the AWS EC2 Mac build-host runbook. Agents connected to the HCS Governance MCP can retrieve the same material with get_guidance(topic="aws-ec2-mac") or guidance://aws-ec2-mac.


Why this standard exists

Azure VM run-command (V1 and V2) has critical limitations for build workloads:

  • 90-minute hard timeout — any build longer than 90 minutes is killed
  • Extension deadlocks — only one run-command can execute at a time; a stuck extension can block a VM for hours
  • No shell access — without RDP or WinRM in the network security group, there is no escape hatch when a command hangs
  • Provisioning state coupling — a VM stuck in Updating blocks all ARM (Azure Resource Manager) operations on it

Local Hyper-V (Tier 2) eliminates all of these. WSL (Tier 1) eliminates them at zero cost for Linux builds.


Architecture overview

Platform VM (Azure WS2025)
├── WSL (Tier 1) — Ubuntu 22.04, Linux-native builds, no startup cost
├── Hyper-V (Tier 2)
│   └── bld-01 — Windows Server 2025, WinPE/ADK/PS modules
│       NAT switch: beacon-nat  10.10.10.0/24 → host .1, guest .10
└── Windows fallback VM (Tier 3 — ephemeral, deployed on demand only)

AWS (Tier 4 — active on demand)
└── EC2 Mac M4 in us-west-2 (family and Region revalidated before allocation)
    └── SSM-tunneled SSH and Screen Sharing; no public inbound rules

Deploying bld-01 (first time or recovery)

The deployment script: D:\git\platform\scripts\build\Deploy-BuildVm.ps1

Steps:

  1. Creates the beacon-nat internal switch (idempotent)
  2. Creates the NAT rule (idempotent)
  3. Creates a dynamic 127 GB VHDX at D:\Data\VMs\bld-01.vhdx
  4. Applies the WS 2025 WIM (index 2) from D:\Data\images\
  5. Injects a valid Unattend.xml (≤15-char computer name, admin credentials, WinRM)
  6. Creates the Gen 2 Hyper-V VM
  7. Boots and waits for OOBE (Out-of-Box Experience) to complete

One-time cost: 20–40 minutes on first deploy.

Prerequisites

RequirementCheck
Hyper-V installedGet-WindowsFeature Hyper-V
WS 2025 ISO presentD:\Data\images\en-us_windows_server_2025_*.iso
D:\Data\VMs\ directoryCreated by deploy script
kv-hcs-vault-01 accessibleaz keyvault secret show ...

Critical: Windows computer names must be ≤ 15 characters (NetBIOS limit). If the name is longer, Setup silently rejects the Unattend.xml and OOBE blocks indefinitely.


Build tool matrix

ToolTier 1 WSLTier 2 bld-01 (Windows)
WinPE ISO (ADK)NoYes
PowerShell modulesNoYes
.NET / C#YesYes
Node.js / TypeScriptYesYes
Azure BicepYesYes
TerraformYesYes
Docker imagesYesYes (nested)
Linux-only toolsYesNo
Bash scriptsYesNo

For build tools running on the CI runner fleet (GitHub Actions / GitLab CI agents), see CI Runners.


Repo integration guide (Tier 2 only)

Only repos with genuine Windows-only builds need these steps. Most repos never touch a build environment directly — their CI runs on the runner fleet (see CI Runners).

1. Add the wrapper script

powershell
# In your repo: scripts/Invoke-BuildOnVm.ps1
# Copy from: D:\git\platform\templates\scripts\Invoke-BuildOnVm.ps1

2. Reference in CI/CD

ADO pipeline:

yaml
- task: PowerShell@2
  displayName: Build on bld-01
  inputs:
    filePath: scripts/Invoke-BuildOnVm.ps1
    arguments: -ScriptFile scripts/build.ps1
    pwsh: true

GitHub Actions (calling out to bld-01 from a step running on the platform VM):

yaml
- name: Build on bld-01
  shell: pwsh
  run: scripts/Invoke-BuildOnVm.ps1 -ScriptFile scripts/build.ps1

3. Write your build script

Your repo's scripts/build.ps1 runs inside bld-01. It can assume:

  • PowerShell 7 at C:\Program Files\PowerShell\7\pwsh.exe
  • Git at C:\Program Files\Git\cmd\git.exe
  • Azure CLI at az
  • Windows ADK + WinPE at C:\Program Files (x86)\Windows Kits\10\
  • .NET SDK at C:\dotnet\

MCP integration

This standard is served by the HCS Governance MCP (Model Context Protocol):

Tool: get_standard
Domain: build-environments

Any AI client connected to the MCP can retrieve this document for build-host selection guidance. For runner-label guidance, the same client should call get_standard(domain="ci-runners") instead.


  • Scripting — PowerShell 7 conventions used in build scripts
  • Automation — pipeline-first principle; pipeline design lives there, compute selection does not
  • CI Runners — the registered CI runner fleet (GitHub Actions / GitLab CI self-hosted agents); use this instead of this standard when the question is "what runner label goes in my YAML"
  • Infrastructure — IaC for build VM provisioning
  • Key Vault — build credentials belong in the consuming scope's assigned vault; kv-hcs-vault-01 is only the HCS-scope default

Checklist: onboarding a repo's build environment

This checklist covers Tier 2 (bld-01) onboarding only. For registering a repo with the CI runner fleet (GitHub Actions / GitLab CI labels and tags), use the checklist in CI Runners instead — that's a separate step from anything below.

  • [ ] Copy Invoke-BuildOnVm.ps1 to the repo's scripts/ folder
  • [ ] Write scripts/build.ps1 with repo-specific build steps
  • [ ] Add a CLAUDE.md entry: "Windows builds run on bld-01 via Invoke-BuildOnVm.ps1"
  • [ ] Add kv-hcs-vault-01 access to the repo's CI service principal
  • [ ] Validate that bld-01 has all tools the build needs
  • [ ] Test: Invoke-BuildOnVm -ScriptBlock { "UP:$env:COMPUTERNAME" }

Copyright © Hybrid Cloud Solutions LLC — Kristopher Turner