◆ SHIPPEDInfrastructure automation

96 Azure Local redeployments in two weeks — because the order never slipped.

A redeployment is a long chain of destructive steps that have to happen in the right order, from Azure prerequisites and BitLocker to OS install, storage reattach, VM import, and HA roles. This is the PowerShell console that kept that order — first version in about a day, hardened over the program. It previewed the risky steps, tracked every site, and logged commands by site. That discipline, not any one clever script, is how one engineer cleared 96 sites.

PowerShell 7Azure
Hyper-VFailover ClusteringBitLockerUnattended ISOAudit trail
96
Azure Local sites redeployed, one engineer
~7
redeployments a day, averaged over two weeks
36
tracked tasks, prep to final workload check
12
sites in flight per batch
redeploy-console
// The problem

The risk wasn't any single step. It was losing the order.

Redeploying an Azure Local site is not one action — it's a long, branching runbook: Azure prerequisites, pre-migration checks, BitLocker, cluster shutdown, OS install, post-deploy validation, storage reattachment, VM import, HA role creation, VM version upgrades, and a final workload health check. The steps are destructive and order-dependent. BitLocker comes off before the rebuild and goes back on only once storage and VMs are back; HA roles can't exist until the VMs they protect are imported. Swap two steps and you find out on production hardware.

Doing that once carefully is fine. Doing it 96 times in two weeks, alone, with a dozen sites in flight, is where humans drop steps. The real failure mode wasn't "I don't know the command" — it was "which site am I on, what did I already run here, and is it safe to run the next thing yet?" The console exists to answer exactly those three questions, every time.

// How it works

A guided runbook, not a folder of scripts.

Right order, every site, and a log to prove it.

01 SOURCE
One SiteData.csv is the source of truth: management IP, hostname, cluster name, and BMC address for every site. The console reads it, generates each site's artifacts, creates the Azure prerequisites, and keeps per-site status so progress survives restarts.
02 ORDER
Numbered actions are grouped in the real deployment sequence, and 36 tracked tasks run from prep to the final workload check. What's next for a site is on the screen, not in someone's head.
03 PREVIEW
Destructive and remote actions support -WhatIf and confirmation prompts, so a step can be dry-run against a site before it touches live hardware. Cleanup only removes artifacts the console generated.
04 LOG
Commands run through a logger that records what ran, on which site, and when, in a per-site tracker and run folder. The log is the audit trail and the resume point.
05 CHECKPOINT
Steps only a human can do — virtual media through the BMC, an Azure portal action, a backup — are manual checkpoints you mark complete, tracked beside the automated work so a site's status never overstates what's done.
// What was hard

Three problems between one site and ninety-six.

None of them was a missing cmdlet.

A dozen sites in flight, one operator. Batches ran twelve sites at once — ninety-six is eight waves of twelve. A 12-site dashboard showed each one by stage, with current and planned work, attention items, and the latest evidence. That removed the decision tax. A site that failed mid-run was resumable, not a forensic exercise, and a slow one could wait without me re-deriving its state from memory or scrollback. The throughput came from the ordering and the log, not the scripts they wrap.

Every node had to start identical. Hand-built installs drift, and drift at site sixty is a mystery. An unattended ISO stages the deployment payload, site data, post-deploy scripts, and NIC drivers. After install, a day-0 helper sets up local admin access, installs the drivers, prepares the Configurator values, validates the management IP source preference, and writes logs for later review. Every node started from the same place, with receipts.

A DNS check that lived in 1601. In August, the console's recurring post-deploy checks moved into The Forge as governed commands, and the port caught one that had been confidently wrong. Static DNS records carry an aging timestamp of 0, which CIM renders as the year 1601, shifted into local time. The console only treated null, "0", or MinValue as static, so it reported every static record as dynamic. The Forge version treats anything before 1602 as static, keeps the raw values, and returns Warning when it can't verify. The migration sequence itself stayed in the console, on purpose: it's operator-paced work, not a job for a queue.

◆ Built fast, then hardened

It started scrappy: a working first version in about a day, pointed at one job — keep a solo operator on-rails across many high-risk migrations. By the end it was 15 core scripts and about 8,400 lines of PowerShell. No client data here, and the console graphic above is illustrative. Want the ordering-and-audit pattern walked through for your own migration work? Ask me about it →