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)