Cloud Migration Platform: Azure Migrate, Site Recovery, App Service Migration, and Hybrid Cloud
Introduction
Enterprise cloud migration requires comprehensive planning, assessment, migration execution, and ongoing optimization. This deep dive builds a complete cloud migration platform leveraging Azure Migrate for discovery and assessment, Azure Site Recovery for VM migration and disaster recovery, App Service Migration Assistant for web apps, Azure Arc for hybrid cloud management, and post-migration cost optimization with Azure Advisor.
Solution Architecture
Components Overview
| Component | Role | Key Features |
|---|---|---|
| Azure Migrate | Assessment hub | Discovery, dependency mapping, sizing recommendations |
| Azure Site Recovery | Migration & DR | Replication, failover, failback orchestration |
| Database Migration Service | Database migration | Online/offline migration, schema conversion |
| App Service Migration Assistant | Web app migration | Compatibility assessment, automated migration |
| Azure Arc | Hybrid management | Unified control plane, policy enforcement |
| Azure Advisor | Optimization | Cost, performance, security recommendations |
| Azure Monitor | Observability | Logs, metrics, alerts, workbooks |
Implementation Guide
Phase 1: Azure Migrate Discovery & Assessment
Deploy Azure Migrate Appliance:
# Download Azure Migrate appliance OVA/VHD
Invoke-WebRequest -Uri "https://aka.ms/migrate/appliance/vmware" -OutFile "AzureMigrateAppliance.ova"
# Import to VMware/Hyper-V (manual via vSphere/Hyper-V Manager)
# After appliance deployment, register with Azure Migrate project
$projectKey = "your-project-key"
$applianceUrl = "https://migrate-appliance.local:44368"
Invoke-RestMethod -Method Post -Uri "$applianceUrl/api/register" -Body @{
projectKey = $projectKey
cloudType = "AzureCloud"
} -ContentType "application/json"
Azure CLI Project Setup:
# Create Azure Migrate project
az migrate project create \
--name migrate-contoso-platform \
--resource-group rg-migration \
--location eastus
# Enable assessment and migration tools
az migrate project enable-tools \
--project migrate-contoso-platform \
--resource-group rg-migration \
--tools AzureMigrate:ServerAssessment AzureMigrate:ServerMigration
Run Assessment:
# Create assessment
az migrate assessment create \
--name assessment-production-vms \
--project migrate-contoso-platform \
--resource-group rg-migration \
--azure-location eastus \
--sizing-criterion PerformanceBased \
--percentile 95 \
--currency USD \
--discount-percentage 30 \
--reserved-instance true
# Get assessment results
az migrate assessment show \
--name assessment-production-vms \
--project migrate-contoso-platform \
--resource-group rg-migration \
--query '{readiness:properties.azureReadinessSummary, costs:properties.monthlyCostEstimate}'
PowerShell: Export Assessment Report:
# Connect to Azure
Connect-AzAccount
# Get assessment details
$assessment = Get-AzMigrateAssessment -ProjectName "migrate-contoso-platform" -ResourceGroupName "rg-migration"
# Export to CSV
$assessment.AssessedMachines | Select-Object MachineName, AzureReadiness, RecommendedVmSize, MonthlyComputeCost, MonthlyStorageCost | Export-Csv -Path "assessment-report.csv" -NoTypeInformation
# Group by readiness
$assessment.AssessedMachines | Group-Object AzureReadiness | Select-Object Name, Count | Format-Table
Phase 2: Azure Site Recovery Migration
Site Recovery Vault Setup:
# Create Recovery Services vault
az backup vault create \
--name vault-migration-asr \
--resource-group rg-migration \
--location eastus
# Configure replication settings
az backup vault backup-properties set \
--name vault-migration-asr \
--resource-group rg-migration \
--backup-storage-redundancy GeoRedundant
Bicep Configuration for ASR:
resource recoveryServicesVault 'Microsoft.RecoveryServices/vaults@2023-01-01' = {
name: 'vault-migration-asr'
location: location
sku: {
name: 'RS0'
tier: 'Standard'
}
properties: {
publicNetworkAccess: 'Enabled'
}
}
resource replicationPolicy 'Microsoft.RecoveryServices/vaults/replicationPolicies@2023-01-01' = {
parent: recoveryServicesVault
name: 'replication-policy-24hr'
properties: {
providerSpecificInput: {
instanceType: 'A2A'
recoveryPointHistory: 1440 // 24 hours
appConsistentFrequencyInMinutes: 60
crashConsistentFrequencyInMinutes: 5
multiVmSyncStatus: 'Enable'
}
}
}
resource replicationFabric 'Microsoft.RecoveryServices/vaults/replicationFabrics@2023-01-01' = {
parent: recoveryServicesVault
name: 'fabric-onprem-vmware'
properties: {
customDetails: {
instanceType: 'VMware'
processServers: [
{
friendlyName: 'process-server-1'
ipAddress: '10.0.1.10'
}
]
}
}
}
Enable Replication (PowerShell):
# Get vault
$vault = Get-AzRecoveryServicesVault -Name "vault-migration-asr" -ResourceGroupName "rg-migration"
Set-AzRecoveryServicesAsrVaultContext -Vault $vault
# Get protection container
$protectionContainer = Get-AzRecoveryServicesAsrProtectionContainer -Fabric (Get-AzRecoveryServicesAsrFabric)
# Get replication policy
$replicationPolicy = Get-AzRecoveryServicesAsrPolicy -Name "replication-policy-24hr"
# Enable replication for VM
$vm = Get-AzVM -ResourceGroupName "rg-onprem" -Name "web-server-01"
$replicationProtectedItem = New-AzRecoveryServicesAsrReplicationProtectedItem `
-VMwareToAzure `
-ProtectionContainer $protectionContainer `
-Name "web-server-01" `
-Policy $replicationPolicy `
-RecoveryAzureStorageAccountId "/subscriptions/.../storageAccounts/stmigration" `
-ProcessServer "process-server-1" `
-Account "vmware-credentials" `
-RecoveryResourceGroupId "/subscriptions/.../resourceGroups/rg-migrated-vms" `
-RecoveryAzureNetworkId "/subscriptions/.../virtualNetworks/vnet-prod" `
-RecoveryAzureSubnetName "subnet-web"
Test Failover:
# Start test failover
$testFailoverJob = Start-AzRecoveryServicesAsrTestFailoverJob `
-ReplicationProtectedItem $replicationProtectedItem `
-Direction PrimaryToRecovery `
-AzureVMNetworkId "/subscriptions/.../virtualNetworks/vnet-test"
# Monitor job
while ($testFailoverJob.State -ne "Succeeded") {
$testFailoverJob = Get-AzRecoveryServicesAsrJob -Job $testFailoverJob
Start-Sleep -Seconds 30
}
# Cleanup test failover
Start-AzRecoveryServicesAsrTestFailoverCleanupJob -ReplicationProtectedItem $replicationProtectedItem
Production Failover:
# Planned failover (graceful shutdown)
$failoverJob = Start-AzRecoveryServicesAsrPlannedFailoverJob `
-ReplicationProtectedItem $replicationProtectedItem `
-Direction PrimaryToRecovery
# Commit failover
Update-AzRecoveryServicesAsrProtectionDirection `
-ReplicationProtectedItem $replicationProtectedItem `
-Direction RecoveryToPrimary
Phase 3: Database Migration Service
DMS Instance Setup:
# Create DMS instance
az dms create \
--name dms-sql-migration \
--resource-group rg-migration \
--location eastus \
--sku-name Premium_4vCores \
--subnet /subscriptions/.../subnets/subnet-dms
# Create migration project
az dms project create \
--name project-sql-migration \
--service-name dms-sql-migration \
--resource-group rg-migration \
--location eastus \
--source-platform SQL \
--target-platform SQLDB
Online Migration Task:
# Create online migration task
az dms project task create \
--name task-migrate-salesdb \
--project-name project-sql-migration \
--service-name dms-sql-migration \
--resource-group rg-migration \
--task-type Migrate.SqlServer.AzureSqlDb.Sync \
--source-connection-json '{
"userName": "sa",
"password": "<password>",
"dataSource": "sql-onprem.contoso.com",
"encryptConnection": true,
"trustServerCertificate": false
}' \
--target-connection-json '{
"userName": "sqladmin",
"password": "<password>",
"dataSource": "sqlserver-prod.database.windows.net",
"encryptConnection": true,
"trustServerCertificate": false
}' \
--database-options-json '[
{
"name": "SalesDB",
"targetDatabaseName": "SalesDB",
"makeSourceDbReadOnly": false,
"tableMap": {
"dbo.Customers": "dbo.Customers",
"dbo.Orders": "dbo.Orders"
}
}
]'
Cutover Script:
# Monitor migration progress
$task = az dms project task show `
--name task-migrate-salesdb `
--project-name project-sql-migration `
--service-name dms-sql-migration `
--resource-group rg-migration | ConvertFrom-Json
# When ready, perform cutover
az dms project task cutover `
--name task-migrate-salesdb `
--project-name project-sql-migration `
--service-name dms-sql-migration `
--resource-group rg-migration `
--object-name SalesDB
Phase 4: App Service Migration
Migration Assistant Assessment:
# Download App Service Migration Assistant
Invoke-WebRequest -Uri "https://appmigration.microsoft.com/api/download/windows/AppServiceMigrationAssistant.msi" -OutFile "AppServiceMigrationAssistant.msi"
# Install
Start-Process msiexec.exe -ArgumentList "/i AppServiceMigrationAssistant.msi /quiet" -Wait
# Run assessment (CLI mode)
& "C:\Program Files\AppServiceMigrationAssistant\Migrate.exe" assess `
--site-name "contoso-webapp" `
--output assessment-report.json
Migration via Azure CLI:
# Create App Service Plan
az appservice plan create \
--name plan-migrated-apps \
--resource-group rg-migration \
--location eastus \
--sku P1V3 \
--is-linux
# Create web app
az webapp create \
--name webapp-contoso-prod \
--resource-group rg-migration \
--plan plan-migrated-apps \
--runtime "DOTNET|8.0"
# Deploy from on-prem (via zip)
az webapp deployment source config-zip \
--resource-group rg-migration \
--name webapp-contoso-prod \
--src app-package.zip
Connection String Migration:
# Migrate connection strings
az webapp config connection-string set \
--resource-group rg-migration \
--name webapp-contoso-prod \
--connection-string-type SQLAzure \
--settings DefaultConnection="Server=tcp:sqlserver-prod.database.windows.net,1433;Database=SalesDB;User ID=sqladmin;Password=<password>;Encrypt=True;"
# Migrate app settings
az webapp config appsettings set \
--resource-group rg-migration \
--name webapp-contoso-prod \
--settings \
ASPNETCORE_ENVIRONMENT=Production \
ApplicationInsights__InstrumentationKey="<key>"
Phase 5: Azure Arc Hybrid Management
Arc-enabled Servers:
# Install Arc agent on on-premises server
$subscriptionId = "your-subscription-id"
$resourceGroup = "rg-migration"
$location = "eastus"
$servicePrincipalClientId = "<sp-client-id>"
$servicePrincipalSecret = "<sp-secret>"
$tenantId = "<tenant-id>"
Invoke-WebRequest -Uri "https://aka.ms/azcmagent-windows" -OutFile "AzureConnectedMachineAgent.msi"
Start-Process msiexec.exe -ArgumentList "/i AzureConnectedMachineAgent.msi /quiet" -Wait
& "$env:ProgramFiles\AzureConnectedMachineAgent\azcmagent.exe" connect `
--service-principal-id $servicePrincipalClientId `
--service-principal-secret $servicePrincipalSecret `
--tenant-id $tenantId `
--subscription-id $subscriptionId `
--resource-group $resourceGroup `
--location $location `
--tags "Environment=Production" "DataCenter=OnPrem"
Apply Azure Policy to Arc Servers:
# Assign policy to enforce monitoring agent
az policy assignment create \
--name "deploy-monitoring-agent-arc" \
--policy "/providers/Microsoft.Authorization/policyDefinitions/0868462e-646c-4fe3-9ced-a733534b6a2c" \
--scope /subscriptions/<sub-id>/resourceGroups/rg-migration \
--assign-identity --location eastus
# Remediation task
az policy remediation create \
--name remediate-arc-monitoring \
--policy-assignment deploy-monitoring-agent-arc \
--resource-group rg-migration
Arc-enabled Kubernetes:
# Connect on-premises Kubernetes cluster
az connectedk8s connect \
--name arc-k8s-onprem \
--resource-group rg-migration \
--location eastus \
--kube-config ~/.kube/config
# Enable Azure Monitor for Arc K8s
az k8s-extension create \
--name azuremonitor-containers \
--cluster-name arc-k8s-onprem \
--resource-group rg-migration \
--cluster-type connectedClusters \
--extension-type Microsoft.AzureMonitor.Containers
Phase 6: Post-Migration Optimization
Azure Advisor Recommendations:
# Get cost recommendations
az advisor recommendation list \
--category Cost \
--query "[].{Resource:resourceMetadata.resourceId, Recommendation:shortDescription.solution, Savings:extendedProperties.savingsAmount}" \
-o table
# Enable automatic remediation
az advisor configuration create \
--resource-group rg-migration \
--low-cpu-threshold 10 \
--exclude false
Cost Optimization Script:
# Identify idle VMs
$vms = Get-AzVM -ResourceGroupName "rg-migrated-vms"
foreach ($vm in $vms) {
$metrics = Get-AzMetric -ResourceId $vm.Id -MetricName "Percentage CPU" -TimeGrain 01:00:00 -StartTime (Get-Date).AddDays(-7)
$avgCpu = ($metrics.Data | Measure-Object Average -Average).Average
if ($avgCpu -lt 5) {
Write-Host "VM $($vm.Name) has low CPU usage: $avgCpu%. Consider resizing or deallocation."
}
}
# Resize overprovisioned VMs
$vm = Get-AzVM -ResourceGroupName "rg-migrated-vms" -Name "web-server-01"
$vm.HardwareProfile.VmSize = "Standard_B2s"
Update-AzVM -ResourceGroupName "rg-migrated-vms" -VM $vm
Reserved Instances Purchase:
# Calculate reservation recommendations
az reservations reservation-order calculate \
--reservation-order-id <order-id> \
--sku-name Standard_D4s_v3 \
--location eastus \
--term P1Y \
--quantity 10
# Purchase reservation
az reservations reservation-order purchase \
--reservation-order-id <order-id> \
--sku Standard_D4s_v3 \
--location eastus \
--quantity 10 \
--term P3Y
Phase 7: Monitoring & Validation
Azure Monitor Workbook (KQL):
// Migration progress dashboard
AzureMigrateAssessment
| where TimeGenerated > ago(30d)
| summarize
TotalServers = dcount(MachineName),
ReadyToMigrate = dcountif(MachineName, AzureReadiness == "Ready"),
ReadyWithConditions = dcountif(MachineName, AzureReadiness == "ReadyWithConditions"),
NotReady = dcountif(MachineName, AzureReadiness == "NotReady")
| extend ReadinessRate = (ReadyToMigrate * 100.0) / TotalServers
// Post-migration performance comparison
Perf
| where TimeGenerated > ago(7d)
| where ObjectName == "Processor" and CounterName == "% Processor Time"
| summarize
AvgCPU_OnPrem = avgif(CounterValue, Computer startswith "onprem"),
AvgCPU_Azure = avgif(CounterValue, Computer startswith "az")
| extend PerformanceGain = ((AvgCPU_OnPrem - AvgCPU_Azure) / AvgCPU_OnPrem) * 100
// Cost tracking
AzureActivity
| where OperationNameValue contains "Microsoft.Compute/virtualMachines/write"
| join kind=inner (
Usage
| where TimeGenerated > ago(30d)
| summarize TotalCost = sum(Quantity * UnitPrice) by ResourceId
) on $left.ResourceId == $right.ResourceId
| project TimeGenerated, ResourceName = split(ResourceId, '/')[8], TotalCost
| order by TotalCost desc
Validation Checklist:
Pre-Cutover Validation:
- [ ] Application functionality tests pass
- [ ] Performance benchmarks meet baselines
- [ ] Database replication lag < 5 minutes
- [ ] SSL certificates configured
- [ ] DNS records prepared (not yet updated)
- [ ] Backup/DR tested and verified
Cutover Execution:
- [ ] Graceful shutdown of on-premises services
- [ ] Finalize database synchronization
- [ ] Update DNS records to Azure IPs
- [ ] Verify application startup in Azure
- [ ] Test authentication/authorization
- [ ] Confirm monitoring/alerts active
Post-Cutover Validation:
- [ ] User acceptance testing completed
- [ ] No critical errors in Application Insights
- [ ] Performance SLAs met
- [ ] Cost tracking dashboards operational
- [ ] Decommission on-premises infrastructure
Best Practices
- Pilot Migration: Start with non-critical workloads to validate process
- Dependency Mapping: Use Azure Migrate dependency analysis to identify interdependencies
- Network Planning: Design VNet architecture with hub-spoke for hybrid connectivity
- Security: Apply Azure Security Center recommendations immediately post-migration
- Automation: Use Infrastructure as Code (Bicep/Terraform) for consistency
- Cost Management: Enable budgets and alerts before migration begins
- Skills Enablement: Train operations team on Azure management tools
Troubleshooting
Issue: ASR replication fails with network connectivity error
Solution: Verify ExpressRoute/VPN connectivity; check NSG rules allow ports 443, 9443; validate proxy settings
Issue: Database migration timeout during cutover
Solution: Reduce transaction log size; disable non-clustered indexes temporarily; increase DMS SKU
Issue: Arc agent registration fails
Solution: Check firewall allows outbound HTTPS to *.guestconfiguration.azure.com; verify service principal permissions
Key Takeaways
- Azure Migrate provides comprehensive assessment for informed migration planning
- Azure Site Recovery enables seamless VM migration with minimal downtime
- Database Migration Service supports online migration for near-zero RTO
- Azure Arc extends Azure management to on-premises and multi-cloud environments
- Post-migration optimization with Advisor ensures cost efficiency
Next Steps
- Implement Azure Landing Zones for enterprise-scale governance
- Deploy Azure Sentinel for unified security operations
- Explore Azure VMware Solution for VMware workload migration without refactoring
- Implement Azure Backup for comprehensive data protection
Additional Resources
Ready to accelerate your cloud migration journey?