Guidance for Azure App Service Managed Instance (preview) - a plan-scoped hosting option for Windows web apps requiring OS customization, COM components, registry modifications, MSI installers, and optional private networking. Use when migrating legacy .NET Framework apps, configuring Windows-specific features, or needing RDP access for diagnostics.
Install
npx skillscat add seligj95/azure-app-service-skills/azure-app-service-managed-instance Install via the SkillsCat registry.
When to Use Managed Instance
Use Managed Instance when you need:
- Legacy Windows compatibility: COM components, registry modifications, MSI installers
- IIS customization: IIS Manager access, custom ACLs, Windows features
- Network shares: UNC paths, drive mapping to Azure Files or on-premises
- RDP diagnostics: Just-in-time RDP via Azure Bastion for troubleshooting
- Migration with minimal refactoring: "Lift and improve" for legacy .NET Framework apps
Do NOT use Managed Instance for:
- Linux or containerized workloads
- Multi-language apps (Python, Node.js, Java, PHP)
- Apps that don't need OS-level customization
Current Limitations (Preview)
| Category | Limitation |
|---|---|
| Platform | Windows only (no Linux/containers) |
| SKUs | Pv4 and Pmv4 only |
| Regions | East Asia, West Central US, North Europe, East US, Australia East |
| Authentication | Entra ID and Managed Identity only (no domain join/NTLM/Kerberos) |
| Workloads | Web apps only (no WebJobs, TCP/NetPipes) |
| Configuration | Persistent changes require scripts (RDP is diagnostics-only) |
SKU Options
Standard Pv4 SKUs
| SKU | vCPU | Memory (MB) |
|---|---|---|
| P0v4 | 1 | 2,048 |
| P1v4 | 2 | 5,952 |
| P2v4 | 4 | 13,440 |
| P3v4 | 8 | 28,672 |
Memory-Optimized Pmv4 SKUs
| SKU | vCPU | Memory (MB) |
|---|---|---|
| P1Mv4 | 2 | 13,440 |
| P2Mv4 | 4 | 28,672 |
| P3Mv4 | 8 | 60,160 |
| P4Mv4 | 16 | 121,088 |
| P5Mv4 | 32 | 246,016 |
Quick Start
Check Region Availability
# List regions with Managed Instance support
az appservice list-locations --managed-instance-enabled --sku P1v4Deploy with Azure Developer CLI
# Clone and deploy sample quickstart
mkdir managed-instance-quickstart
cd managed-instance-quickstart
azd init --template https://github.com/Azure-Samples/managed-instance-azure-app-service-quickstart.git
azd env set AZURE_LOCATION northeurope
azd upDeploy via Azure Portal
- Create resource → Search "managed instance"
- Select Web App (for Managed Instance) (preview)
- Configure:
- Runtime: ASP.NET V4.8 (or .NET 8.0)
- Pricing plan: Pv4 or Pmv4
- Configuration script (optional): Storage account, container, zip file
Configuration (Install) Scripts
Install scripts run at instance startup for persistent OS customization.
Requirements
- Script must be named
Install.ps1 - Packaged as single
.zipfile - Uploaded to Azure Blob Storage
- Plan-level managed identity with
Storage Blob Data Readerrole
Example Script Structure
scripts.zip
├── Install.ps1 # Entry point (required)
├── myComponent.msi # Dependencies
└── config.xml # Configuration filesExample Install.ps1
# Install MSI component
$ComponentInstaller = "myComponent.msi"
try {
$Component = Join-Path $PSScriptRoot $ComponentInstaller
Start-Process $Component -ArgumentList "/q" -Wait -ErrorAction Stop
Write-Host "Successfully installed $ComponentInstaller"
} catch {
Write-Error "Failed to install ${ComponentInstaller}: $_"
exit 1
}
# Register COM component
regsvr32 /s (Join-Path $PSScriptRoot "mycomponent.dll")
# Enable Windows feature
Enable-WindowsOptionalFeature -Online -FeatureName "MSMQ-Container" -All -NoRestart
# Install fonts
Get-ChildItem -Recurse -Include *.ttf, *.otf | ForEach-Object {
$Destination = "$env:windir\Fonts\$($_.Name)"
Copy-Item $_.FullName -Destination $Destination -Force
$FontName = $_.BaseName + " (TrueType)"
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" `
-Name $FontName -PropertyType String -Value $_.Name -Force | Out-Null
}Script Best Practices
- Make scripts idempotent (check before install)
- Avoid modifying protected Windows system directories
- Stagger heavy installations to reduce startup latency
- Log installation progress for troubleshooting
- Scripts run with Administrator privileges
Script Logs
# On instance
C:\InstallScripts\<scriptName>\Install.log
# Via Azure Monitor/Log Analytics
# Enable diagnostic settings → App Service Console LogsStorage Mounts
Mount external storage accessible to your apps.
Azure Files Mount
# 1. Create Key Vault secret with connection string
az keyvault secret set \
--vault-name <vault-name> \
--name storage-connection \
--value "DefaultEndpointsProtocol=https;AccountName=<storage>;AccountKey=<key>;EndpointSuffix=core.windows.net"
# 2. Configure via Portal:
# Managed Instance → Configuration → Mounts → + New storage mount
# - Storage type: Azure Files
# - Select storage account and file share
# - Select Key Vault and secret
# - Choose drive letter (e.g., E:)Custom UNC Path Mount
For SMB shares (on-premises, VMs, or non-Microsoft):
# 1. Store credentials in Key Vault
az keyvault secret set \
--vault-name <vault-name> \
--name unc-credentials \
--value "username=<user>,password=<password>"
# 2. Configure mount:
# - Storage type: Custom
# - UNC path: \\server\share
# - Select Key Vault secret for credentialsLocal Temporary Storage
- Limited to 2 GB
- Not persisted after restarts
- Use for temporary processing only
Registry Key Adapters
Create Windows registry keys with values from Key Vault.
Setup
# 1. Store value in Key Vault
az keyvault secret set \
--vault-name <vault-name> \
--name my-registry-value \
--value "secret-configuration-value"
# 2. Configure via Portal:
# Managed Instance → Configuration → Registry Keys → + Add
# - Path: HKLM:\SOFTWARE\MyApp\Config
# - Vault: Select Key Vault
# - Secret: Select secret
# - Type: String or DWORDSupported Types
String(REG_SZ)DWORD(32-bit integer)
Caution: Be careful when modifying system-critical registry paths.
Plan-Level Managed Identity
Required for configuration scripts, storage mounts, and registry adapters.
Assign User-Assigned Identity
# Create identity
az identity create \
--name mi-managed-instance \
--resource-group <rg>
# Get identity ID
IDENTITY_ID=$(az identity show \
--name mi-managed-instance \
--resource-group <rg> \
--query id -o tsv)
# Assign to plan via Portal:
# Managed Instance → Identity → User assigned → + AddGrant Permissions
# Get identity principal ID
PRINCIPAL_ID=$(az identity show \
--name mi-managed-instance \
--resource-group <rg> \
--query principalId -o tsv)
# Grant Storage Blob Data Reader for config scripts
az role assignment create \
--assignee $PRINCIPAL_ID \
--role "Storage Blob Data Reader" \
--scope <storage-account-or-container-id>
# Grant Key Vault Secrets User for mounts/registry
az role assignment create \
--assignee $PRINCIPAL_ID \
--role "Key Vault Secrets User" \
--scope <keyvault-id>RDP Access via Bastion
Just-in-time RDP for transient diagnostics.
Prerequisites
- Managed Instance must be VNet integrated
- Azure Bastion deployed in the VNet
- Bastion Standard tier with Native Client Support enabled
- Port 3389 allowed in NSG (Bastion subnet → App Service subnet)
Configure
# 1. Enable VNet integration (see networking skill)
# 2. Deploy Bastion
az network bastion create \
--name bastion-host \
--resource-group <rg> \
--vnet-name <vnet-name> \
--location <location> \
--sku Standard
# 3. Enable RDP:
# Managed Instance → Configuration → Bastion/RDP → Allow Remote DesktopRDP Diagnostics
Available via RDP:
- Event Viewer
- IIS Manager
- File system access
- C:\InstallScripts logs
Warning: Manual changes via RDP are LOST on restart or platform maintenance. Use configuration scripts for persistent changes.
Networking
VNet Integration (Optional)
# Add VNet integration
az webapp vnet-integration add \
--name <app-name> \
--resource-group <rg> \
--vnet <vnet-name> \
--subnet <subnet-name>
# Route all traffic through VNet
az resource update \
--resource-group <rg> \
--name <app-name> \
--resource-type "Microsoft.Web/sites" \
--set properties.outboundVnetRouting.allTraffic=truePrivate Endpoints
az network private-endpoint create \
--name <app-name>-pe \
--resource-group <rg> \
--vnet-name <vnet-name> \
--subnet private-endpoints-subnet \
--private-connection-resource-id <app-resource-id> \
--group-id sites \
--connection-name app-connectionDeployment
Zip Deploy
az webapp deploy \
--resource-group <rg> \
--name <app-name> \
--src-path app.zip \
--type zipRun From Package
az webapp config appsettings set \
--name <app-name> \
--resource-group <rg> \
--settings WEBSITE_RUN_FROM_PACKAGE=1
az webapp deploy \
--resource-group <rg> \
--name <app-name> \
--src-path app.zip \
--type zipRuntime Support
Preinstalled
- .NET Framework 3.5
- .NET Framework 4.8
- .NET 8.0
Custom Runtimes
Install via configuration scripts (you manage updates).
Comparing Hosting Options
| Feature | Standard App Service | Managed Instance | ASE |
|---|---|---|---|
| OS Customization | No | Yes (scripts) | Yes |
| RDP Access | No | Yes (Bastion) | Yes |
| COM/Registry/MSI | No | Yes | Yes |
| Linux/Containers | Yes | No | Yes |
| Multi-language | Yes | .NET only | Yes |
| Isolation | Shared | Plan-scoped | Full |
| Cost | Lower | Pv4/Pmv4 | Highest |
Best Practices
- Use configuration scripts for persistent changes (not RDP)
- Centralize secrets in Azure Key Vault
- Test scripts in staging before production rollout
- Monitor with Defender for Cloud for threat detection
- Validate logging setup for troubleshooting
- Align network rules with dependency inventories
Troubleshooting
Configuration Script Failures
# Check script logs on instance
C:\InstallScripts\<scriptName>\Install.log
# Stream via Log Analytics
az monitor log-analytics query \
--workspace <workspace-id> \
--analytics-query "AppServiceConsoleLogs | where ResultDescription contains 'Install' | take 50"Storage/Registry Adapter Issues
- Check plan-level managed identity has correct permissions
- Verify Key Vault secrets are accessible
- Review App Service Platform Logs
Common Errors
| Error | Cause | Solution |
|---|---|---|
| Script timeout | Heavy installation | Stagger installations, optimize script |
| Storage mount failed | Credentials invalid | Verify Key Vault secret format |
| Registry key not created | Wrong path/permissions | Check registry path syntax |
| RDP unavailable | No VNet integration | Enable VNet integration first |