Modernization Playbook

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.

React + Vite Azure Static Web Apps Azure Functions App Service API Management Azure SQL Managed Instance Logic Apps Data Factory Blob / Queue Storage

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 pieceAzure targetWhy it's the fast path
Windows/Java thick-client UIStatic Web AppsHosts the React SPA with managed CI/CD, free SSL, and PR-based staging built in — no server to provision.
Business logic, background jobsAzure FunctionsPay-per-execution, fastest to stand up per operation; no app-server capacity planning.
Core session/stateful servicesApp ServiceFor logic that needs to run continuously or longer than a Function's execution limit.
Single API surface for the SPAAPI ManagementOne gateway in front of Functions + App Service; handles auth, throttling, versioning.
SQL Server databaseSQL Managed InstanceNear-total surface-area compatibility with on-prem SQL Server — usually zero schema rewrites.
Nightly batch / SQL Agent jobsData FactoryPurpose-built for scheduled data movement and transformation at scale.
Notifications, approvals, SaaS hooksLogic AppsConnector-rich, built for system-to-system workflow rather than bulk data.
Files, documents, exportsBlob StorageCheap, durable, and the natural Logic Apps / Data Factory trigger source.
Windows Integrated AuthMicrosoft Entra IDSSO for the SPA (MSAL) and token validation at the API Management layer.

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.

  1. Run Azure Migrate against the current environment for a dependency map and rough sizing.
  2. Run the Data Migration Assistant (DMA) against the SQL Server instance for a compatibility report — this determines the database target in Phase 2.
  3. 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.
  4. Write down the target architecture and a rough phase order (this document is that order).
Fast path

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.

  1. 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.
  2. 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.
  3. Create resource groups per environment with a naming convention and required tags (CostCenter, Environment, Owner) enforced by Azure Policy.
  4. Stand up networking: one VNet with subnets for the app tier, data tier, and integration services. NSGs between subnets.
  5. Turn on Cost Management budgets and alerts before anything else deploys, not after the first surprise bill.
Fast path

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.

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.

TargetApp changes neededBest when
SQL Managed InstanceLittle to noneThe default choice — near-full compatibility (SQL Agent, cross-DB queries, linked servers, CLR) with minimal rewrite.
SQL Server on Azure VMNone (IaaS)DMA flags OS-level dependencies (COM+, filesystem jobs) that MI can't host.
Azure SQL DatabaseModerate to significantOnly if the schema is already being redesigned — not a fast-path option for a lift.
  1. Provision SQL Managed Instance inside the data subnet from Phase 1.
  2. Configure Azure Database Migration Service (DMS) in online mode — it replicates continuously so the cutover window is minutes, not a weekend.
  3. Let replication run in parallel with development on Phases 3–5; this is what makes the timeline compress instead of stacking sequentially.
  4. Cut over during a defined low-traffic window, validate row counts and key transactions, then stop replication.
Fast path

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.

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.
Fast path

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.

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.

  1. Scaffold the SPA with Vite + TypeScript.
  2. 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.
  3. Wire up MSAL against Entra ID to replace Windows Integrated Auth (use Azure AD B2C instead if any users are external/consumer, not staff).
Fast path

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.

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.

UseFor
Data FactoryBulk data copy, scheduled ETL/ELT, keeping legacy and new SQL Server in sync during parallel run, anything that was a SQL Agent job.
Logic AppsBusiness 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.

Fast path

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.

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.

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

  1. Run the legacy app and the new Azure stack side by side, with Data Factory keeping both databases in sync.
  2. UAT against real users and a load test on the new API layer before committing to a date.
  3. Cut traffic over via DNS or Azure Front Door, with a defined rollback trigger and window agreed in advance — not improvised at 2am.
  4. After a stable burn-in period, decommission the on-prem servers and remove the sync pipelines from Phase 2.
Fast path

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.