Azure仮想マシンのエクスポートとアップロードを駆使して節約する

# 概要
この記事では主に以下３つを記載している
- Azure仮想マシンのエクスポートとアップロードを使うことで節約になる事例紹介
- Azure仮想マシンにアタッチしているディスクをVHD（Virtual Hard Disk）ファイルとしてエクスポートする方法
- VHDファイルをAzureへアップロードし、仮想マシンを再構築する方法

# Azure仮想マシンのエクスポートとアップロードを使うことで節約になる事例紹介
Azure仮想マシンは使っていない間、CPUやメモリといったリソースの割り当てを解除（解放）することで、課金を抑えることが出来ます。

ただし、***仮想マシンを構成するディスク（ストレージ）については、課金がずっと発生***します。

そんなとき、ディスクを***VHDファイルとしてローカルPC等に保管する***ことで、Azureに保管するより安くなる場合があります。
そのため普段は使っていない仮想マシンがAzureに構築されたままになっている場合は、VHDファイルとしてエクスポートして保管することで課金を抑えましょう。

ちなみに私は過去に開発したシステムを念のために保管したい場合などにも利用しています（バックアップ目的）。

# Azure仮想マシンにアタッチしているディスクをVHD（Virtual Hard Disk）ファイルとしてエクスポートする方法
ディスクをVHDファイルとしてエクスポートする方法は簡単です。
画面操作のみでもエクスポートできます。

一応この記事では以下２つの方法を記載します。
1. 画面操作でVHDファイルをエクスポートする方法
2. AzureCLIでVHDファイルをエクスポートする方法

## 1.画面操作でVHDファイルをエクスポートする方法
1. エクスポートするディスクを確認する
    1. AzurePortalで仮想マシンページに遷移する
    2. 設定>「ディスク」を選択する
    3. OSディスクとデータディスクにアタッチされているディスクを確認する（必要なディスクをすべてエクスポートしておかないと、あとから仮想マシンを再構築できなくなります）
    また、このときにサイズ（GiB）を確認したほうが良いです。GiBが大きいほどエクスポート、インポートの操作に時間とお金がかかります。
    ![ディスクの確認.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1612369/e381b0a0-fd6b-fa98-13bc-68d9191ca45e.png)
2. 仮想マシンの割り当てを解除する
    1. AzurePortalで仮想マシンの概要ページに遷移する
    2. 「停止」をクリックする
3. VHDファイルをエクスポートする
    1. 1で確認したディスクをのページに遷移する
    2. 設定>「ディスクのエクスポート」に遷移する
    3. 「URLの生成」をクリックする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1612369/349b376b-c6d3-9aa3-b0f2-95ebd1d7b0ed.png)
    4. 「VHDファイルのダウンロード」をクリックする
    5. 拡張子を「.vhd」に変えて保存する（デフォルトだと拡張子は何もない状態でダウンロードされます。あとから変更も可能ではありますが、最初からvhdファイルとしてダウンロードすることをお勧めします）
4. 1で確認したディスクの数だけ、3を繰り返す

## 2.AzureCLIでVHDファイルをエクスポートする方法
AzureCLIのインストールなどは他の記事を参考にしてください。
以下のスクリプトを保存してPowerShellから実行する。

```powershell:AzureVMBackup.ps1
<#
    .Description
    リソースグループ内のすべてのVMをVHDとして保管します
#>

Param(
    [Parameter(Mandatory,
        HelpMessage = 'バックアップ対象のVMが含まれるリソースグループ名')]
    [String] 
    $ResourceGroup,

    [Parameter(Mandatory,
        HelpMessage = 'バックアップ対象のシステム（VM）名')]
    [String] 
    $SystemName,

    [Parameter(Mandatory,
        HelpMessage = 'バックアップ先のフォルダパス')]
    [String] 
    $VMImagePath
)

$ErrorActionPreference = 'Stop'

# ログインする
az login

$CurrentTime = Get-Date -Format 'yyyyMMddHHmmss'
$VMImageDownloadFolderPath = $VMImagePath + '\' + $SystemName + '\' + $CurrentTime

# リソースグループ内のVMを取得する
$VmList = az resource list `
    --resource-group $ResourceGroup `
    --resource-type Microsoft.Compute/virtualMachines `
    --output json | ConvertFrom-Json

# リソースグループ内のディスクを取得する
$DiskList = az resource list `
    --resource-group $ResourceGroup `
    --resource-type Microsoft.Compute/disks `
    --output json | ConvertFrom-Json

# VHDをダウンロードするフォルダを新規で作成する
Write-Host 'Create a folder'
New-Item `
    -Path $VMImageDownloadFolderPath `
    -ItemType 'directory'

# AzureVMを停止（割り当て解除）する
Write-Host "Deallocate all VMs in a resource group"
foreach ($VmName in $VmList.name) {
    az vm deallocate `
        --resource-group $ResourceGroup `
        --name $VmName
}

foreach ($DiskName in $DiskList.name) {
    $VMImageDownloadFilePath = $VMImageDownloadFolderPath + '\' + $DiskName + '.vhd'

    # VHDダウンロード用のShared Access Signature(SAS)を生成する
    $Sas = az disk grant-access `
        --duration-in-seconds 3600 `
        --access-level Read `
        --resource-group $ResourceGroup `
        --name $DiskName `
        --output json | ConvertFrom-Json

    # VHDをダウンロードする
    Invoke-WebRequest `
        -Uri $Sas.accessSas `
        -OutFile $VMImageDownloadFilePath
}
```

# VHDファイルをAzureへアップロードし、仮想マシンを再構築する方法
AzureでVHDファイルをアップロードするには、一度ストレージアカウントを経由する方法がよく紹介されている。
ただ、ストレージアカウントをVHDファイルをアップロードするためだけに作るのは大変なので、AzurePowerShellを使って直接Azureリソースとしてディスクを作成する。

以下のスクリプトファイルをファイルに保存し、PowerShellで実行する

```powershell:AzureVMUpload.ps1
<#
    .Description
    指定したフォルダ内の仮想ハードディスク（VHD）（拡張子が".vhd"であるファイル）をアップロードする
#>

Param(
    [Parameter(Mandatory,
        HelpMessage = "リソースグループ")]
    [String]
    $ResourceGroup,

    [Parameter(Mandatory,
        HelpMessage = "VHDのフォルダパス")]
    [String]
    $VhdFolderPath
)

$ErrorActionPreference = "Stop"

# フォルダパスのチェック
if (!(Test-Path -Path $VhdFolderPath -PathType Container)) {
    Write-Error "The folder does not exist. The process will be terminated."
    return
}

$Location = "JapanEast"
$VmList = Get-ChildItem -Path $VhdFolderPath -Filter *.vhd

# AzureにVHDをアップロードする
Write-Host 'Upload all VHD'
foreach ($VmName in $VmList) {
    Add-AzVhd `
        -ResourceGroupName $ResourceGroup `
        -LocalFilePath $VmName `
        -Location $Location `
        -DiskName ([io.path]::GetFileNameWithoutExtension($VmName))
}
```

# 注意事項
VHDファイルはディスクサイズと同等になるので、かなり大きなファイルになります。
そのため、ダウンロード、アップロードで時間がかかる + 途中で失敗する可能性も大きいです。
私の経験から途中で失敗した場合は、ファイルサイズが明らかに異なるので、ファイルサイズの確認は必ず実施しましょう。

# 最後に
今回はAzureの仮想マシンをVHDファイルとして一度ローカルにダウンロードし、アップロードする手順を紹介しました。
一度VHDファイルに変換してしまえば、そのあとにさらに変換させ、VMWareやHyperV、AWSへの移行も可能です。
さらにこの記事で紹介したように、普段使っていないVMを安いストレージに保管することで、節約することも可能です。
色々な使い方がありますので、どうぞ参考にしてください。