| name | run-privatelink-demo |
| description | Drive the live five-beat Private Link demo for DATACON 2026, "Why Azure Is the Best Cloud for SQL" — Demo 3, the control plane is the security perimeter (Entra-only Azure SQL Hyperscale, a client VM whose managed identity is the server admin, and the private DNS zone that nobody links). USE WHEN the presenter says any of: "let's run demo 3", "start the private link demo", "run the control plane demo", "let's do the five beats", "walk me through demo 3", "next beat", "rehearse the private link demo", "pre-flight demo 3", "reset the demo", "is demo 3 ready", "what do I say at beat 4", "the VM query still works, is that right", or wants step-by-step help presenting the Private Link arc. Scope is RUN / REHEARSE / RESET the five beats ONLY. Do NOT use to provision, rename or re-region the environment (that's build-privatelink-demo), to run the Adventure Works SQL MCP demos, or to run the Ward General hyperscale demos — those are separate. |
Run the Private Link demo (Demo 3)
Read presentations/dataconus2026/whyazurebest/demos/demo3_controlplane/README.md
first. It holds the narration, the measured findings and the Learn quotes. Drive
from it; do not invent beats.
Kit: presentations/dataconus2026/whyazurebest/demos/demo3_controlplane/.
This skill assumes the environment already exists.
What the demo proves
A perimeter around a PaaS database is a control-plane property, not a firewall
exercise. Five az commands take the database from publicly addressable to
reachable only from inside the VNet — no firewall rule deleted, no VPN, no code
change, no redeploy, no new connection string.
Then it lands the most common Private Link failure in production: endpoint right,
zone right, A record right, still resolves public — because nobody linked the zone
to the VNet.
Pre-flight — always, before the first beat
cd presentations/dataconus2026/whyazurebest/demos/demo3_controlplane
.\reset-demo.ps1 # rewinds beats 2, 3, 5; repoints RDP at the current public IP
.\05-verify.ps1 # must print 17/17 PASS, "Demo is in its Beat 1 state"
mstsc .\demo3.rdp # sign in as demoadmin
Run scripts synchronously (isBackground: false, timeout: 0) and check the
exit code — 05-verify.ps1 exits 1 if anything is out of place.
A new venue means a new egress IP, which breaks RDP. reset-demo.ps1 calls
allow-me.ps1 for you; run it standalone if only RDP is broken.
reset-demo.ps1 re-enables public access first, and that order is load-bearing.
With public access disabled, any attempt to add or edit a firewall rule is denied
(Error 42101).
Two windows, and they never swap
Laptop terminal = control plane. RDP session = client. That split is the
point. Say so at the start and keep both on screen the whole time.
The five beats
| # | Window | Run | Expected |
|---|
| 1 | VM | .\query.ps1 | Works over the public endpoint |
| 2 | Laptop | .\beat-2-add-privatelink.ps1 | PE + zone + A record. No VNet link |
| 2 | VM | .\query.ps1 | Still works — adding Private Link broke nothing |
| 3 | Laptop | .\beat-3-lockdown.ps1 | publicNetworkAccess=Disabled, every firewall rule left in place |
| 4 | VM | .\query.ps1 -Expect Fail | Blocked — public access denied |
| 4 | VM | .\dns.ps1 | Resolves a public address — the zone isn't linked |
| 5 | Laptop | .\beat-5-dns-link.ps1 | VNet link created |
| 5 | VM | .\dns.ps1 | Resolves 10.42.1.4 |
| 5 | VM | .\query.ps1 | Works again, privately |
Drive one beat at a time: say which beat, give the command, wait for the result,
confirm it matches, then ask before moving on.
Narration the presenter should not miss
- Beat 1 —
query.ps1 prints SUSER_SNAME(), so the audience sees the managed
identity connect. No password exists anywhere on this server; SQL auth was off
from birth. The 0.0.0.0 "Allow Azure services" rule is deliberately on — it
is the anti-pattern the demo exists to kill.
- Beat 2 — the payoff is that nothing happens. Adding a private endpoint is
additive and non-breaking.
- Beat 3 — read the firewall rules on screen. Every one survives. The rule bought
nothing; disabling public access did all the work.
- Beat 4 — the big one.
dns.ps1 shows public DNS already CNAMEs the server to
its privatelink name. The VM is asking for exactly the right thing; it just has
no private zone to answer with. That is Learn's number-one cause, live.
- Beat 4 — say this out loud:
Test-NetConnection -Port 1433 reports
TcpTestSucceeded : True even with public access disabled. The TCP handshake to
the gateway still completes; rejection happens at login. Nobody should use
Test-NetConnection as proof a database is locked down.
- "But I can still resolve the name" — yes. Resource existence is enumerable;
resource access is not. Beat 4 is the proof.
Do not read numbers off the slide
The private address (10.42.1.4) and the topology are real and pinned. The
public-side addresses are deliberately not — the gateway varies by region and
round-robins between rehearsals. Read the tail of the CNAME chain off the screen.
Likewise, do not quote a specific login error number; read the message that appears.
Recovery
| Symptom | Do this |
|---|
Beat 5 dns.ps1 still shows a public IP | Run it once more. The link takes seconds to propagate; Clear-DnsClientCache is already in the script |
| Beat 4 shows a private IP | The VNet link already existed — Beat 2 must not create it. Run reset-demo.ps1; 05-verify.ps1 fails if it finds a link |
| Beat 2 says "already exists" but the A-record list is empty | It created nothing. Verify by side effect, not by the message |
| RDP won't connect | .\allow-me.ps1 |
| VM unreachable but ARM works | Invoke-VmScript from 00-config.ps1 goes through ARM, not the VNet |
| VM query fails against a server that doesn't exist | vmkit/config.ps1 drifted from 00-config.ps1. Fix both, .\push-vmkit.ps1 |
Edited anything under vmkit/? Re-push it. The VM runs its own copy in C:\demo.
Between rehearsals, and when you finish
.\reset-demo.ps1
.\05-verify.ps1
Always leave the environment at Beat 1, 17/17 PASS. Never walk away with
publicNetworkAccess=Disabled still set — the next pre-flight will fail with
Error 42101 before it can fix itself.
Deallocate the VM between rehearsals. Serverless Hyperscale bills per second and
does not auto-pause; that's General Purpose only.