A hands-on workshop that walks through the day-to-day work of a platform team on Azure: extending a centralized network, onboarding a new workload, wiring up secure identity-based access, and automating deploys with GitHub Actions — all using Infrastructure as Code and GitHub Copilot.
By the end of the workshop you will have, with GitHub Copilot doing the heavy lifting:
- Stood up a mock landing zone (hub VNet) and onboarded a workload spoke in a hub-and-spoke topology.
- Read an unfamiliar app, designed its Azure footprint, and implemented it as Bicep using Azure Verified Modules, with private endpoints, managed identities, and the distributed Private DNS pattern.
- Split the deployment into test and prod environments with isolated spokes and zone-redundant prod.
- Published the work to your own GitHub repo and wired up two staged GitHub Actions pipelines — one for infra, one for the app — using OIDC and environment-gated approvals.
- Proved both pipelines end-to-end with trivial, observable changes.
- README.md — this file. Goal and prerequisites.
- CHORES.md — the chores, as an index of per-chore pages under chores/. Short, requirements-only. Work through them in order with Copilot.
- Each chore page links to a matching
chores/details-NN.mdwith hints, expected outcomes, and background. Open the detail page when you get stuck or want to check your work.
-
An Azure subscription with Owner permissions.
-
Signed in via Azure CLI with the target subscription selected:
az login az account set --subscription <subscription-id>
-
A Git client — on Windows install Git for Windows:
winget install --id Git.Git -e --source winget
macOS:
brew install git(or use the Xcode Command Line Tools). Linux: installgitfrom your distro's package manager. -
A GitHub Copilot license. Agent mode is used throughout the chores.
-
MCP servers enabled in VS Code. This repo ships the configuration for you: when you open the folder, VS Code reads .vscode/mcp.json and prompts you to start the Microsoft Learn MCP server, and reads .vscode/extensions.json and prompts you to install the Bicep extension (which provides the second MCP server). The chat modes and skills in this repo rely on both being available.
MCP server How it gets registered Why this workshop needs it Microsoft Learn MCP ( microsoft-learn)HTTP server in .vscode/mcp.json(no install)Grounds the azure-principal-architect,bicep-plan, andazure-verified-modules-bicepmodes in current Microsoft documentation viamicrosoft_docs_search/microsoft_docs_fetch.Bicep MCP ( bicepschema/bicep_m_*)Ships with the Bicep VS Code extension recommended in .vscode/extensions.jsonUsed by the Bicep agents to pull live resource schemas, AVM module versions, and Bicep best practices when authoring IaC. After accepting the prompts, open MCP: List Servers in VS Code and confirm
microsoft-learnand the Bicep server are both Running. If Copilot's tool list doesn't showmicrosoft_docs_searchandbicepschema, fix the MCP setup before starting the chores. -
PowerShell 7+ (deployment scripts use
#requires -Version 7.0). -
winget install --id GitHub.cli -e
macOS:
brew install gh. Linux: see the official instructions. -
winget install --id JGraph.Draw -e --accept-source-agreements --accept-package-agreements
macOS:
brew install --cask drawio. Linux: see the releases page.
- A GitHub account that lets you create a new repository (personal account, or corporate GHEC with
Create repositoryrights). Sign in to GitHub from VS Code or viagh auth login.
Clone the workshop repository to your local machine and open it in VS Code:
git clone https://github.com/azureholic/az-platform-engineering-workshop.git
cd az-platform-engineering-workshop
code .In a real environment, the baseline for this workshop would be a full Azure Landing Zone deployment — either the Enterprise-scale Accelerator (ALZ), the SMB landing zone, or the SMB Ready Foundation. These accelerators provision a full management-group hierarchy, policy baseline, identity, connectivity, and management subscriptions.
Standing all of that up takes time we don't have. Instead, we simulate the centralized "platform" side of a landing zone by deploying a minimal hub VNet into a single subscription. The hub stands in for the connectivity subscription, so the workload-onboarding patterns we practice (peering, private endpoints, Private DNS) are the same ones you'd use in production.
Design choices locked in up front:
- Single subscription. All resources — platform and workload — live in one subscription. In a real ALZ they'd be split across a connectivity subscription and one or more landing-zone subscriptions.
- Distributed Private DNS. Private DNS zones for Private Link are deployed and linked per workload, not centrally in the hub. This is the distributed Private DNS pattern — simpler for a workshop and a valid ALZ option.
- Hub-and-spoke topology. Workload VNets are spokes that peer to the hub.
A hub VNet (vnet-hub, 192.168.100.0/24) with placeholder subnets — no gateway or firewall is actually deployed, the subnets exist so the topology is realistic:
| Subnet | Range | Purpose |
|---|---|---|
AzureFirewallSubnet |
192.168.100.0/26 |
Reserved for Azure Firewall (/26 minimum) |
GatewaySubnet |
192.168.100.64/27 |
Reserved for VPN / ExpressRoute gateway |
snet-shared-services |
192.168.100.96/27 |
Shared platform services (DNS forwarders, jumpbox, etc.) |
192.168.100.128/25 is left free for growth. No central Private DNS zones — each workload owns its own under the distributed pattern.
The "platform" side of the landing zone is simulated by a single hub VNet in mock-alz/. Deploy it before starting Chore 1:
cd mock-alz
./Deploy-Hub.ps1 # optional: -Location westeurope -ResourceGroupName rg-platformThis creates rg-platform with a hub VNet (vnet-hub, 192.168.100.0/24) and placeholder subnets for firewall, gateway, and shared services. It is the "given" for every chore. Do not edit or delete it.
Each chore in CHORES.md is meant to be solved with Copilot in agent mode, not by copy-pasting a finished solution. The loop is always the same: share the chore as the prompt, let Copilot draft IaC and scripts using the MCP servers, review the diff, deploy, verify, iterate. If you get stuck or want to check your work, open the matching chores/details-NN.md page for hints, expected outcomes, and background.
If you don't know how to approach a chore, ask Copilot to help you! It is an execelent troubleshooter and can run any CLI command for you to achieve your goals. Be explicit in your instructions.
Two non-negotiables that come from .github/copilot-instructions.md:
- Never modify application source under
workload-app/— the platform team treats the app code as immutable. The one exception is container build assets (Dockerfile,.dockerignore, nginx config, entrypoint scripts), which live next to the service they build (e.g.workload-app/backend/HotelBooking.Api/Dockerfile,workload-app/frontend/Dockerfile,workload-app/frontend/nginx/default.conf) and are platform-team property. Chore 13 requires a one-line edit to actual application source insideworkload-app/; you make that by hand in the editor — Copilot is not allowed to touch app source. - All container base images come from
mcr.microsoft.com, not Docker Hub.