Legacy to Azure, the fast path
A phased plan for moving a classic thick-client (Windows or Java) front end and SQL Server backend onto Azure — React SPA, Functions, App Service, API Management, Logic Apps, Data Factory, and Azure SQL Managed Instance — sequenced for the shortest realistic timeline without skipping the governance and cutover steps that prevent a failed go-live.
Service map — legacy component to Azure target
The decision that removes the most back-and-forth. Pick these defaults up front and only deviate where a phase below flags a real reason to.
| Legacy piece | Azure target | Why it's the fast path |
|---|---|---|
| Windows/Java thick-client UI | Static Web Apps | Hosts the React SPA with managed CI/CD, free SSL, and PR-based staging built in — no server to provision. |
| Business logic, background jobs | Azure Functions | Pay-per-execution, fastest to stand up per operation; no app-server capacity planning. |
| Core session/stateful services | App Service | For logic that needs to run continuously or longer than a Function's execution limit. |
| Single API surface for the SPA | API Management | One gateway in front of Functions + App Service; handles auth, throttling, versioning. |
| SQL Server database | SQL Managed Instance | Near-total surface-area compatibility with on-prem SQL Server — usually zero schema rewrites. |
| Nightly batch / SQL Agent jobs | Data Factory | Purpose-built for scheduled data movement and transformation at scale. |
| Notifications, approvals, SaaS hooks | Logic Apps | Connector-rich, built for system-to-system workflow rather than bulk data. |
| Files, documents, exports | Blob Storage | Cheap, durable, and the natural Logic Apps / Data Factory trigger source. |
| Windows Integrated Auth | Microsoft Entra ID | SSO for the SPA (MSAL) and token validation at the API Management layer. |
Discover & assess →
Week 1 · blocks every later phase
Before anything moves, inventory what actually exists: where the business logic lives (in the Windows/Java client, in stored procedures, or both), the real size and complexity of the SQL Server schema, every integration point (file shares, email, third-party APIs, printers, scanners), and the current auth model.
- Run Azure Migrate against the current environment for a dependency map and rough sizing.
- Run the Data Migration Assistant (DMA) against the SQL Server instance for a compatibility report — this determines the database target in Phase 2.
- Choose a cutover pattern. For a thick client being replaced piece by piece, the strangler-fig approach — new APIs and new UI stood up alongside the legacy app, traffic cut over feature by feature — is lower-risk and faster to a first production release than a big-bang rewrite.
- Write down the target architecture and a rough phase order (this document is that order).
Don't wait for a full application-portfolio assessment if there's only one app. A half-day of DMA + Migrate output plus a whiteboard session with whoever knows the app best is enough to start Phase 1 in parallel with the rest of this phase.
Week 1 · everything else deploys into this
This is the one phase worth doing in the right order, because every later resource inherits its identity, network, and policy setup from here.
- Create the subscription(s) — at minimum split non-production and production; add a management-group hierarchy only once there's more than one application sharing the environment.
- Set up identity: an Entra ID admin security group, a dedicated break-glass account separate from Global Admin, and MFA enforced through Entra ID Security Defaults.
- Create resource groups per environment with a naming convention and required tags (CostCenter, Environment, Owner) enforced by Azure Policy.
- Stand up networking: one VNet with subnets for the app tier, data tier, and integration services. NSGs between subnets.
- Turn on Cost Management budgets and alerts before anything else deploys, not after the first surprise bill.
Skip full hub-and-spoke networking and Azure Firewall unless multiple applications or sites will share connectivity — that adds real fixed cost (firewall + VPN gateway run roughly $1,000+/month) and setup time this project doesn't need yet. A single VNet with subnets and NSGs is enough to reach production safely; add the hub later if a second workload arrives.
Database migration →
Weeks 2–4 · runs parallel with Phase 3
The backend stays SQL Server — the only real decision is which Azure hosting model, and it's driven almost entirely by what the DMA compatibility report from Phase 0 turned up.
| Target | App changes needed | Best when |
|---|---|---|
| SQL Managed Instance | Little to none | The default choice — near-full compatibility (SQL Agent, cross-DB queries, linked servers, CLR) with minimal rewrite. |
| SQL Server on Azure VM | None (IaaS) | DMA flags OS-level dependencies (COM+, filesystem jobs) that MI can't host. |
| Azure SQL Database | Moderate to significant | Only if the schema is already being redesigned — not a fast-path option for a lift. |
- Provision SQL Managed Instance inside the data subnet from Phase 1.
- Configure Azure Database Migration Service (DMS) in online mode — it replicates continuously so the cutover window is minutes, not a weekend.
- Let replication run in parallel with development on Phases 3–5; this is what makes the timeline compress instead of stacking sequentially.
- Cut over during a defined low-traffic window, validate row counts and key transactions, then stop replication.
Managed Instance is the default for a reason: it's the only one of the three that usually needs zero connection-string or query rewrites, which removes an entire category of testing from the critical path.
API & middle tier →
Weeks 2–5 · parallel
This is where the Windows/Java client's business logic gets extracted into services the new React frontend — and the still-running legacy client, during the transition — can both call.
- Azure Functions for discrete, stateless business rules and background/queue-triggered work — cheapest and fastest to deploy one at a time.
- App Service (Web API) for anything that needs to run continuously, hold more state, or exceed a Function's execution timeout.
- API Management in front of both as the single gateway the frontend and any remaining legacy callers talk to — one base URL, Entra ID token validation, throttling, and versioning in one place.
Don't build out APIM policies before there's anything to gateway. Stand up the first two or three Functions/endpoints directly, add API Management once there's a real surface to unify — it's a config layer, not a prerequisite.
React frontend →
Weeks 3–6 · parallel
React is the right call here — largest ecosystem, first-class Azure tooling, and it's what most teams can hire for. Build it against the real APIs from Phase 3 as they come online rather than mocking them; contract mismatches surface immediately instead of at integration time.
- Scaffold the SPA with Vite + TypeScript.
- Deploy to Azure Static Web Apps — GitHub/DevOps CI/CD, managed SSL, and a free staging environment per pull request come out of the box.
- Wire up MSAL against Entra ID to replace Windows Integrated Auth (use Azure AD B2C instead if any users are external/consumer, not staff).
Reserve App Service for the frontend only if server-side rendering or custom Node middleware is genuinely required. For a typical internal-app SPA, Static Web Apps reaches production faster and with less to operate.
Automation layer →
Weeks 4–7 · parallel
Data Factory and Logic Apps get confused for each other constantly — the split that keeps both cheap and maintainable is data-in-motion versus systems-in-motion.
| Use | For |
|---|---|
| Data Factory | Bulk data copy, scheduled ETL/ELT, keeping legacy and new SQL Server in sync during parallel run, anything that was a SQL Agent job. |
| Logic Apps | Business workflow: Blob-arrival triggers, email/SMS/Teams notifications, third-party SaaS calls, human-approval steps, retry/error wrappers around Function calls. |
Layer Azure Storage underneath both: Blob for documents and exports (with lifecycle policies so old files age out automatically), Queue Storage to decouple Functions from bursty workloads, and Table Storage only if there's a clearly unstructured, high-volume key-value dataset — most apps don't need it.
Don't rebuild every legacy scheduled job as a Logic App out of habit. If a job's only role is moving or reshaping rows, it belongs in Data Factory — it's built for that and costs less at volume.
Security & ops hardening →
Weeks 5–7 · parallel, not an afterthought
- Managed identities on every Function and App Service so they reach SQL MI, Storage, and Key Vault without a stored secret anywhere.
- Key Vault holding every connection string and API key, referenced by app settings rather than hard-coded.
- Private endpoints for SQL MI and Storage if the data is sensitive enough to warrant it.
- Application Insights + Log Analytics wired into every Function, App Service, and Logic App as it's built — retrofitting observability after cutover is the most common regret on projects like this.
CI/CD & infrastructure as code →
Start in Week 2, runs throughout
Start this early, not after Phase 1 is "done" — the landing zone itself should be the first thing captured as code, so every environment after it is a repeat of the same deployment instead of a new manual setup.
- Bicep for infrastructure — native to Azure and the fastest to pick up for a team without existing multi-cloud Terraform investment.
- Azure DevOps or GitHub Actions pipelines per component: API, SPA, Functions, and the IaC templates themselves.
- Separate dev/test/prod from the pipeline definitions, not by hand-editing resources per environment.
Weeks 6–8 · closes the project
- Run the legacy app and the new Azure stack side by side, with Data Factory keeping both databases in sync.
- UAT against real users and a load test on the new API layer before committing to a date.
- Cut traffic over via DNS or Azure Front Door, with a defined rollback trigger and window agreed in advance — not improvised at 2am.
- After a stable burn-in period, decommission the on-prem servers and remove the sync pipelines from Phase 2.
Because Phases 2 through 7 run largely in parallel rather than in sequence, a typical mid-size app following this order reaches a production cutover in roughly six to eight weeks — the database and API/frontend tracks are usually the ones actually gating the date, not the automation or hardening work.
Sequence assumes the phases marked "parallel" are staffed to run concurrently — if one person is doing all of it, add the phase durations together instead of overlapping them.
Have a legacy platform to move to Azure?
We'll walk through this exact playbook against your actual environment — no account managers, no hand-offs.