Why bother
Moving off IIS VMs removes OS patching, IIS configuration drift and manual scaling. App Service gives you slots, autoscale, managed certificates and built-in authentication. Most line-of-business sites move with few code changes if you check the blockers first.
1. Assess
Use the App Service migration assessment (Azure Migrate web app discovery or the App Service Migration Assistant) against your IIS servers. Then check these by hand:
| Blocker | App Service answer |
|---|---|
| Writes to local disk (uploads, logs) | Azure Storage (Blob) or mounted Azure Files; local disk is not persistent across instances |
| Windows authentication (Kerberos/NTLM) | Entra ID authentication (Easy Auth or MSAL in code) |
| GAC assemblies, COM components, MSI installers | Not supported — refactor or use Windows containers |
| Registry access, Windows services, scheduled tasks | WebJobs, Azure Functions or a separate worker |
| Connects to on-prem SQL or file shares | VNet integration + hybrid connectivity, or Hybrid Connections |
| Custom IIS modules / URL Rewrite rules | web.config rewrite rules work on Windows plans; native modules need containers |
2. Prepare the target
az appservice plan create -g rg-web -n asp-web-prod --sku P1v3 --is-linux false
az webapp create -g rg-web -p asp-web-prod -n app-intranet-prod --runtime "dotnet:8"
az webapp vnet-integration add -g rg-web -n app-intranet-prod --vnet vnet-spoke-web --subnet snet-appsvc-int
az webapp identity assign -g rg-web -n app-intranet-prod
For .NET Framework apps, use a Windows plan and pick the ASP.NET 4.8 runtime stack.
3. Move configuration out of the box
- Connection strings and app settings → App Service configuration with Key Vault references.
web.configtransforms → environment-specific app settings.- Machine keys (for forms auth / view state across instances) → set explicitly or move to Data Protection keys in Blob storage for ASP.NET Core.
4. Deploy to a staging slot
az webapp deployment slot create -g rg-web -n app-intranet-prod --slot staging
az webapp deploy -g rg-web -n app-intranet-prod --slot staging --src-path ./publish.zip --type zip
Test functionally against the real backends via VNet integration.
5. Certificates and domains
az webapp config hostname add -g rg-web --webapp-name app-intranet-prod --hostname intranet.contoso.com
az webapp config ssl create -g rg-web -n app-intranet-prod --hostname intranet.contoso.com # free managed cert
Use a Key Vault certificate instead for wildcard or EV certificates.
6. Cut over
- Lower DNS TTL in advance.
- Freeze content changes on the IIS server; copy final uploads to Blob.
- Swap staging to production, then switch DNS (or move the hostname in Front Door / Application Gateway).
- Keep the IIS VM stopped but intact for the rollback window.
Tip
Put Front Door or Application Gateway in front of both old and new sites before cutover. Then the switch is a backend change with instant rollback, not a DNS change.
Checklist
- No local disk writes, COM or GAC dependencies left
- Authentication moved to Entra ID
- Secrets in Key Vault, managed identity used for Azure resources
- Health check path and autoscale rules set
- Logs to Application Insights; old VM decommissioned after soak