Every datacenter exit starts the same way: someone opens a spreadsheet with 1,400 rows, and someone else says “we’ll just lift and shift.” Eighteen months later, half the estate is still on-prem and the spreadsheet has become sentient.
This is a field report from a migration that didn’t go that way — 140 production workloads moved in 11 months, zero rollback events. The difference wasn’t heroics. It was treating the migration as a factory, not a project, and putting an AI copilot inside the loop where humans were the bottleneck.
#The Factory Model
A migration factory has three stations. Every workload passes through all three, in order, with automated gates between them:
- Discover — dependency mapping, traffic baselining, data classification
- Replatform — codify the target state in Terraform, rehearse the cutover
- Cutover — DNS flip behind a health-checked gate, with automatic rollback armed
The rule that made it work: no workload enters a station until the previous station’s artifacts are machine-verified. No architecture diagram in Confluence counts. If it isn’t in the repo, it doesn’t exist.
#Station 1: Discovery, Where AI Earned Its Keep
Dependency mapping is where migrations die. Netflow data tells you that two servers talk; it doesn’t tell you why, or whether the dependency survives the move.
We fed three sources into an LLM-assisted triage pipeline:
- 90 days of flow logs, aggregated to (source, dest, port, volume) tuples
- Configuration management exports (packages, services, cron entries)
- Ticket history mentioning each hostname — five years of it
def classify_dependency(flow, cmdb_ctx, ticket_ctx):
"""LLM triage: label each observed flow as
HARD (breaks on move), SOFT (degrades), or GHOST (dead)."""
prompt = build_prompt(flow, cmdb_ctx, ticket_ctx)
verdict = llm.complete(prompt, schema=DependencyVerdict)
# Never trust, always verify: verdicts route to a human
# queue unless confidence > 0.9 AND corroborated by CMDB.
return verdict if verdict.corroborated else human_queue.put(verdict)
The result: 6,200 observed flows collapsed into 380 real dependencies, of which 41 were ghost dependencies — traffic to services that had been decommissioned but never unplugged. Humans reviewed every HARD verdict. The AI’s job was not to decide; it was to make the human queue 16x shorter.
Treat the model like a brilliant intern with no liability insurance: it drafts, you sign.
#Station 2: Replatforming as Code
Every target environment was expressed as a Terraform module with an identical contract:
module "workload" {
source = "git::ssh://git@internal/tf-modules//workload?ref=v4.2.1"
name = "billing-api"
tier = "critical" # drives HA topology + alerting
data_class = "pci" # drives encryption + network policy
cutover_window = "2026-03-14T02:00Z"
rollback_target = "dc1-vip-billing"
}
Two things matter here:
tieranddata_classare inputs, not documentation. They mechanically select subnet placement, KMS policy, and pager routing. Compliance stopped being a review meeting and became a plan-time failure.rollback_targetis mandatory. You cannot merge a workload module without declaring where traffic goes if the cutover gate fails. This single required field prevented every “we’ll figure out rollback later” conversation.
Each workload got a rehearsal cutover in a staging VPC: synthetic traffic replayed from production captures, gate evaluated, rollback fired on purpose. If the rollback path had never been exercised, the cutover didn’t get a calendar slot.
#Station 3: Cutover Night, Minus the Drama
The cutover itself was a state machine, not a runbook:
DRAIN → SYNC_FINAL → FLIP → OBSERVE(15m) → COMMIT | ROLLBACK
The OBSERVE gate watched four signals — error rate, p99 latency, saturation, and business KPI (orders/minute) — against a baseline captured the same weekday one week prior. Any breach auto-fired ROLLBACK. Humans could veto a rollback; they could not veto a breach going unexamined.
Out of 140 cutovers, the gate fired 9 times in rehearsal and twice in production. Both production fires were correct calls (a connection-pool exhaustion and a mis-sized cache). Both rolled back in under four minutes, and both workloads cut over cleanly the following week. That is what “zero rollback events” means in the honest accounting: zero unplanned, human-scrambled rollbacks. The machine rolled back twice, calmly, exactly as designed.
#What the AI Didn’t Do
Worth stating plainly, because the vendor decks won’t:
- It did not write the Terraform. Generated modules failed review often enough that we kept humans on authorship and used the model for diff explanation during review instead.
- It did not make cutover decisions. Gates were dumb thresholds on purpose — auditable, arguable, boring.
- It did not replace the discovery interviews. It made them shorter by arriving with hypotheses instead of blank pages.
#The Scorecard
| Metric | Spreadsheet era | Factory era |
|---|---|---|
| Workloads/month | 3–4 | 13 |
| Discovery effort/workload | ~40 hrs | ~6 hrs |
| Unplanned rollbacks | (nobody counted) | 0 |
| Run-rate delta | — | −40% |
The uncomfortable summary: the AI was maybe 20% of the win. The other 80% was refusing to migrate anything that wasn’t described in code, rehearsed in staging, and armed with a rollback path. That part has been available since before the transformers showed up. We just kept skipping it.
Next transmission: the org-design side of this migration — what happens to a 40-person infrastructure team when the datacenter it was built around disappears.