Overview

Script will help you to restore a virtual machine from the existing blob snapshots.

Although some familiarity with the Azure Portal would be beneficial, however, for the benefit of everyone, a step by step guide in this document is provided.

Script workflow:

  • Script executes
  • Reads the input file which is a comma separated .CSV file to read VM Name/Subscription Name/Resource Group Name
  • Prompts user to login to Azure.
  • Validate provided virtual machine exists in Azure, gather details about the available snapshots
  • List available snapshots for each Virtual Name and request user to select the snapshot to restore
  • Save the VM Object to preserve the VM configuration
  • Delete the VM from Portal
  • Restore the snapshot selected by the user on the existing OS Disk
  • Provision the VM based on the restored OS Disk URI using saved VM Config

Pre-requisites

The following prerequisites must be met, before performing the Core capacity check steps.

  1. Internet connection
  2. Valid Azure Subscription
  3. The person who will perform the below steps must have one of the following roles in the Azure subscription
    • Owner
    • Contributor
  4. Some familiarity with the Azure Portal would be beneficial, although not necessary
  5. Latest version of Microsoft .NET Framework (download link)
  6. Latest version of Microsoft Windows Management Framework (download link)
  7. Latest version of Microsoft PowerShell (download link including detailed installation guide)
  8. Latest version of Azure cmdlets (installation guide), although steps are provided below
  9. Some familiarity with PowerShell ISE, although not necessary (documentation)
  10. Some familiarity with the Azure Portal would be beneficial, although not necessary
  11. Some familiarity with PowerShell scripting would be beneficial, although not necessary

Restore VM from snapshot

Windows PowerShell scripts are the only means to restore a blob snapshot now, however, there might be some advanced features introduced in Azure Portal by Microsoft later after this document is written.

Script and the input file format are provided at the end of the document. For using this script,

    1. Open PowerShell ISE and open the script file
    2. Execute the Script
    3. You will be first promoted to enter your Azure credentials
    4. When prompted to enter the snapshot to be restored, input the value as instructed on the PowerShell execution window.
    5. Once the inputs are provided the script will proceed to restore the virtual machine from the selected snapshot

Input file and PowerShell script

Input File format (ServerList.csv)

ServerName,SubscriptionName,ResourceGroupName
<VM Name>,<Subscription Name>,<VM Resource Group>

PowerShell Script

<#
ScriptName : Restore VM Snapshot
Description : This script will restore the snapshot for the provided server.
Author : Karthik Sankaran
Version : 1.1
#How it works#
- Read the Input File for Virtual Machine Name, Subscription Name and Resource Groups
- Get the VM object (JSON)
- Save the JSON configuration to a file (To rebuild the VM wherever it goes wrong)
- Remove the VM (Only the configuration, all dependencies are kept )
- Restore the snapshot, overwriting the exsiting OS Disk VHD file.
- Change the Storage config because the recration needs the disk attach option
- ReCreate the VM
#>

#Functions used in the
Function RunLog-Command([string]$Description, [ScriptBlock]$Command, [string]$LogFile){
Try{
$Output = $Description+' ... '
Write-Host $Output -ForegroundColor Yellow
((Get-Date -UFormat "[%d-%m-%Y %H:%M:%S] ") + $Output) | Out-File -FilePath $LogFile -Append -Force
$Result = Invoke-Command -ScriptBlock $Command
}
Catch {
$ErrorMessage = $_.Exception.Message
$Output = 'Error '+$ErrorMessage
((Get-Date -UFormat "[%d-%m-%Y %H:%M:%S] ") + $Output) | Out-File -FilePath $LogFile -Append -Force
$Result = ""
}
Finally
{
if ($ErrorMessage -eq $null) {$Output = "[Completed] $Description ... "} else {$Output = "[Failed] $Description ... "}
((Get-Date -UFormat "[%d-%m-%Y %H:%M:%S] ") + $Output) | Out-File -FilePath $LogFile -Append -Force
}
Return $Result
}
Function LogintoAzure()
{
$Error_WrongCredentials = $True
$AzureAccount = $null
while ($Error_WrongCredentials) {
Try {
Write-Host "Info : Please, Enter the credentials of an Admin account of Azure" -ForegroundColor Cyan
#$AzureCredentials = Get-Credential -Message "Please, Enter the credentials of an Admin account of your subscription"
$AzureAccount = Login-AzureRmAccount
if ($AzureAccount.Context.Tenant -eq $null)
{
$Error_WrongCredentials = $True
$Output = " Warning : The Credentials for [" + $AzureAccount.Context.Account.id +"] are not valid or the user does not have Azure subscriptions "
Write-Host $Output -BackgroundColor Red -ForegroundColor Yellow
}
else
{$Error_WrongCredentials = $false ; return $AzureAccount}
}
Catch {
$Output = " Warning : The Credentials for [" + $AzureAccount.Context.Account.id +"] are not valid or the user does not have Azure subscriptions "
Write-Host $Output -BackgroundColor Red -ForegroundColor Yellow
Generate-LogVerbose -Output $logFile -Message $Output
}
Finally {
}
}
return $AzureAccount
}

Function Select-Subscription ($SubscriptionName, $AzureAccount)
{
Select-AzureRmSubscription -SubscriptionName $SubscriptionName
}

#Main Script section
#Input parameters
$CurrentPath = Split-Path $MyInvocation.MyCommand.Definition
$ServerList = Import-Csv "<path to the input file>\ServerList.csv"
foreach($Server in $ServerList)
{
$ServerName = $Server.ServerName
$Subscription = $Server.SubscriptionName
$ResourceGroup = $Server.ResourceGroupName
write-host $servrerName
write-host $subscription
##Setting Global Paramaters##
$ErrorActionPreference = "Stop"
$date = Get-Date -UFormat "%Y-%m-%d-%H-%M"
$workfolder = Split-Path $script:MyInvocation.MyCommand.Path
$logFile = $workfolder+'\'+$date+'.log'
Write-Output "Steps will be tracked on the log file : [ $logFile ]"
##Login to Azure##
$Description = "Connecting to Azure"
$Command = {LogintoAzure}
$AzureAccount = RunLog-Command -Description $Description -Command $Command -LogFile $LogFile
##Select the Subscription##
$Description = "Selecting the Subscription : $Subscription"
$Command = {Select-AzureRmSubscription -SubscriptionName $Subscription | Out-Null}
RunLog-Command -Description $Description -Command $Command -LogFile $LogFile
#command logic to get the list of snapshots
$VM = Get-AzureRmVM -ResourceGroupName $ResourceGroup -Name $ServerName
$StrAccName = ([System.Uri]$VM.StorageProfile.OsDisk.Vhd.uri).Host.Split('.')[0] #getting SA of VHD.
$RGName = Get-AzureRmStorageAccount | where {$_.StorageAccountName -eq $StrAccName} | Select-Object -ExpandProperty ResourceGroupName #getting RG of VHD.
$VHDName = ($VM.StorageProfile.OsDisk.vhd.Uri).Split('/')[-1] #getting VHD name.
$SrcCntName = ($VM.StorageProfile.OsDisk.vhd.Uri).Split('/')[-2] #Source container name.

$StrAcc = Get-AzureRmStorageAccount -AccountName $StrAccName -ResourceGroupName $RGName # Get the Storage Account
$CntNames = Get-AzureStorageContainer -Context $strAcc.Context | Select-Object -ExpandProperty Name #getting all the containers
#List the snapshots available and get the user to select the time they want to restore to
$vmSnapshots = Get-AzureStorageBlob –Context $StrAcc.Context -Container $SrcCntName | Where-Object {$_.ICloudBlob.IsSnapshot -and $_.Name -eq $VHDName -and $_.SnapshotTime -ne $null } #Get the list of Snapshots
$count = 1
Foreach ($vmsnap in $vmSnapshots)
{
write-host $count.ToString() "-:" $vmsnap.SnapshotTime
$count+=1
}
#Accept user choice of the restore time
Write-host "Please type the number corresponding to the snapshot you wish to restore to and press ENTER Key..." -ForegroundColor Yellow
$sChoice = Read-Host
$ArrayIndex = $sChoice - 1
#Get the Virtual Machine Object
$Description = "Restoring the Azure VM [$ServerName] : (Step 0 : Getting the VM Object...) "
$Command = {Get-AzureRmVM -ResourceGroupName $ResourceGroup -Name $ServerName }
$VmObject = RunLog-Command -Description $Description -Command $Command -LogFile $LogFile
#Stop the Virtual Machine
$Description = "Restoring the Azure VM [$ServerName] : (Step 1 : Stopping the VM...) "
$VMstate = (Get-AzureRmVM -ResourceGroupName $ResourceGroup -Name $ServerName -Status).Statuses[1].code
if ($VMstate -ne 'PowerState/deallocated' -and $VMstate -ne 'PowerState/Stopped')
{
$Command = { $VmObject | Stop-AzureRmVM -StayProvisioned -Force | Out-Null}
RunLog-Command -Description $Description -Command $Command -LogFile $LogFile
}
#Export the VmObject to a file
$Description = "Restoring the Azure VM [$ServerName] : (Step 2 : Exporting the VM Config to a file : $ResourceGroup-$ServerName.json ...) "
$Command = {ConvertTo-Json -InputObject $VmObject -Depth 100 | Out-File -FilePath $workfolder'\'$ResourceGroup-$ServerName'.json'}
RunLog-Command -Description $Description -Command $Command -LogFile $LogFile
#Restoring the VM#
$VmObject.OSProfile = $null
$VmObject.StorageProfile.ImageReference = $null
if ($VmObject.StorageProfile.OsDisk.Image) {$VmObject.StorageProfile.OsDisk.Image = $null}
$VmObject.StorageProfile.OsDisk.CreateOption = 'Attach'
for ($s=1;$s -le $VmObject.StorageProfile.DataDisks.Count ; $s++ )
{
$VmObject.StorageProfile.DataDisks[$s-1].CreateOption = 'Attach'
}
$Description = "Restoring the Azure VM [$ServerName] : (Step 3 : Removing the VM...) "
$Command = {Remove-AzureRmVM -Name $VmObject.Name -ResourceGroupName $VmObject.ResourceGroupName -Force | Out-null}
RunLog-Command -Description $Description -Command $Command -LogFile $LogFile
Start-sleep 5
#Write-Verbose -Verbose "creating VHD from the snapshot..."
$Description = "Restoring the Azure VM [$ServerName] : (Step 4 : creating VHD from the snapshot...) "
# Select the blob snapshot you want to be copied by using the snapshotTime property
$SnapshotTime = $vmSnapshots[$ArrayIndex] | select -ExpandProperty SnapshotTime
# Select the blob snapshot you want to restore based on the snapshotTime property we got from previous step
$RestorePoint = $vmSnapshots | where { $_.SnapshotTime -eq $SnapshotTime }
$snapshot = [Microsoft.WindowsAzure.Storage.Blob.CloudBlob] $RestorePoint[0].ICloudBlob # Convert the blob selected to a CloudBlob type
# Copy the blob snapshot to destination container overwriting the existing OS disk
$copy = Start-AzureStorageBlobCopy -CloudBlob $snapshot -Context $StrAcc.Context -DestContext $StrAcc.Context -DestContainer $SrcCntName -DestBlob $VHDName -ConcurrentTaskCount 10 -Force
$status = $copy | Get-AzureStorageBlobCopyState
While($status.Status -eq "Pending")
{
$status = $copy | Get-AzureStorageBlobCopyState
Start-Sleep 10
$status
}
Write-Verbose -Verbose "VHD created in $DestContName container....!!!"
$Description = "Restoring the Azure VM [$ServerName] : (Step 5 : Creating the VM...) "
$Command = {New-AzureRmVM -ResourceGroupName $VmObject.ResourceGroupName -Location $VmObject.Location -VM $VmObject | Out-null}
RunLog-Command -Description $Description -Command $Command -LogFile $LogFile
$Description = "Restoring the Azure VM [$ServerName] : (Step 6 : VM Creation completed, please proceed to login to the VM and validate the details...) "
RunLog-Command -Description $Description -Command $Command -LogFile $LogFile
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *

Bitnami