Azure Virtual Desktop の Start Virtual Machine on Connect と非公式な VM 内自己割り当て解除をやってみた

## 背景と目的

Azure Virtual Desktop (AVD) の [Start VM on Connect](https://docs.microsoft.com/en-us/azure/virtual-desktop/start-virtual-machine-connect) で VM の自動起動が出来ます。しかしながら、MS のドキュメントを見ると「[ユーザーは、クライアントから VM をオフにできますか？](https://docs.microsoft.com/ja-jp/azure/virtual-desktop/start-virtual-machine-connect-faq#can-users-turn-off-the-vm-from-their-clients)」の中で VM の割り当ては解除されない、と書かれています。どうせなら、VM 内でシャットダウンする時に自身で割り当て解除出来たら、使っている時だけ課金されて、無駄なコストが発生しないハッピーな仕組みとなりますよね。

ということで、多少強引で非公式ではありますが、VM のマネージド ID にカスタムロールで割り当て解除権限を付与して自身の VM に権限を割り当て、VM 内で自己割り当て解除する Powershell スクリプトを実行する仕組みを試してみました。

## 前提条件

https://qiita.com/mnrst/items/63c6b9b77d58a557493d

以前、私が書いたこちらの記事で作成した Azure Virtual Desktop (AVD) 環境を前提とします。

## Start Virtual Machine on Connect

### 自動起動用のカスタムロールを作成します

```bash:bash
cat <<EOF > avd-startvm.json
{
    "Name": "Start VM on connect",
    "Description": "Start VM on connect with AVD",
    "IsCustom": true,
    "Actions": [
        "Microsoft.Compute/virtualMachines/start/action",
        "Microsoft.Compute/virtualMachines/read",
        "Microsoft.Compute/virtualMachines/instanceView/read"
    ],
    "DataActions": [
    ],
    "NotDataActions": [
    ],
    "AssignableScopes": [
        "/subscriptions/$(az account show --query id --output tsv)"
    ]
}
EOF

az role definition create \
  --role-definition @avd-startvm.json
```

### 作成したカスタムロールを Windows Virtual Desktop に割り当てます

```bash:bash
az role assignment create \
  --assignee $(az ad sp list \
  --display-name "Windows Virtual Desktop" \
  --query "[?displayName == 'Windows Virtual Desktop'].objectId" \
  --output tsv) \
  --role "Start VM on connect" \
  --resource-group ${prefix}-rg
```

### ホストプールのプロパティ「接続時に VM を起動する」を「はい」にします

```bash:bash
az rest \
  --method patch \
  --uri https://management.azure.com/subscriptions/$(az account show --query id --output tsv)/resourceGroups/${prefix}-rg/providers/Microsoft.DesktopVirtualization/hostPools/${prefix}-hp?api-version=2021-01-14-preview \
  --body '{
    "properties": {
        "startVMOnConnect": true
    }
  }'
```

これで停止済み (割り当て解除)の VM を自動で起動出来るようになります。

## 非公式な VM 内自己割り当て解除

### 「割り当て解除」権限のみのカスタムロールを作成します

```bash:bash
cat <<EOF > avd-deallocateSvm.json
{
    "Name": "Self Deallocate for VM",
    "Description": "Self Deallocate for VM",
    "IsCustom": true,
    "Actions": [
        "Microsoft.Compute/virtualMachines/deallocate/action"
    ],
    "DataActions": [
    ],
    "NotDataActions": [
    ],
    "AssignableScopes": [
        "/subscriptions/$(az account show --query id --output tsv)"
    ]
}
EOF

az role definition create \
  --role-definition @avd-deallocateSvm.json
```

### VM 自身にカスタムロールを割り当てます

```bash:bash
az role assignment create \
  --assignee $(az vm show \
  --resource-group ${prefix}-rg \
  --name ${prefix}-vm \
  --query identity.principalId \
  --output tsv) \
  --role "Self Deallocate for VM" \
  --scope $(az vm show \
  --resource-group ${prefix}-rg \
  --name ${prefix}-vm \
  --query id \
  --output tsv)
```

### VM 内に下記のスクリプトを用意して実行します

```powershell:powershell
$metadata = (Invoke-RestMethod -Headers @{"Metadata"="true"} -Method GET -Uri "http://169.254.169.254/metadata/instance?api-version=2021-02-01")
$resourceId = $metadata.compute.resourceId

$content = (Invoke-RestMethod -Headers @{"Metadata"="true"} -Method GET -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fmanagement.azure.com%2F")
$access_token = $content.access_token

Invoke-RestMethod -Headers @{"Authorization"="Bearer $access_token"} -Method POST -Uri "https://management.azure.com$resourceId/deallocate?api-version=2021-03-01"
```

すぐに「シャットダウン」と画面に表示されて、VM 割り当て解除処理が進みます。

## 参考

[Start Virtual Machine on Connect](https://docs.microsoft.com/en-us/azure/virtual-desktop/start-virtual-machine-connect)

[Azure PowerShell を使用してトークンを取得する](https://docs.microsoft.com/ja-jp/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token#get-a-token-using-azure-powershell)