Azure 書き込みアクセラレータのOnOffを自動化する

MシリーズのVMで利用できるディスク機能`書き込みアクセラレータ`の設定と解除を自動化してみます。
MシリーズはSAP HANAをAzure上に構築する際に利用する事が出来るVMサイズですね。
書き込みアクセラレータはディスクパフォーマンスを高くすることが出来る機能ですが、この機能を設定しているディスクはAzure Backupで除外されバックアップできません。

参考：[書き込みアクセラレータを使用する時の制限事項](https://docs.microsoft.com/ja-jp/azure/virtual-machines/how-to-enable-write-accelerator#restrictions-when-using-write-accelerator)

その為、Azure Backupの対象に含めるには書き込みアクセラレータを解除する必要があります。
定期的なバックアップ前後に書込みアクセラレータの設定と解除を手動で行うのは運用として耐えられないことだと思いますのでAzure Functionsで自動化します。
VMの起動と停止の処理は別途用意してある前提としています。

# VMとディスクを特定するための準備
処理を行うVMを特定するため、VMにタグをつけて対象ディスクと設定時刻、解除時刻をそれぞれ設定します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/b7a30efc-59ac-de60-2902-c29aa717c56b.png" width=95%>
対象ディスクのLunをカンマ区切りで指定します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/7e470021-04cd-ee4c-c384-40cb0797fa7c.png" width=65%>
バックアップ開始前にOff、起動前にOnとなるようにします。


# Azure Functionsの設定
VM等のリソースの参照や変更を行うためシステムマネージドIDをオンにして対象リソースにロールを割り当てます。
今回はリソースグループに`共同作成者`を割り当てました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/0ec992db-f9bb-8828-f441-9c1de4d0ce01.png" width=80%>
タイムゾーンを日本に変更するため、アプリケーション設定を追加します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/45d75799-70b0-275e-ec1c-81346000581c.png"  width=80%>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/a90414f0-224c-e59b-5ba8-59ae2b8c7dd4.png" width=65%>
TimerTriggerで15分毎に実行するようにします。
NCronTab式で記述します（参考：[NCRONTAB式](https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-timer?tabs=csharp#ncrontab-expressions)）

```plaintext:TimerTrigger
0 */15 * * * *
```

# 実行処理の記述
ホストキャッシュの設定を変更するとディスクが瞬断されるため（デタッチとアタッチします）、VMが起動している時は処理を中断するようにします。
15分毎に処理を実行するようにしたため、実行時刻～15分前の範囲で指定されたタグの時刻を処理対象とします。
以下のサンプルは書き込みアクセラレータOffの処理です。
Onにする場合は `$OnOff` の値を"On"にした関数を追加してください。

```powershell
$SubscriptionID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx"
$ResourceGroupName = "xxxxx"

$OnOff = "Off"
$SkipStatus = "PowerState/running"

$LunTagName = "WriteAcceleratorLun"
$switchTagName = "WriteAccelerator${OnOff}"
$MinuteRange = "15"
$CurrentTime = (Get-Date)
$RangeStart = $CurrentTime.AddMinutes(-$MinuteRange)

Write-Information "Write Acceleratorの設定${OnOff}処理を開始します。"

# Azureにログイン
Connect-AzAccount -Identity -SubscriptionId $SubscriptionID

$WriteAccelerator = $false
if ($switchTagName -match "On$") {
    $WriteAccelerator = $true
}

# 対象VMの取得
$VirtualMachines = Get-AzVM -ResourceGroupName $ResourceGroupName | where { $_.Tags.Keys -eq $switchTagName }

Write-Information "対象時間：${RangeStart}～${CurrentTime}"

foreach ($VirtualMachine in $VirtualMachines) {

    $Tagval = $VirtualMachine.Tags.$switchTagName

    Write-Information "対象VM：$(${VirtualMachine}.Name)"

    # 時間が設定されていない場合スキップ
    if (!$Tagval) {
        continue
    }

    $TagTime = $null
    $TagTime = [datetime]$Tagval
    Write-Information "指定時刻：${TagTime}"

    # 時刻の指定に不備がある場合スキップ
    if (!$TagTime) {
        Write-Error "タグ「${switchTagName}」の値が不正です。"
        continue
    }

    # 実行時間範囲外の場合スキップ
    if (($TagTime -le $RangeStart) -or ($CurrentTime -lt $TagTime)) {
        Write-Information "実行時間の範囲外のためスキップします。"
        continue
    }

    # VMが起動していたらスキップ
    $VMStatus = Get-AzVM -ResourceGroupName $VirtualMachine.ResourceGroupName -Name $VirtualMachine.Name -Status | Select-Object @{n = "Status"; e = { $_.Statuses[1].Code } }
    if ($VMStatus.Status -eq $SkipStatus) {
        Write-Information "仮想マシン「$($VirtualMachine.Name)」のステータスが「$($VMStatus.Status)」のためスキップします。"
        continue
    }

    $TargetLun = $VirtualMachine.Tags.$LunTagName
    Write-Information "対象Lun：${TargetLun}"
    # 対象ディスク取得
    $TargetDisks = $VirtualMachine.StorageProfile.DataDisks | where { $_.Lun -in $TargetLun.Split(",") }

    if (!$TargetDisks) {
        Write-Error "対象ディスクの取得に失敗したためスキップします。"
        continue
    }

    Write-Information "仮想マシン「$(${VirtualMachine}.Name)」のWrite Acceleratorの設定を${OnOff}にします。"
    foreach ($TargetDisk in $TargetDisks) {
        $resultDiskSetting = $null
        $resultDiskSetting = Set-AzVMDataDisk -VM $VirtualMachine -Name $TargetDisk.Name -WriteAccelerator:$WriteAccelerator
        if (!$resultDiskSetting) {
            Write-Error "ディスク「$($TargetDisk.Name)」のWrite Acceleratorの設定${OnOff}に失敗しました。"
            continue
        }
    }

    $resultVMUpdate = Update-AzVM -ResourceGroupName $VirtualMachine.ResourceGroupName -VM $VirtualMachine
    if (!$resultVMUpdate.IsSuccessStatusCode) {
        Write-Error "仮想マシン「$(${VirtualMachine}.Name)」のWrite Acceleratorの設定${OnOff}に失敗しました。"
        continue
    }

    Write-Information "仮想マシン「$(${VirtualMachine}.Name)」のWrite Acceleratorの設定${OnOff}に成功しました。"
}

Write-Information "Write Acceleratorの設定${OnOff}処理が終了しました。"
exit
```

# 実行結果
VMディスクでの状態を確認する以外にLog Analyticsでも結果を確認できます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/accd0982-292d-589b-8b4b-29b881d86a95.png" width=85%>
Azure Monitorで設定すればエラー検知してメールで通知することも可能です。