Managed Instance on Azure App Service Is GA: Migrate Legacy .NET Framework Apps Without Rewriting Them

Managed Instance on Azure App Service Is GA: Migrate Legacy .NET Framework Apps Without Rewriting Them

If you've ever tried to move a 15-year-old ASP.NET app to Azure App Service and hit a wall โ€” a COM component that needs registering, an MSI installer the app depends on, hardcoded C:\ paths, or a registry key some vendor DLL reads at startup โ€” you already know why that app is still sitting on an on-premises Windows Server or an unmanaged VM. Standard App Service is a clean sandbox: no OS-level customization, no registry access, no arbitrary installers. That's exactly what makes it unsuitable for this class of app.

Microsoft's answer, Managed Instance on Azure App Service, went generally available on August 18, 2026, after roughly nine months in public preview (announced at Ignite 2025). It's a plan-scoped hosting option that keeps App Service's managed patching, autoscaling, and diagnostics, but adds the OS-level hooks โ€” configuration scripts, registry adapters, storage mounts, and Bastion-based RDP โ€” that legacy Windows apps actually need.

This guide covers what Managed Instance actually is, how it differs from both standard App Service and a plain VM lift-and-shift, and the concrete steps to stand one up and migrate an app onto it.

Why this exists

Before Managed Instance, teams with COM/registry/GAC-dependent .NET Framework apps had exactly two realistic paths to Azure:

  1. Lift-and-shift to an IaaS VM โ€” works, but you're back to patching the OS yourself, managing scale sets by hand, and losing App Service's deployment slots, easy TLS, and built-in diagnostics.
  2. Rewrite/refactor to remove the dependency โ€” the "correct" long-term answer, but often a multi-quarter project for an app the business wants off a decommissioned server now, not after a rewrite.

Managed Instance is a third path aimed squarely at the gap between those two: apps that depend on machine-level state (COM registration, MSI-installed components, registry values, GAC assemblies, mapped network drives, custom IIS configuration) but don't otherwise need a full VM's worth of manual operations. It's not a general-purpose upgrade to App Service โ€” if your app doesn't have OS dependencies, standard App Service (or a container) is still the simpler, cheaper choice.

What you're actually getting

Managed Instance is Windows Server 2022 under the hood, running .NET Framework 3.5, 4.8, and .NET 8.0 out of the box (anything else, install it yourself via a configuration script โ€” the platform doesn't maintain extra runtimes). Four plan-level capabilities are what separate it from standard App Service:

  • Configuration (install) scripts โ€” a PowerShell Install.ps1 that runs at instance startup with Administrator permissions, for changes that need to survive a restart (COM registration, MSI installs, enabling Windows roles/features).
  • Registry key adapters โ€” define registry paths at the plan level, with values sourced from Azure Key Vault secrets, instead of baking them into an image.
  • Storage mounts โ€” persistent Azure Files or custom UNC shares, mounted to a drive letter, for apps that expect a shared filesystem.
  • RDP via Azure Bastion โ€” just-in-time, for diagnostics only. Microsoft is explicit that anything you do over RDP that isn't backed by a configuration script gets wiped on the next instance recycle.

The important constraints, upfront:

  • Windows web apps only โ€” no Linux, no containers.
  • Limited to Pv4 and Pmv4 pricing plans (regular Standard/Basic tiers don't support it).
  • Available in select regions only, with more added over time โ€” check availability before you plan a migration date.
  • Only one configuration script adapter per plan, but unlimited registry/storage adapters.
  • Adding or editing any adapter restarts every instance on the plan, affecting every web app deployed to it.

Step-by-step: deploying a Managed Instance and migrating an app

1. Confirm prerequisites

  • An Azure subscription with Pv4/Pmv4 available in your target region.
  • A user-assigned managed identity โ€” required for the plan to read configuration scripts from Blob storage and pull secrets from Key Vault for registry/storage adapters.
  • If you're using a configuration script: a Storage account with a Blob container holding a single zip file whose root contains Install.ps1 (that exact filename โ€” it's the required entry point).

2. Create the plan and app (Azure CLI)

 1# Create the Managed Instance plan
 2az deployment group create \
 3  --resource-group "my-rg" \
 4  --template-file infra/app-service-plan-managed-instance.json \
 5  --parameters \
 6    location="<region>" \
 7    appServicePlanName="my-mi-plan" \
 8    userAssignedIdentityResourceId="<identity-resource-id>" \
 9    installScriptSourceUri="<blob-url-to-scripts.zip>" \
10    skuName=P1V4 \
11    skuCapacity=1
12
13# Create the web app on that plan
14az webapp create \
15  --name "my-legacy-app" \
16  --resource-group "my-rg" \
17  --plan "my-mi-plan" \
18  --runtime "ASPNET:V4.8"
19
20# Attach a managed identity to the web app itself (can differ from the plan's identity)
21az webapp identity assign \
22  --name "my-legacy-app" \
23  --resource-group "my-rg" \
24  --identities "<identity-resource-id>"

If P1V4/P1Mv4 doesn't show up as an available SKU, it's almost always a region-availability or quota problem โ€” verify Pv4/Pmv4 quota in that region before troubleshooting anything else.

3. Write and attach the configuration script

A minimal script package is just a zip with Install.ps1 at the root plus whatever it installs:

1Install.ps1
2myComponentInstaller.msi
3config.xml
1# Install.ps1 โ€” install a legacy component the app depends on
2$ComponentInstaller = "myComponentInstaller.msi"
3try {
4    $Component = Join-Path $PSScriptRoot $ComponentInstaller
5    Start-Process $Component -ArgumentList "/q" -Wait -ErrorAction Stop
6} catch {
7    Write-Error "Failed to install ${ComponentInstaller}: $_"
8    exit 1
9}

Upload the zip to your Blob container, then attach it: Configuration โ†’ General Settings โ†’ Configuration script in the portal, or reference installScriptSourceUri in the ARM/Bicep deployment as above. Make the script idempotent (check before installing) โ€” it reruns on every instance start, not just the first one.

4. Add registry keys and storage mounts, if your app needs them

Registry key (value pulled from Key Vault, not hardcoded):

Portal: Configuration โ†’ Registry Keys โ†’ + Add, then supply the registry path, the Key Vault name, the secret name, and the value type (String or DWORD).

Storage mount (Azure Files, persistent across restarts):

  1. Create a Storage account and file share.
  2. Store the connection string as a Key Vault secret (DefaultEndpointsProtocol=...;AccountName=...;AccountKey=...).
  3. Configuration โ†’ Mounts โ†’ + New storage mount, pick the drive letter, storage account, share, and Key Vault secret.

5. Deploy the app and verify

1az webapp deploy \
2  --resource-group "my-rg" \
3  --name "my-legacy-app" \
4  --src-path app.zip \
5  --type zip

Browse to the app's default domain from the Overview page and confirm it starts cleanly with the installed dependency (font, COM component, whatever the script provisioned) actually present.

6. Enable Bastion RDP for diagnostics (optional)

Requires the plan to be VNet-integrated and a Bastion host on that VNet, plus port 3389 open from the Bastion subnet's NSG to the plan's subnet NSG. Then: Configuration โ†’ Bastion/RDP โ†’ Allow Remote Desktop (via Bastion). Use this to look at a broken instance, not to fix it permanently โ€” anything changed by hand here disappears on the next recycle.

Best practices

  • Treat the configuration script as the only source of truth for OS-level state. If a fix only exists because you RDP'd in and changed something, it doesn't survive the next restart โ€” write it into Install.ps1 instead.
  • Keep secrets in Key Vault, not in the script. Registry adapters and storage mounts both pull credentials from Key Vault by design โ€” don't defeat that by hardcoding a connection string in Install.ps1.
  • Batch your adapter changes. Since adding or editing any adapter restarts every instance on the plan, stage registry keys, storage mounts, and script updates together rather than one at a time in production.
  • Watch provisioning time as you add dependencies. There's no size limit on the configuration script's zip, but larger dependency sets mean longer instance startup โ€” test against a realistic package size before committing to a plan-wide rollout.
  • Right-size against the memory table, not just vCPU count โ€” P1v4 gives you 5,952 MB addressable memory, P1Mv4 gives you 13,440 MB on the same 2 cores. If your legacy app is memory-hungry (common for old IIS worker processes), the M-series tiers are worth the extra cost before you assume you need more cores.

Common mistakes to avoid

  • Assuming this replaces a full VM. Managed Instance still can't do everything a VM can โ€” no arbitrary background services running independently of the app's lifecycle, no full admin control outside the configuration-script/RDP model. If the app needs true VM-level control, this isn't the right target.
  • Using RDP for "quick fixes" in production. It works, which is exactly the trap โ€” the change vanishes on the next instance recycle and you'll be debugging the same "fixed" issue again a week later.
  • Forgetting Linux/container workloads aren't supported at all. This is Windows-only; if part of your migration involves containerized services, they need a different target (App Service Linux/containers, Container Apps, or AKS).
  • Not checking regional availability before promising a migration date. GA doesn't mean "every region" โ€” Microsoft is rolling out regions over time, and Pv4/Pmv4 quota can also be the blocker even where the region is listed.
  • Skipping the idempotency check on install scripts. A script that isn't safe to rerun will eventually double-install something or fail outright the second time an instance restarts โ€” which happens far more often than "just once at creation."

Troubleshooting

P1V4/P1Mv4 not available when creating the plan: check region availability for Managed Instance first, then check Pv4/Pmv4 quota in that subscription/region โ€” both block SKU selection identically and look the same from the portal dropdown.

Configuration script doesn't seem to run: confirm the managed identity is assigned to the plan (not just the web app) with Storage Blob Data Reader on the container, and that the zip's root file is named exactly Install.ps1.

App can't reach the registry value or storage mount: verify the plan's managed identity has access to the Key Vault secret referenced by the adapter โ€” a missing Key Vault access policy/RBAC role is the most common cause, not a misconfigured adapter.

Instance restarted unexpectedly after a small config change: expected โ€” any adapter add/edit (script, registry, or storage) restarts every instance on the plan. Plan changes for a maintenance window if the app is customer-facing.

FAQ

Does Managed Instance support Linux or containers? No. Windows web apps only, as of GA.

Which pricing tiers support it? Only Pv4 and Pmv4. Standard, Basic, and other Premium tiers don't expose the Managed Instance option.

What OS and runtimes come preinstalled? Windows Server 2022, with .NET Framework 3.5, 4.8, and .NET 8.0. Anything else needs a configuration script.

Can I run destructive operations in a configuration script, like modifying System32? No โ€” the platform blocks operations that could destabilize the instance. Configuration scripts are for installing dependencies and enabling roles/features, not arbitrary system surgery.

How many configuration script adapters can a plan have? Exactly one. There's no limit on the number of registry or storage adapters, though, and more adapters means longer provisioning time.

Can I use RDP changes as my actual deployment mechanism? No โ€” RDP via Bastion is diagnostics-only. Anything not captured in a configuration script is lost on the next instance recycle.

Key takeaways

QuestionAnswer
GA dateAugust 18, 2026 (public preview since Ignite 2025)
What it's forLegacy .NET Framework apps needing COM, MSI, registry, GAC, or custom IIS config
OS / runtimesWindows Server 2022; .NET Framework 3.5/4.8 and .NET 8.0 preinstalled
Supported tiersPv4 and Pmv4 only
Linux/containersNot supported
Persistent OS changesConfiguration (install) script, runs at every instance start as Administrator
SecretsRegistry adapters and storage mounts pull from Key Vault
RDPBastion-based, diagnostics only, changes don't persist across restarts
Adapter changesRestart all instances on the plan โ€” batch them

Further Reading