Back to Blog
DevOps
3 min read

Building Azure VMs from Snapshot Disks with Terraform

AzureTerraformVMsSnapshotsDisaster Recovery

Sometimes you need to create a VM from disk snapshots - disaster recovery testing, creating dev environments from production snapshots, or migrating between subscriptions.

Here's how to do it properly with Terraform.

The Process

  1. Create managed disks from snapshots
  2. Create a NIC
  3. Create the VM using the managed disks

Creating Disks from Snapshots

# OS Disk from snapshot
resource "azurerm_managed_disk" "os_disk" {
  name                 = "disk-vm-restored-os"
  location             = azurerm_resource_group.this.location
  resource_group_name  = azurerm_resource_group.this.name
  storage_account_type = "Premium_LRS"
  create_option        = "Copy"
  source_resource_id   = data.azurerm_snapshot.os_snapshot.id
  disk_size_gb         = 128

  tags = {
    Environment = "dev"
    Source      = "snapshot-restore"
  }
}

# Data Disk from snapshot
resource "azurerm_managed_disk" "data_disk" {
  name                 = "disk-vm-restored-data"
  location             = azurerm_resource_group.this.location
  resource_group_name  = azurerm_resource_group.this.name
  storage_account_type = "Premium_LRS"
  create_option        = "Copy"
  source_resource_id   = data.azurerm_snapshot.data_snapshot.id
  disk_size_gb         = 256
}

Referencing Existing Snapshots

data "azurerm_snapshot" "os_snapshot" {
  name                = "snap-prod-vm-os-20240801"
  resource_group_name = "rg-snapshots"
}

data "azurerm_snapshot" "data_snapshot" {
  name                = "snap-prod-vm-data-20240801"
  resource_group_name = "rg-snapshots"
}

Creating the VM

resource "azurerm_network_interface" "this" {
  name                = "nic-vm-restored"
  location            = azurerm_resource_group.this.location
  resource_group_name = azurerm_resource_group.this.name

  ip_configuration {
    name                          = "internal"
    subnet_id                     = azurerm_subnet.this.id
    private_ip_address_allocation = "Dynamic"
  }
}

resource "azurerm_linux_virtual_machine" "this" {
  name                = "vm-restored"
  resource_group_name = azurerm_resource_group.this.name
  location            = azurerm_resource_group.this.location
  size                = "Standard_D4s_v3"
  admin_username      = "adminuser"

  network_interface_ids = [
    azurerm_network_interface.this.id
  ]

  admin_ssh_key {
    username   = "adminuser"
    public_key = file("~/.ssh/id_rsa.pub")
  }

  os_disk {
    name                 = azurerm_managed_disk.os_disk.name
    caching              = "ReadWrite"
    storage_account_type = "Premium_LRS"
  }

  source_image_id = null  # Not needed when using existing disk
}

# Attach data disk
resource "azurerm_virtual_machine_data_disk_attachment" "data" {
  managed_disk_id    = azurerm_managed_disk.data_disk.id
  virtual_machine_id = azurerm_linux_virtual_machine.this.id
  lun                = 0
  caching            = "ReadOnly"
}

Important Notes

OS Disk attachment - When creating a VM from an existing OS disk, you can't use source_image_reference. The disk already has the OS.

Disk size - The new disk must be at least as large as the snapshot source.

Storage account type - Can be different from the source. Useful for creating Standard disks from Premium snapshots in dev environments.

Cross-region - Snapshots are regional. To restore in a different region, first copy the snapshot.

Windows VMs

For Windows, use azurerm_windows_virtual_machine:

resource "azurerm_windows_virtual_machine" "this" {
  name                = "vm-restored-win"
  resource_group_name = azurerm_resource_group.this.name
  location            = azurerm_resource_group.this.location
  size                = "Standard_D4s_v3"
  admin_username      = "adminuser"
  admin_password      = var.admin_password

  network_interface_ids = [
    azurerm_network_interface.this.id
  ]

  os_disk {
    name                 = azurerm_managed_disk.os_disk.name
    caching              = "ReadWrite"
    storage_account_type = "Premium_LRS"
  }
}

Automating Snapshot Creation

For regular snapshots, use Azure Backup or a scheduled pipeline:

resource "azurerm_snapshot" "os_disk" {
  name                = "snap-vm-os-${formatdate("YYYYMMDD", timestamp())}"
  location            = azurerm_resource_group.this.location
  resource_group_name = azurerm_resource_group.this.name
  create_option       = "Copy"
  source_uri          = azurerm_managed_disk.source_os.id
}

Need help with backup and disaster recovery in Azure? Get in touch - we help organisations protect their cloud infrastructure.

Need help with your Azure environment?

Get in touch for a free consultation.

Get in Touch