Skill v1.0.0
currentTrusted Publisher100/100version: "1.0.0" name: build-privatelink-demo description: >- Provision, rename, re-region, verify or tear down the Azure environment for DATACON 2026 Demo 3 — "the control plane is the security perimeter" (Private Link + Entra-only Azure SQL Hyperscale + a client VM whose managed identity is the server admin). USE WHEN the presenter says any of: "build the private link demo", "provision demo 3", "set up the control plane demo", "deploy the privatelink demo on this machine", "rebuild demo 3", "move demo 3 to another region", "rename the demo 3 server", "the VM got deallocated, redeploy it", "run pre-flight for demo 3", "why is 05-verify failing", "tear down demo 3", or moves this talk to a new laptop and needs the VNet + VM + Entra-only server + demo kit in place. Scope is PROVISION + CONFIGURE + VERIFY + TEARDOWN ONLY. Do NOT use to present or rehearse the five beats (that's run-privatelink-demo), to build the Adventure Works / SQL MCP kit, or to deploy the Ward General hyperscale talk — those are separate.
Build the Private Link (Demo 3) environment
Read `presentations/dataconus2026/whyazurebest/demos/demo3_controlplane/README.md` first, every time. It is the source of truth and it is current. This skill covers only what the README does not: the order to work in and the traps that have already cost a rebuild.
All paths are workspace-relative to the bwsql root. Kit: presentations/dataconus2026/whyazurebest/demos/demo3_controlplane/.
The shape of it
- Control plane runs on the laptop, `az` only. The laptop never opens a SQL
connection — it cannot, by design.
- Data plane runs inside the VM, `sqlsim` only. The VM never runs
az. az vm run-command invokebridges the two through ARM. It keeps working even
after publicNetworkAccess=Disabled, so it is also the way back in if RDP breaks.
Running the scripts
- Always synchronous,
isBackground: false,timeout: 0. Provisioning steps
run minutes. Never background them, never poll.
- Check the exit code.
05-verify.ps1exits 1 when the demo is not at Beat 1. - Every step is idempotent. If step 4 fails, fix it and run
.\setup-all.ps1 -From 4. - If the terminal starts mangling chained commands, switch to
execution_subagent.
First build on a new machine
cd presentations/dataconus2026/whyazurebest/demos/demo3_controlplane.\setup-all.ps1
Steps 1–6: network → VM → RDP rule → Entra-only server + Hyperscale DB → ODBC prerequisites → pre-flight.
Two things need a human:
- Step 2 prompts for the VM local administrator password. The presenter types
it straight into the terminal. Never collect it with a question tool, never echo it, never put it in a script or a variable you print.
- `sqlsim.exe` must be dragged over RDP into
C:\demo— too big for
run-command. setup-all.ps1 pauses and prints the exact source path. Push the other three files first with .\push-vmkit.ps1, then finish with .\setup-all.ps1 -From 6.
Done when 05-verify.ps1 prints 17/17 PASS and "Demo is in its Beat 1 state".
Renaming or moving to another region
Both are edits to 00-config.ps1 — and to `vmkit/config.ps1`.
push-vmkit.ps1 copies vmkit/config.ps1 verbatim; it is not generated from 00-config.ps1. The FQDN and database name are therefore written down twice and nothing enforces that they match.
This has already broken the demo once. After a re-region the VM-side file still named the deleted server. Every laptop script and all 17 checks passed while query.ps1 on the VM pointed at a server that no longer existed.
Order of work:
- Validate the target region has capacity for
Standard_E2s_v5before touching
anything — az vm list-skus --location <region> --size Standard_E2s_v5 and check restrictions is empty, plus vCPU quota for the ESv5 family.
- Edit
00-config.ps1($Location,$ServerName,$DatabaseName). - Edit
vmkit/config.ps1to match. .\99-teardown.ps1 -Confirm, then.\setup-all.ps1..\push-vmkit.ps1, then verify by side effect:
``powershell . .\00-config.ps1 Invoke-VmScript "Get-Content 'C:\demo\config.ps1' | Select-String 'ServerFqdn|DatabaseName'" ``
Logical server names are globally unique DNS labels — a rename can collide.
Traps that are already settled — do not rediscover them
| Trap | The rule | |
|---|---|---|
--external-admin-sid for a managed identity | Application (client) ID, never the object ID. The object ID creates cleanly and then every login fails. Never write an appId→objectId fallback | |
| Entra admin assignment | Must happen at server create time. az sql server ad-admin create has no --principal-type, so it cannot declare Application | |
| ODBC Driver 18 returns 1603 | Install VC++ redist first. The MSI log says Error 1723. Never diagnose an MSI from its exit code | |
az ... dns-zone-group show on a missing group | Returns exit 0 and `{}`. Invoke-Az maps a property-less object to $null — keep that fix. Verify existence checks by side effect, not by the message | |
az vm run-command --scripts with a multi-line string | Silently emits nothing. Use Invoke-VmScript, which writes a temp .ps1 and passes @file | |
Read-Host in an agent-driven terminal | Returns empty. 99-teardown.ps1 asks for the database name, so it aborts with exit 1 and deletes nothing. The presenter must run teardown themselves |
Teardown
.\99-teardown.ps1 -Confirm
Scoped on purpose: VM, NIC, disk, public IP, private endpoint, DNS zone and link, logical server, database, VNet, nsg-demo3-client, demo3.rdp. It never deletes the resource group — bwsqlestaterg is shared and Demo 1 counts resources in it. Tearing down drops Demo 1's estate by one logical server and one database, so re-check that workbook if the recording matters.
Azure policy injects NRMS-* NSGs alongside the VNet. Teardown does not remove them and they survive as orphans (subnets: null, networkInterfaces: null). They are harmless. Confirm they are unassociated before proposing deletion, and ask first — the resource group is shared.
Before you change shared state
Provisioning is fine to do. Do not, without asking:
- re-run a beat script that locks the server down, or leave
publicNetworkAccess=Disabled behind after an investigation
- delete anything in
bwsqlestatergthat this kit did not create - re-measure by mutating the live environment when the presenter is rehearsing
Leave the environment at Beat 1, 17/17 PASS.