From Rack to Cloud: Part 3 – Lift, Shift, or Take the Scenic Route?

Choosing between rehost, refactor, and rebuild in your Azure migration.

Opening

Not every workload needs a redesign. Some just need a new postcode. Others? It’s time to retire them.

Cloud migrations often stall at a deceptively simple decision:
Do we lift it as-is, or modernise it on the way?

In this post, we’ll help you decide when to rehost, refactor, or rebuild — and how the 300-VM not-for-profit did it, under pressure and on a deadline.

Migration Strategies: Know Your Options

When moving to Azure, most workloads land in one of three lanes:

Option Description Example Azure Services
Rehost Lift and shift with minimal or no changes Azure VMs, Azure VMware Solution
Refactor Minor config/code updates to unlock cloud features App Services, Azure SQL MI, Logic Apps
Rebuild Re-architected using PaaS or SaaS Azure SQL DB, Serverless Functions

Each one has a trade-off: time, cost, control, and complexity.

Real-World Impact: Triage Under Pressure

Back to our 300-VM migration story.

This Sydney-based not-for-profit had a 90-day deadline to vacate their co-location facility. They didn’t have time to modernise everything, but they didn’t want to reproduce years of technical debt in the cloud either.

Rehost (~240 VMs): Fastest path, lowest change

  • Why: These systems were functional and stable. No major dependencies blocking a straight move.
  • Target: Azure IaaS VMs + storage accounts.
  • Win: Fast migration path, time saved on rework.

Refactor (~40 VMs): Small tweaks, big wins

  • Why: Some apps benefited from better scalability, backup automation, or simpler hosting.
  • Target: Azure App Services and Azure SQL Managed Instance.
  • Win: Cut licensing, improved operational efficiency.

Rebuild (~20 VMs): Platform shift for long-term gain

  • Why: Legacy on-prem PostgreSQL and SQL Server workloads were brittle, tightly scoped, and overdue for rationalisation.
  • Target: Rebuilt as PaaS data platforms using Azure SQL Database.
  • Win: Simplified management, improved performance, and tighter cost control.

How to Decide

  • Time constraints – Is this a quick win or an anchor best left behind?
  • Team capabilities – Is your team ready to manage PaaS or re-architected systems?
  • Risk and reliability – Will changes introduce new risk for key workloads?
  • Dependency complexity – Is the app isolated or tied into legacy systems?
  • Future-fit – Does rehosting block cleaner modernisation later on?

Implementation Examples

Azure Portal

  • Rehost: Use Azure Migrate with replication + cutover
  • Refactor: Use App Service Migration Assistant and Database Migration Service
  • Rebuild: Create new Azure SQL DB instances and move schema + data

Bicep Sample – IaaS Virtual Machine within Availability Set for HA

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
// Parameters
@description('Prefix for the Virtual Machine names')
param vmNamePrefix string = 'myvm'

@description('Location for all resources.')
param location string = resourceGroup().location

@description('Admin username for the Virtual Machines')
param adminUsername string = 'azadmin'

@description('Admin password for the Virtual Machines')
@secure()
param adminPassword string

@description('Number of Virtual Machines to deploy')
param vmCount int = 2

@description('Name of the Virtual Network')
param virtualNetworkName string = 'myVNet'

@description('Name of the Subnet')
param subnetName string = 'mySubnet'

@description('Network Interface Prefix Name')
param networkInterfaceName string = 'nic'

// Create an Availability Set to support high availability
resource availabilitySet 'Microsoft.Compute/availabilitySets@2024-11-01' = {
  name: '${vmNamePrefix}-avset'
  location: location
  properties: {
    platformUpdateDomainCount: 5
    platformFaultDomainCount: 2
  }
  sku: {
    name: 'Aligned'
  }
}

var subnetRef = resourceId('Microsoft.Network/virtualNetworks/subnets', virtualNetworkName, subnetName)

resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-09-01' existing = {
  name: virtualNetworkName
}

resource networkInterface 'Microsoft.Network/networkInterfaces@2023-09-01' = [for i in range(0, vmCount): {
  name: '${networkInterfaceName}-${vmNamePrefix}-${i}'
  location: location
  properties: {
    ipConfigurations: [
      {
        name: 'ipconfig1'
        properties: {
          privateIPAllocationMethod: 'Dynamic'
          subnet: {
            id: subnetRef
          }
        }
      }
    ]
  }
  dependsOn: [
    virtualNetwork
  ]
}]

// Loop to deploy multiple Virtual Machines within the Availability Set
resource vms 'Microsoft.Compute/virtualMachines@2024-11-01' = [for i in range(0, vmCount): {
  name: '${vmNamePrefix}-${i}'
  location: location
  properties: {
    hardwareProfile: {
      vmSize: 'Standard_DS1_v2'
    }
    osProfile: {
      computerName: '${vmNamePrefix}-${i}'
      adminUsername: adminUsername
      adminPassword: adminPassword
    }
    storageProfile: {
      imageReference: {
        publisher: 'MicrosoftWindowsServer'
        offer: 'WindowsServer'
        sku: '2025-Datacenter'
        version: 'latest'
      }
      osDisk: {
        createOption: 'FromImage'
      }
    }
    networkProfile: {
      networkInterfaces: [
        {
          id: networkInterface[i].id
        }
      ]
    }
    availabilitySet: {
      id: availabilitySet.id
    }
  }
}]

Gotchas & Edge Cases

  • Legacy Licensing Surprises: Some OS and SQL Server versions can’t be brought to Azure without extra licensing or re-platforming. Do the homework.
  • Hard-coded Infrastructure Assumptions: Apps with fixed IPs, server names, or outdated DNS references can break silently after a rehost.
  • Security Model Drift: Group policies, local service accounts, or on-host firewall rules may not behave the same way in Azure — especially in locked-down workloads.
  • RBAC Gaps and Identity Mismatches: Teams assume the same local admin rights apply in the cloud — but Azure RBAC changes that model. Plan for it.
  • Readiness ≠ Priority: A workload might be easy to move but not critical to your business. Balance technical simplicity with business impact.

Best Practices

  • Use your Azure Migrate discovery output to flag easy wins
  • Mix models: rehost what’s stable, refactor what’s ready, rebuild what’s broken
  • Avoid the all-or-nothing trap, modernisation doesn’t have to block progress
  • Reassess post-migration: Some workloads will be easier to modernise after they’re stable in Azure
  • Track everything in a shared backlog with clear ownership

Brewed Take ☕

🍺
Brewed Insight:

Lift-and-shift buys you time. Refactor buys you efficiency. Rebuild buys you tomorrow.

The best migrations aren’t all-in on one model they mix tactics based on risk, value, and readiness.

Not everything belongs in a VM, but not everything is ready for PaaS either.

Learn More