When restoring an Encrypted Virtual Machine that is managed by Windows Azure, you will need to use PowerShell.
This is a two stage process.
Restore-BackupImageToDisk … | Restore-EncryptedBekVM …
Stage 1
Retrieve the Backup Container – This will contain the encrypted disks and json files with the secret keys.
-
keyEncryptionKey
-
diskEncryptionKey
This will allow you to retrieve the disks and JSON metadata in step 2.
Restore-BackupImageToDisk.ps1
param(
[Parameter(Mandatory=$true)]
[string]
$SubscriptionName="RangerRom",
[Parameter(Mandatory=$true)]
[string]
$ResourceGroup="Ranger-Rom-Prod",
[Parameter(Mandatory=$true)]
[string]
$StorageAccount="RangerRomStorage",
[Parameter(Mandatory=$true)]
[string]
$RecoveryVaultName="RecoveryVault",
[Parameter(Mandatory=$true)]
[string]
$VMName,
[Parameter(Mandatory=$true)]
[datetime]
$FromDate
)
Login-AzureRmAccount
Get-AzureRmSubscription -SubscriptionName $SubscriptionName | Select-AzureRmSubscription
$endDate = Get-Date
$namedContainer = Get-AzureRmRecoveryServicesBackupContainer -ContainerType "AzureVM" -Status "Registered" | Where { $_.FriendlyName -eq $VMName }
$backupitem = Get-AzureRmRecoveryServicesBackupItem -Container $namedContainer -WorkloadType "AzureVM"
$rp = Get-AzureRmRecoveryServicesBackupRecoveryPoint -Item $backupitem -StartDate $FromDate.ToUniversalTime() -EndDate $enddate.ToUniversalTime()
$restorejob = Restore-AzureRmRecoveryServicesBackupItem -RecoveryPoint $rp[0] -StorageAccountName $StorageAccount -StorageAccountResourceGroupName $ResourceGroup
Wait-AzureRmRecoveryServicesBackupJob -Job $restorejob -Timeout 43200
$restorejob = Get-AzureRmRecoveryServicesBackupJob -Job $restorejob
$details = Get-AzureRmRecoveryServicesBackupJobDetails -Job $restorejob
$details
Stage 2
We will grab the job details via the pipeline if provided or go find the earliest Restore Job after the FromDate and then initiate a restore of all the encrypted disks to the new Virtual Machine.
Restore-EncryptedBekVM.ps1
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$True, Mandatory=$false, HelpMessage="Requires a AzureVmJobDetails object e.g. Get-AzureRmRecoveryServicesBackupJobDetails. Optional.")]
$JobDetails,
[Parameter(Mandatory=$true, HelpMessage="Assumes osDisks, DataDisk, RestorePoints, AvailbilitySet, VM, Nic are all in same resource group.")]
[string]
$DefaultResourceGroup="Ranger-Rom-Prod",
[Parameter(Mandatory=$true)]
[string]
$SourceVMName="Lisha",
[Parameter(Mandatory=$true)]
[string]
$TargetVMName,
[Parameter(Mandatory=$true)]
[string]
$BackupVaultName="RecoveryVault",
[Parameter(Mandatory=$true)]
[datetime]
$FromDate
)
Begin {
$FromDate = $FromDate.ToUniversalTime()
Write-Verbose "Started. $(Get-Date)"
}
Process {
Write-Verbose "Retrieving Backup Vault."
if(-not $JobDetails)
{
Get-AzureRmRecoveryServicesVault -Name $BackupVaultName -ResourceGroupName $DefaultResourceGroup | Set-AzureRmRecoveryServicesVaultContext
$Job = Get-AzureRmRecoveryServicesBackupJob -Status Completed -Operation Restore -From $FromDate | Sort -Property StartTime | Where { $_.WorkloadName -eq $SourceVMName} | Select -Last 1
if($Job -eq $null) {
throw "Job $workLoadName not found."
}
$JobDetails = Get-AzureRmRecoveryServicesBackupJobDetails -Job $Job
}
Write-Verbose "Query the restored disk properties for the job details."
$properties = $JobDetails.properties
$storageAccountName = $properties["Target Storage Account Name"]
$containerName = $properties["Config Blob Container Name"]
$blobName = $properties["Config Blob Name"]
Write-Verbose "Found Restore Blob Set at $($Properties['Config Blob Uri'])"
Write-Verbose "Set the Azure storage context and restore the JSON configuration file."
Set-AzureRmCurrentStorageAccount -Name $storageaccountname -ResourceGroupName $DefaultResourceGroup
$folder = New-Item "C:\RangerRom" -ItemType Directory -Force
$destination_path = "C:\$($folder.Name)\$SourceVMName.config.json"
Get-AzureStorageBlobContent -Container $containerName -Blob $blobName -Destination $destination_path
Write-Verbose "Restore config saved to file. $destination_path"
$restoreConfig = ((Get-Content -Path $destination_path -Raw -Encoding Unicode)).TrimEnd([char]0x00) | ConvertFrom-Json
# 3. Use the JSON configuration file to create the VM configuration.
$oldVM = Get-AzureRmVM | Where { $_.Name -eq $SourceVMName }
$vm = New-AzureRmVMConfig -VMSize $restoreConfig.'properties.hardwareProfile'.vmSize -VMName $TargetVMName -AvailabilitySetId $oldVM.AvailabilitySetReference.Id
$vm.Location = $oldVM.Location
# 4. Attach the OS disk and data disks - Managed, encrypted VMs (BEK only
$bekUrl = $restoreConfig.'properties.storageProfile'.osDisk.encryptionSettings.diskEncryptionKey.secretUrl
$keyVaultId = $restoreConfig.'properties.storageProfile'.osDisk.encryptionSettings.diskEncryptionKey.sourceVault.id
$kekUrl = $restoreConfig.'properties.storageProfile'.osDisk.encryptionSettings.keyEncryptionKey.keyUrl
$storageType = "StandardLRS"
$osDiskName = $vm.Name + "_Restored_" + (Get-Date).ToString("yyyy-MM-dd-hh-mm-ss") + "_osdisk"
$osVhdUri = $restoreConfig.'properties.storageProfile'.osDisk.vhd.uri
$diskConfig = New-AzureRmDiskConfig -AccountType $storageType -Location $restoreConfig.location -CreateOption Import -SourceUri $osVhdUri
$osDisk = New-AzureRmDisk -DiskName $osDiskName -Disk $diskConfig -ResourceGroupName $DefaultResourceGroup
Set-AzureRmVMOSDisk -VM $vm -ManagedDiskId $osDisk.Id -DiskEncryptionKeyUrl $bekUrl -DiskEncryptionKeyVaultId $keyVaultId -KeyEncryptionKeyUrl $kekUrl -KeyEncryptionKeyVaultId $keyVaultId -CreateOption "Attach" -Windows
$count = 0
foreach($dd in $restoreConfig.'properties.storageProfile'.dataDisks)
{
$dataDiskName = $vm.Name + "_Restored_" + (Get-Date).ToString("yyyy-MM-dd-hh-mm-ss") + "_datadisk" + $count ;
$dataVhdUri = $dd.vhd.uri;
$dataDiskConfig = New-AzureRmDiskConfig -AccountType $storageType -Location $restoreConfig.location -CreateOption Import -SourceUri $dataVhdUri
$dataDisk = New-AzureRmDisk -DiskName $dataDiskName -Disk $dataDiskConfig -ResourceGroupName $DefaultResourceGroup
Add-AzureRmVMDataDisk -VM $vm -Name $dataDiskName -ManagedDiskId $dataDisk.Id -Lun $dd.Lun -CreateOption "Attach"
$count += 1
}
Write-Verbose "Setting the Network settings."
$oldNicId = $oldVM.NetworkProfile.NetworkInterfaces[0].Id
$oldNic = Get-AzureRmNetworkInterface -Name $oldNicId.Substring($oldNicId.LastIndexOf("/")+ 1) -ResourceGroupName $DefaultResourceGroup
$subnetItems = $oldNic.IpConfigurations[0].Subnet.Id.Split("/")
$networkResourceGroup = $subnetItems[$subnetItems.IndexOf("resourceGroups")+1]
$nicName= $vm.Name + "_nic_" + (New-Guid).Guid
$nic = New-AzureRmNetworkInterface -Name $nicName -ResourceGroupName $networkResourceGroup -Location $oldNic.location -SubnetId $oldNic.IpConfigurations[0].Subnet.Id
$vm=Add-AzureRmVMNetworkInterface -VM $vm -Id $nic.Id
$vm.DiagnosticsProfile = $oldVM.DiagnosticsProfile
Write-Verbose "Provisioning VM."
New-AzureRmVM -ResourceGroupName $oldVM.ResourceGroupName -Location $oldVM.Location -VM $vm
}
End {
Write-Verbose "Completed. $(Get-Date)"
}
Usage
Summary
Ensure you have Azure VM Backups and they are backed on on a regular schedule.
Use this script to backup a Restore Point from a Recovery Vault Container.
Remember it will pick the earliest date of a Restore Point relative to your From Date. If there are 4 Restores in the recovery vault from 1 May, it will pick the first one.
Remember to:
- Decommission the old VM
- Update DNS with the new ip address
- Update the Load Balance Sets (If using a Load Balancer)