[Azure]ARM虚拟机整机磁盘快照[4]——从快照还原虚拟机

60 篇文章 0 订阅
59 篇文章 0 订阅

这个还原脚本有一个小前提,就是原虚拟机不能先删除,原因是为了简化操作和参数,脚本会从原来虚拟机的属性中读取资源组,虚拟网络,可用性集等属性,然后直接利用原虚拟机的某个时间点的快照在和原虚拟机同一子网下面创建出一台类似的虚拟机。新的虚拟机由于不能使用原来的网络接口和IP地址,所以会重新生成这些对象。

如果希望将原虚拟机删除,可以在这个脚本的基础上进行酌情修改,比如可以先记录原虚拟机的各种信息,然后在脚本中删除原虚拟机,新建虚拟机的时候使用原虚拟机的组件,加上这部分逻辑后就能做出类似 Hyper-V 上面的还原效果了,直接可以把原虚拟机和原磁盘都覆盖掉。

不过感觉这种逻辑比较激进一些,有兴趣的读者可以做一下。

还原逻辑也大部分借用了之前读取快照的逻辑,额外加入了 RestoreSnapshot 的逻辑和创建虚拟机的逻辑,脚本如下:

param(
    [Parameter(Mandatory = $true)] 
    [string]$SubscriptionName, 
 
    [Parameter(Mandatory = $true)]
    [string]$ResourceGroupName,

    [Parameter(Mandatory = $true)]
    [string]$OldVMName,

    [Parameter(Mandatory = $true)]
    [string]$NewVMName
)

Function GetResourceNameFromResourceId($resourceId)
{
    return $resourceId.Substring($resourceId.LastIndexOf('/') + 1);
}

Function GetResourcePropertyFromResourceId($resourceId, $propertyName)
{
    $propertyName = $propertyName + "/";
    $rgName = $resourceId.Substring($resourceId.IndexOf($propertyName) + $propertyName.Length);
    return $rgName.Substring(0, $rgName.IndexOf("/"));
}

Function PrepareBlobContext($vhdAbsoluteUri)
{
    $StorageAccountName = $vhdAbsoluteUri.Substring(8, $vhdAbsoluteUri.IndexOf(".blob.core.chinacloudapi.cn") - 8);    #length of "https://" is 8
    $ContainerPathIndex = $vhdAbsoluteUri.IndexOf("blob.core.chinacloudapi.cn/") + 27;
    $ContainerName = $vhdAbsoluteUri.SubString($ContainerPathIndex, $vhdAbsoluteUri.IndexOf('/', $ContainerPathIndex) - $ContainerPathIndex);
    $BlobName = $vhdAbsoluteUri.SubString($vhdAbsoluteUri.LastIndexOf('/') + 1);

    $storageAccount = Get-AzureRmStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageAccountName;
    $storageContext = $storageAccount.Context;
    $blobClient = $storageContext.Context.StorageAccount.CreateCloudBlobClient();
    $blobContainer = $blobClient.GetContainerReference($ContainerName);
    $blob = $blobContainer.GetBlockBlobReference($BlobName);

    return $blob;
}

Function RestoreSnapshot($snapshot, $snapshotUri, $newVmName)
{
    $StorageAccountName = $snapshotUri.Substring(8, $snapshotUri.IndexOf(".blob.core.chinacloudapi.cn") - 8);    #length of "https://" is 8
    $ContainerPathIndex = $snapshotUri.IndexOf("blob.core.chinacloudapi.cn/") + 27;
    $ContainerName = $snapshotUri.SubString($ContainerPathIndex, $snapshotUri.IndexOf('/', $ContainerPathIndex) - $ContainerPathIndex);
    $BlobName = $snapshotUri.SubString($snapshotUri.LastIndexOf('/') + 1);

    $storageAccount = Get-AzureRmStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageAccountName;
    $storageContext = $storageAccount.Context;
    $blobClient = $storageContext.Context.StorageAccount.CreateCloudBlobClient();
    $blobContainer = $blobClient.GetContainerReference($ContainerName);
    $newBlobName = "{0}-{1}" -f $newVmName, $BlobName;
    $blob = $blobContainer.GetBlobReference($newBlobName);
    [void]($blob.StartCopyFromBlob($snapshot.SnapshotQualifiedUri));

    return $blob.Uri;
}

Function RestoreVM($rgName, $oldVmName, $newVmName)
{
    $vm = Get-AzureRmVM -ResourceGroupName $rgName -Name $oldVmName -WarningAction Ignore;

    $osDiskUri = $vm.StorageProfile.OsDisk.Vhd.Uri;
    $blob = PrepareBlobContext $osDiskUri;
    $osDiskSnapshotList += @(GetSnapshotListByBlob $blob);
    $osDiskSnapshotPropertiesList += @(GetSnapshotPropertiesList $osDiskSnapshotList $blob);

    $dataDiskSnapshotLists = @();
    $dataDiskSnapshotPropertiesLists = @();
    foreach($dataDiskUri in $vm.StorageProfile.DataDisks.Vhd.Uri)
    {
        $blob = PrepareBlobContext $dataDiskUri;
        $dataDiskSnapshotList = @(GetSnapshotListByBlob $blob);
        $dataDiskSnapshotPropertiesList = @(GetSnapshotPropertiesList $dataDiskSnapshotList $blob);
        $dataDiskSnapshotLists += $null;
        $dataDiskSnapshotLists[$dataDiskSnapshotLists.Count - 1] = $dataDiskSnapshotList;
        $dataDiskSnapshotPropertiesLists += $null;
        $dataDiskSnapshotPropertiesLists[$dataDiskSnapshotPropertiesLists.Count - 1] = $dataDiskSnapshotPropertiesList;
    }
    
    $resultList = @();
    $count = $osDiskSnapshotPropertiesList.Count;
    for ($i = 0; $i -lt $count; $i++)
    {
        $findMatch = $true;
        $result = @();
        $osDiskSnapshotTime = $osDiskSnapshotPropertiesList[$i].SnapshotTime;
        $result += $i;
        foreach ($dataDiskSnapshotPropertiesList in $dataDiskSnapshotPropertiesLists)
        {
            $matchIndex = FindMatchSnapshot $osDiskSnapshotTime $dataDiskSnapshotPropertiesList;
            if ($matchIndex -eq -1)
            {
                $findMatch = $false;
                break;
            } else {
                $result += $matchIndex;
            }
        }
        if ($findMatch)
        {
            $resultList += $null;
            $resultList[$resultList.Count - 1] = $result;
        }
    }

    $resultCount = $resultList.Count;
    if ($resultCount -eq 0)
    {
        Write-Host "Virtual Machine has no snapshots." -ForegroundColor Yellow;
        return;
    }

    Write-Host "Virtual Machine has the following snapshots:" -ForegroundColor Yellow;
    Write-Host ("{0, -10}{1}" -f "Index", "Snapshot Time") -ForegroundColor Green;
    for ($i = 0; $i -lt $resultCount; $i++)
    {
        Write-Host ("{0, -10}{1}" -f ($i+1), $osDiskSnapshotPropertiesList[$resultList[$i][0]].SnapshotTime) -ForegroundColor Green;
    }

    $restoredOSDiskUri = $null;
    $restoredDataDiskUris = @();
    $dataDiskCount = $dataDiskSnapshotLists.Count;
    $selection = $resultList[(Read-Host "Please select the snapshot you want to restore") - 1];
    If ($PSCmdlet.ShouldContinue("Confirm?", "=== Confirm restore Opeartion ==="))
    {
        $restoredOSDiskUri = RestoreSnapshot $osDiskSnapshotList[$selection[0]] $osDiskSnapshotPropertiesList[$selection[0]].SnapshotUri.AbsoluteUri $newVmName;
        for ($i = 0; $i -lt $dataDiskCount; $i++)
        {
            $restoredDataDiskUri = RestoreSnapshot $dataDiskSnapshotLists[$i][$selection[$i+1]] $dataDiskSnapshotPropertiesLists[$i][$selection[$i+1]].SnapshotUri.AbsoluteUri $newVmName;
            $restoredDataDiskUris += $restoredDataDiskUri;
        }
        Write-Host "Done";
    } else {
        Write-Host "Canceled";
    }

    # create new VM with restored vhds
    $newVmSize = $vm.HardwareProfile.VmSize;
    $location = $vm.Location;
    $availabilitySetId = $vm.AvailabilitySetReference.Id;
    $oldNicId = ($vm.NetworkProfile.NetworkInterfaces | where {$_.Primary -eq $true}).Id;
    $oldNicName = GetResourceNameFromResourceId $oldNicId;
    $oldNic = Get-AzureRmNetworkInterface -Name $oldNicName -ResourceGroupName $rgName;
    $subnetId = $oldNic.IpConfigurations[0].Subnet.Id;
    $subnetName = GetResourceNameFromResourceId $subnetId;
    $vnetName = GetResourcePropertyFromResourceId $subnetId "virtualNetworks";
    $vnet = Get-AzureRmVirtualNetwork -Name $vnetName -ResourceGroupName $rgName;
    $subnet01 = Get-AzureRmVirtualNetworkSubnetConfig -Name $subnetName -VirtualNetwork $vnet;
    $publicIP = New-AzureRmPublicIpAddress -Name ("{0}PIP" -f $newVmName) -ResourceGroupName $rgName -Location $location -AllocationMethod Dynamic -IpAddressVersion IPv4 –Force;
    $NIC = New-AzureRmNetworkInterface -Name ("{0}NIC" -f $newVmName) -ResourceGroupName $rgName -Location $location -SubnetId $subnet01.Id -PublicIpAddressId $publicIP.Id;

    $OSDiskName = $newVmName + "_OSDisk";
    $vmconfig = $null;
    if ($availabilitySetId -eq $null)
    {
        $vmconfig = New-AzureRmVMConfig -VMName $newVmName -VMSize $newVmSize;
    } else {
        $vmconfig = New-AzureRmVMConfig -VMName $newVmName -VMSize $newVmSize -AvailabilitySetId $vm.AvailabilitySetReference.Id;
    }
    if ($vm.StorageProfile.OsDisk.OsType.ToString() -eq "Windows")
    {
        $vmconfig = $vmconfig | Set-AzureRmVMOSDisk –Name $OSDiskName -VhdUri $restoredOSDiskUri -CreateOption attach -Windows;
    } else {
        $vmconfig = $vmconfig | Set-AzureRmVMOSDisk –Name $OSDiskName -VhdUri $restoredOSDiskUri -CreateOption attach -Linux;
    }
    $vmconfig = $vmconfig | Add-AzureRmVMNetworkInterface -Id $NIC.Id -Primary;

    $lun = 0;
    foreach ($restoredDataDiskUri in $restoredDataDiskUris)
    {
        $dataDiskName = "{0}DataDisk{1}" -f $newVmName, $lun;
        $vmconfig = $vmconfig | Add-AzureRmVMDataDisk -Name $dataDiskName -VhdUri $restoredDataDiskUri -Lun $lun -CreateOption attach;
        $lun++;
    }

    New-AzureRmVM -ResourceGroupName $rgName -Location $location -VM $vmconfig;
}

Function FindMatchSnapshot($osDiskSnapshotTime, $snapshotPropertiesList)
{
    $count = $snapshotPropertiesList.Count;
    for ($i = 0; $i -lt $count; $i++)
    {
        if ($osDiskSnapshotTime -gt $snapshotPropertiesList[$i].SnapshotTime)
        {
            continue;
        }

        # wo consider snapshots for different data disks within 2 minutes (120 sec) should be related
        if (($snapshotPropertiesList[$i].SnapshotTime - $osDiskSnapshotTime).TotalSeconds -lt 120)
        {
            return $i;
        } else {
            return -1;
        }
    }
    return -1;
}

Function GetSnapshotListByBlob($blob)
{
    return $blob.Container.ListBlobs($blob.Name, $true, "Snapshots") | Where {$_.SnapshotTime -ne $null};
}

Function GetSnapshotPropertiesList($Snapshots, $blob)
{
    $Index = 0
    $SnapshotPropertiesList = @()
    Foreach($Snapshot in $Snapshots)
    {
        $Index++
        $SnapshotPropertiesList += [PSCustomObject]@{ID = $Index
                                                     BlobName = $blob.Name
                                                     SnapshotTime = $Snapshot.SnapshotTime
                                                     SnapshotUri = $Snapshot.Uri}
    }

    return $SnapshotPropertiesList;
}

#$Cred = New-Object System.Management.Automation.PSCredential("XXXXXXXX@XXXXXXXX.partner.onmschina.cn", (ConvertTo-SecureString "XXXXXXXX" -AsPlainText -Force));
#[void](Login-AzureRmAccount -EnvironmentName AzureChinaCloud -Credential $AzureRMCred);

[void](Select-AzureRmSubscription -SubscriptionName $SubscriptionName);

RestoreVM $ResourceGroupName $OldVMName $NewVMName;

调用方法需要额外指定一个新的虚拟机名字:

PS> &.\[ARM]restore_VM_from_snapshot.ps1 -SubscriptionName <Subscription Name> -ResourceGroupName <Resource Group Name> -OldVMName <Old VM Name> -NewVMName <New VM Name>

脚本执行结果:


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值