AzureBackupから可用性セットへリストアするPowerShellスクリプト

#はじめに
AzureのIaaS上で仮想マシンを構築・運用する場合、データやシステムの保護・復元手法として、標準サービスであるAzure Backupを用いることが多いかと思います。

Azure Backupはバックアップのスケジューリングやリストアが簡単な操作で実施できますが、仮想マシンのリストア時に可用性セットへ追加することができない、ストレージアカウント内のコンテナ名やVHD名、NIC名にランダムな文字列が付随し管理し辛いなどの不便な点もあります。

そこで本記事では、Azure Backupから仮想マシンを可用性セット内へリストアしつつリソースの名前を綺麗に整えるスクリプトを紹介します。

>2018/12/18追記
>現在では、Azure仮想マシンのディスク作成方法として以下の2種類が存在します。
>(1)VHDファイル + ストレージアカウントによる従来の方式、非管理ディスク
>(2)ディスクリソースによる新しい方式、管理ディスク
>管理ディスク方式で仮想マシンを作成した場合、PowerShellスクリプトを使用せずとも、Azure Backupでのリストア時に既存のディスクとリストアしたディスクの付け替えを自動で行う「ディスクの交換」が可能となりました。

#スクリプトの流れ
本記事のスクリプトを実行すると、以下の流れでAzure BackupからのVHDリストア、VHDファイル名変更、VHDから仮想マシン作成の処理を行います。

Azure BackupからのVHDリストアを行うと、元々の仮想マシンの構成情報を含んだconfigファイル(.json)が生成されるため、リストアされたVHDの名前や数、使用していたNICの名前、仮想マシンのサイズなどはそこから取り出すようにしています。

1. サブスクリプションへのログイン
2. Recovery Services Vaultから最新のバックアップアイテム情報を取得
3. バックアップアイテムからVHDファイルを復元
4. VHDリストア時に生成されるconfigファイルを端末へダウンロード、読み込み
5. リストアされたOS用、データディスク用VHDファイルの名前を変更しながら「vhds」コンテナにコピー
6. 既存の可用性セットの情報を取得
7. 既存のNICの情報を取得
8. 仮想マシンのデプロイ
9. VHD復元時に作成された、ストレージアカウント内のコンテナを削除

#手順
必要に応じて以下のパラメータ(変数)の値を編集し、スクリプトを実行します。
※NICは既存のものを使用するため、リストア対象の仮想マシンを削除した状態で実行してください。
※仮想マシン、ストレージアカウント、可用性セット、Recovery Servicesコンテナが同じリソースグループ内に含まれる想定です。

| 変数                   |       入力する値 |
|:-----------------------|:-------------------|
| $SubscriptionName      | 使用しているサブスクリプションの名前    |
| $ResourceGroupName     | 使用しているリソースグループ名 |
| $RecoveryServicesName  | 対象仮想マシンのバックアップデータが格納されているRecovery Servicesコンテナ名 |
| $VirtualMachineName    | 復元後の仮想マシン名 |
| $StorageAccountName    | Azure BackupからのVHD復元先となるストレージアカウント名 |
| $AvailabilitySetname   | 復元後の仮想マシンを追加する可用性セット名 |
| $Location              | 仮想マシンの復元先となるリージョン |

#スクリプト

```
$SubscriptionName = "subscription01"
$ResourceGroupName = "resourcegroup01"
$RecoveryServicesName = "recoverycontainer01"
$VirtualMachineName = "vm01"
$StorageAccount = "storage01"
$AvailabilitySetName = "availabilityset01"
$Location = "Japaneast"


#スクリプト開始
Write-Output "Azure RestoreからのVMリストアスクリプト:開始"(Get-Date)"`n"
Write-Output "Resource Group:$ResourceGroupName`n"
Write-Output "対象VM: $VirtualMachineName`n"


#ログイン
Write-Output "Azureサブスクリプションへのログイン:開始"(Get-Date)`n
$Credential = Get-Credential -Message "Azure Subscriptionへログインするためのアカウント、パスワードを入力"
Login-AzureRmAccount -Credential $Credential -SubscriptionName $SubscriptionName
Write-Output "サブスクリプション:$SubscriptionName`n"
Write-Output "Azureサブスクリプションへのログイン:完了"(Get-Date)"`n"


#対象Recovery Services Vaultの選択
Write-Output "対象Recovery Servicesコンテナの選択:開始"(Get-Date)"`n"
$Vault = Get-AzureRmRecoveryServicesVault -ResourceGroupName $ResourceGroupName -Name $RecoveryServicesName
Set-AzureRmRecoveryServicesVaultContext -Vault $Vault
Write-Output "選択されたRecovery Servicesコンテナ:"$Vault.Name"`n"
Write-Output "対象Recovery Servicesコンテナの選択:完了"(Get-Date)"`n"


#対象VMのバックアップアイテムを取得
Write-Output "`n対象VMのバックアップアイテムを取得:開始"(Get-Date)
$Container = Get-AzureRmRecoveryServicesBackupContainer  -ContainerType AzureVM -Status Registered -FriendlyName $VirtualMachineName
$BackupItem = Get-AzureRmRecoveryServicesBackupItem -Container $Container -WorkloadType AzureVM
Write-Output "`n対象VMのバックアップアイテムを取得:完了"(Get-Date)


# 過去 7 日間のバックアップの情報を取得し最新を利用
Write-Output "最新の復元ポイントを選択:開始"(Get-Date)"`n"
$StartDate = (Get-Date).AddDays(-7)
$EndDate = Get-Date
$RecoveryPoint = Get-AzureRmRecoveryServicesBackupRecoveryPoint -Item $BackupItem -StartDate $StartDate.ToUniversalTime() -EndDate $EndDate.ToUniversalTime()
Write-Output "選択された復元ポイント`n"
$RecoveryPoint[0]
Write-Output "最新の復元ポイントを選択:完了"(Get-Date)"`n"

#バックアップアイテムからVHDを復元開始
Write-Output "バックアップアイテムからVHDの復元開始:開始"(Get-Date)"`n"
$RestoreVHDJob = Restore-AzureRmRecoveryServicesBackupItem -RecoveryPoint $RecoveryPoint[0] -StorageAccountName $StorageAccount -StorageAccountResourceGroupName $ResourceGroupName
$RestoreVHDJob
Write-Output "バックアップアイテムからのVHDの復元開始:完了"(Get-Date)"`n"


#VHD復元のステータスチェック
Write-Output "VHD復元のステータスチェック:開始"(Get-Date)"`n"
Write-Output "1分間隔でステータスを表示します`n"
$RestoreVHDJob.Status
while($RestoreVHDJob.Status -ne "Completed") {
    Start-Sleep -Seconds 60
    $RestoreVHDJob = Get-AzureRmRecoveryServicesBackupJob -Job $RestoreVHDJob
    $RestoreVHDJob.Status
    if($RestoreVHDJob.Status -eq "Failed"){
        throw
    }
}
Write-Output "`nVHD復元のステータスチェック:完了"(Get-Date)


#ストレージアカウントのContext作成
Write-Output "ストレージアカウントのContext作成:開始"(Get-Date)"`n"
$StorageKey = (Get-AzureRmStorageAccountKey -ResourceGroupName $ResourceGroupName -Name $StorageAccount)[0].Value
$StorageContext = New-AzureStorageContext -StorageAccountName $StorageAccount -StorageAccountKey $StorageKey
Write-Output "ストレージアカウントのContext作成:完了"(Get-Date)"`n"


#リストアに必要なVHD情報の取得
Write-Output "復元されたVHD情報の取得:開始"(Get-Date)"`n"
$VHDDetails = Get-AzureRmRecoveryServicesBackupJobDetails -Job $RestoreVHDJob


#VHDが復元されたコンテナ名の取得
$TMPContainerName = $VHDDetails.Properties["Config Blob Container Name"]
Write-Output "VHD復元先コンテナ名:"$TMPContainerName"`n"


#復元されたVHD名の取得
$TMPVHDName = ""
Get-AzureStorageBlob -Container $TMPContainerName -Context $StorageContext | ForEach-Object{
    if($_.Name.Contains(".vhd")){
        $TMPVHDName = $_.Name
        Write-Output "復元されたVHD名:"$TMPVHDName"`n"
    }
}

#Configファイルのダウンロード、読み込み
$scriptPath = (Convert-Path .)
$destPath = $scriptPath+"\config.json"
$ConfigBlobName = $VHDDetails.Properties["Config Blob Name"]
Get-AzureStorageBlobContent -Container $TMPContainerName -Blob $ConfigBlobName -Destination $destPath -Context $StorageContext
$ConfigOBJ = ((Get-Content -Path $destPath -Encoding Unicode)).TrimEnd([char]0x00) | ConvertFrom-Json
$TmpVhdUri = $ConfigOBJ.'properties.storageProfile'.osDisk.vhd.uri
Write-Output "`n"


#OS用VHDのコピー
Write-Output "OS用VHDのコピー開始:開始"(Get-Date)"`n"
$DestOSVhdName = "$VirtualMachineName"+"-RestoredosDisk.vhd"
$BlobCopy = Start-AzureStorageBlobCopy -AbsoluteUri $TmpVhdUri -DestContainer vhds -DestBlob $DestOSVhdName -Context $StorageContext
Write-Output "OS用VHDのコピー開始:完了"(Get-Date)"`n"


#ステータスチェック
Write-Output "`nOS用VHDコピーのステータスチェック:開始"(Get-Date)
do{
    $CopyState = Get-AzureStorageBlobCopyState -Context $StorageContext -Container vhds -Blob $DestOSVhdName
    Start-Sleep -Seconds 5
    $Percent = $CopyState.BytesCopied / $CopyState.TotalBytes * 100
    Write-Progress -Activity "処理中..." -PercentComplete $Percent -CurrentOperation "$Percent% complete" -Status "Please wait."
}while($copyState.Status -eq "pending")
Write-Output "コピー完了`n"
Write-Output "OS用VHDコピーのステータスチェック:完了"(Get-Date)"`n"


#データ用VHDコピー
Write-Output "データディスク用VHDのコピー:開始"(Get-Date)"`n"


#データディスクの本数を取得
$DataDiskCount = $ConfigOBJ.'properties.storageProfile'.dataDisks.count


#コンテナ内のデータディスク用VHD名を取得し、リネームしつつコピー
for([int]$i=0;$i -lt $DataDiskCount;$i++){
    [int]$DiskNum = $i+1
    $DataDiskBlobUri = $ConfigOBJ.'properties.storageProfile'.DataDisks.item($i).vhd.uri
    $DestDataVhdName = $VirtualMachineName + "-RestoredDataDisk" + $DiskNum + ".vhd"

    #コピー開始
    Write-Output "$DiskNum 本目のデータディスクのコピー開始:"(Get-Date)"`n"
    $DataDiskCopy = Start-AzureStorageBlobCopy -AbsoluteUri $DataDiskBlobUri -DestContainer vhds -DestBlob $DestDataVhdName -Context $StorageContext

    #コピーのステータスチェック
    do{
        $CopyState = Get-AzureStorageBlobCopyState -Context $StorageContext -Container vhds -Blob $DestDataVhdName
        Start-Sleep -Seconds 5
        $Percent = $CopyState.BytesCopied / $CopyState.TotalBytes * 100
        Write-Progress -Activity "処理中..." -PercentComplete $Percent -CurrentOperation "$Percent% complete" -Status "Please wait."
    }while($copyState.Status -eq "pending")
    Write-Output "$DiskNum 本目のデータディスクのコピー完了"(Get-Date)"`n"
}

Write-Output "データディスク用VHDのコピー:完了"(Get-Date)"`n"


#既存可用性セットの取得
Write-Output "可用性セットの取得:開始"(Get-Date)"`n"
$AvailabilitySet = Get-AzureRmAvailabilitySet -ResourceGroupName $ResourceGroupName -Name $AvailabilitySetName
Write-Output "可用性セット名:"$AvailabilitySetName"`n"
Write-Output "可用性セットの取得:完了"(Get-Date)"`n"


#復元したVHDから仮想マシンをデプロイ
Write-Output "仮想マシンのデプロイ:開始"(Get-Date)"`n"


#既存NICの取得
$NICName = $ConfigOBJ.'properties.networkProfile'.networkInterfaces.id.split("/")[8]
$NIC = Get-AzureRmNetworkInterface -ResourceGroupName $ResourceGroupName -Name $NICName


# 仮想マシン設定の定義
$VMSize = $ConfigOBJ.'properties.hardwareProfile'.vmSize
$OSType = $ConfigOBJ.'properties.storageProfile'.osDisk.osType
$OSCaching = $ConfigOBJ.'properties.storageProfile'.osDisk.caching
$VM = New-AzureRmVMConfig -Name $VirtualMachineName -VMSize $VMSize -AvailabilitySetId $AvailabilitySet.Id
$VHDURI = "https://"+$StorageAccount+".blob.core.windows.net/vhds/"+$DestOSVhdName
if($OSType -eq "Windows"){
    $VM = Set-AzureRmVMOSDisk -VM $VM -VhdUri $VHDURI -Name "$VirtualMachineName-RestoredosDisk" -CreateOption attach -Windows -Caching $OSCaching
}elseif($OSType -eq "Linux"){
    $VM = Set-AzureRmVMOSDisk -VM $VM -VhdUri $VHDURI -Name "$VirtualMachineName-RestoredosDisk" -CreateOption attach -Linux -Caching $OSCaching
}

for([int]$j=0;$j -lt $DataDiskCount;$j++){
    $DataDiskName = $VirtualMachineName + "-RestoredDataDisk" + ($j+1) + ".vhd"
    $DataDiskUri = "https://"+$StorageAccount+".blob.core.windows.net/vhds/"+$DataDiskName
    $DiskCache = $ConfigOBJ.'properties.storageProfile'.dataDisks.item($j).caching
    $VM=Add-AzureRmVMDataDisk -VM $VM -Name $DataDiskName -VhdUri $DataDiskUri $Daatad -Caching $DiskCache -Lun $j -CreateOption Attach
}

    $VM = Add-AzureRmVMNetworkInterface -VM $VM -NetworkInterface $NIC
    $VM.NetworkProfile.NetworkInterfaces.Item(0).Primary = $true

    # 仮想マシンの新規作成
    Write-Output "仮想マシンのデプロイ`n"
    $DeployVM = New-AzureRmVM -ResourceGroupName $ResourceGroupName -Location $Location -VM $VM -DisableBginfoExtension
    if($DeployVM.IsSuccessStatusCode -eq $true){
    
    }else{
        throw
    }

Write-Output "仮想マシンのデプロイ:完了"(Get-Date)"`n"

#不要なコンテナの削除
Write-Output "不要なコンテナの削除:$TMPContainerName`n"
Remove-AzureStorageContainer -Name $TMPContainerName -Context $StorageContext -Force

Write-Output "Azure Recovery Services ContainerからのVMリストアスクリプト:完了"(Get-Date)"`n"


```