[azure]2023-06-18~9 min read

Moving VMware to Azure Without Losing Your Mind

I recently led a migration from an on-premises VMware/VxRail environment to Microsoft Azure. The environment ran business-critical ERP, dozens of application servers, file services, domain controllers — the kind of infrastructure that had grown organically over a decade, with all the undocumented dependencies that implies.

Here's what the project actually looked like, month by month.

Month 1-2: Discovery and the landing zone decision

The standard playbook says "start with Azure Migrate." I ran it. It discovered servers, estimated sizing, flagged dependencies. Useful as a starting point, but it didn't tell me what I really needed to know: which workloads were tightly coupled, which services would break under latency, and which servers were still running because nobody had been brave enough to turn them off.

I spent the first month doing what Azure Migrate couldn't: walking through the environment with the people who built it. The sysadmin who'd been there 15 years knew which scheduled tasks depended on which file shares. The DBA knew which databases could tolerate replication lag and which couldn't. The ERP support team knew which integrations would need reconfiguration, which was especially critical given the number of legacy EDI pipelines that nobody had documented since the mid-2010s. This information isn't in any discovery tool. You have to talk to people.

Parallel to discovery: the landing zone design. This is the foundational architecture decision that every other decision builds on, and once it's set, changing it is expensive. I chose a hub-spoke model with subscription segmentation: management, connectivity, identity in the hub; production, non-production, and shared services in separate spokes. Each spoke got its own VNet with controlled peering back to the hub.

Why hub-spoke? Because it gave us centralized control over ingress/egress through Azure Firewall in the hub, while keeping workload isolation in the spokes. It also simplified cost attribution — each spoke maps to a cost center, and the Azure bill becomes legible to finance. I've seen organizations pick more complex topologies and regret it within six months. Simple architecture ages better.

The RBAC design took almost as long as the networking. We didn't want the on-prem pattern of "domain admins can do everything" carried into Azure. I designed a layered model: subscription owners, resource group contributors, workload readers. Each role mapped to an actual team responsibility. Nobody gets Owner unless they truly need it. Nobody gets Contributor to production without a change control process.

Month 3-4: Hybrid coexistence and the networking problem

We couldn't migrate everything at once. ERP and related business-critical systems needed phased cutover with validation windows between each phase. So we ran hybrid — VMware on-prem and Azure side by side — for several months.

This was the hardest part of the project.

The networking challenges were relentless. Latency between on-prem and Azure introduced subtle timing issues in applications that had been developed assuming sub-millisecond connectivity. DNS resolution became a split-brain problem: which DNS server is authoritative for which zone? We had to carefully manage conditional forwarding so that Azure-resident services resolved through Azure DNS and on-prem services resolved through the existing domain controllers. A few legacy applications had hardcoded IP addresses. Finding them required packet captures and a lot of patience.

We set up a Site-to-Site VPN between the VxRail environment and Azure, then migrated to ExpressRoute for production traffic. The VPN was fine for testing but not for ERP workloads — the latency variance was too high. ExpressRoute gave us consistent sub-5ms latency, which was tight enough for the ERP to function normally.

One surprising challenge: backup and disaster recovery during the hybrid phase. We had on-prem backup infrastructure (Veeam) that wasn't designed for Azure workloads. We had Azure Backup for cloud workloads that didn't cover on-prem. During the transition, we were essentially running two parallel DR strategies, with manual coordination between them. I redesigned the backup architecture mid-project — Azure Backup for all migrated workloads, with a defined cutover date for each application. On that date, the on-prem backup stopped and the cloud backup took over. No gap, no overlap confusion.

Month 5-6: Migration execution

We ran the migrations in waves, ordered by dependency and risk.

Wave 1: Non-production and dev/test. The lowest-risk workloads first. This let us validate the process, iron out the migration runbooks, and build confidence. A few surprises: a dev server that was actually running a production integration nobody knew about. A test environment that the QA team considered critical but hadn't been documented as such. These are the things you discover when you actually try to turn servers off.

Wave 2: File services and domain controllers. Moving file servers meant migrating terabytes of data with minimal downtime. We used Azure File Sync to stage data in advance, then cut over with a brief DNS change. Users barely noticed. Domain controllers were trickier — we deployed new DCs in Azure, let them replicate, then demoted the on-prem ones. The key was patience: let replication fully converge before making changes. Rushing this step causes lingering replication issues that are painful to troubleshoot.

Wave 3: Application servers. These were the bulk of the migration — dozens of application servers with varying levels of complexity. For each server, we followed the same pattern: assess dependencies, replicate to Azure, test in isolation, cut over with a coordinated DNS change, validate, monitor for 48 hours. The process was repetitive but necessary. Rushing any step introduced risk that would cascade to the ERP wave.

Wave 4: ERP and business-critical systems. This was the high-stakes wave. Downtime wasn't an option. We used Azure Migrate for the initial replication, let it sync for a week, then scheduled a 4-hour maintenance window for the final cutover. The window was generous because we knew from the earlier waves that things always take longer than estimated. The ERP cutover itself went smoothly — the work was in the preparation, not the execution.

What Azure Migrate did and didn't do

Azure Migrate is good at discovery and assessment. It gives you server inventory, dependency maps, sizing recommendations. What it doesn't do: tell you which dependencies are real and which are artifacts of years of accumulated configuration. It flagged 200+ dependencies between servers. Half of them were irrelevant — old connections to decommissioned services, test integrations, monitoring agents that hadn't reported in years. You can't automate away the need to understand your environment.

It also doesn't handle the operational side. Who is responsible for each migrated workload? What's the incident response process when something breaks in Azure vs. on-prem? How does patching change when servers are in the cloud? These aren't migration tool questions, but they're what determines whether the migration is successful in the long term.

The hidden work

Project plans show migrations as linear: discover, assess, migrate, validate. Reality is messier. Here's the work that didn't appear on any slide deck:

  • Dependency mapping by hand. Walking through each server with its owner and asking "what does this talk to?" produced a more accurate map than any automated tool. Took two full weeks. Worth every hour.

  • Legacy EDI analysis. The ERP environment had EDI integrations going back years — purchase orders, invoices, shipping notifications flowing between trading partners through aging translation software running on old Windows servers. Each pipeline had its own communication protocol, its own schedule, its own failure mode. Mapping these took me the better part of three weeks, and I'm fairly sure I still didn't find all of them until the dry run surfaced the last few holdouts that had been quietly running since 2012.

  • The "we forgot about that server" list. Every migration discovers servers that nobody knew existed. We found four. One was running a legacy license server that three applications depended on. Another was a print server for a warehouse that was still operational. Each discovery required pausing the migration plan, assessing impact, and adjusting the schedule.

  • Post-migration cleanup. After the last server migrated, the real optimization work began. Right-sizing VMs that were over-provisioned (Azure Migrate's recommendations were conservative — I cut 20-30% from most estimates after a month of performance data). Cleaning up orphaned NICs, disks, and public IPs. Tuning backup schedules for cloud-native storage. This was three weeks of work after "the migration is complete."

  • Decommissioning the old environment. You can't just turn off VMware and walk away. There are contracts, licensing, hardware disposal, data sanitization. This had its own project plan and took two months after the last workload moved.

What I'd do differently next time

Start the operational handoff earlier. We treated operations as a post-migration activity. In hindsight, the operations team should have been involved from month one, building their Azure skills while the architecture was being designed. By the time workloads were migrating, they should have already been comfortable with the portal, the monitoring tools, and the incident response process. Instead, we had a compressed training period that stressed everyone.

Budget more time for dependency discovery. Two weeks was not enough. I'd allocate a month next time, with dedicated sessions for each application owner. The time spent upfront on dependency mapping pays back tenfold during migration by avoiding surprises.

Build cost governance into the architecture, not after. We designed a solid cost model — subscription per cost center, clear tagging strategy, budget alerts. But we didn't enforce it rigidly enough during migration, and the first month's Azure bill had some surprises: a few VMs that were over-provisioned, some data egress charges we hadn't anticipated, a forgotten Premium SSD that was billing hundreds per month for a workload that needed Standard. Cost governance needs to be operational from day one, not retrofitted.

Accept that something will fail. No migration goes perfectly. The goal isn't to prevent every failure — it's to have enough monitoring, enough contingency planning, and enough operational discipline that failures are detected quickly and fixed without drama. The dry run is the most important part of the plan. If you're not finding issues during the dry run, you're not testing hard enough.

The migration completed on schedule, the ERP stayed up, and nobody lost data. By the standard metrics, it was a success. But the real measure isn't whether the servers moved — it's whether the environment is more manageable, more observable, and more cost-effective than what it replaced. On those counts, the answer is yes. The on-prem VMware environment was a black box. The Azure environment has dashboards, alerts, cost visibility, and clear operational boundaries. That's the win.

[azure][migration][virtualization]