AzureでWindowsアプリのCI/CD環境を作る：DevDriveを組み合わせてみる


# 概要

開発者の手元のビルド環境だけではなく、どこでもリリースビルドが再現可能であること。
Warningの作り込みやUTコードのFailを早めに見つけてデグレを防ぐこと。
こうした、CI/CD的な考え方は、Windowsアプリ開発においても大事なところだと思います。（現場であまり重視されていない感もありますが・・・）

方法はいくつもあると思いますが、Azure Pipelines (DevOps)とAzure VMで実現できます。そのあたりの話はまた別の記事で書こうと思います。

## この記事の話は

Windows 11の新機能として登場したDevHomeやDevDriveなどは、もちろん開発環境の量産にも使えますが、実はCI/CDを大きく助けてくれる機能にもなりそうです。

今回は、その中でもDevDriveが導入できるかを試してみて、効果を見ていこうと思います。

# 結果まとめ

説明が長くなるので、最初に結果をまとめます。こんな感じの内容を説明していきます。

1. Azure Pipelinesのビルド環境にDevDriveを組み込むこと自体は難しくない
1. むしろWin11-23H2へのDevDriveの導入で、Defenderのアップデートが必要なのが罠だった
1. 中でも、一時ディスクDドライブにコマンドで作成する方法が、低コストで使いやすいと思ったので、それを紹介
1. 効果はいまいちだったが少なくともマイナスではない（[詳しくは次の記事に](https://qiita.com/spc_ksudoh/items/42d7ab8ad27c475d011c)）


# 環境の作り方

Azure Pipelinesのビルド環境にDevDriveを組み込む方法について、注意点なども触れながら順番に紹介します。

## DevDriveの導入

DevDriveは、ディスク上のパーティションを割り当てるか、もしくはvhdファイルを新規作成してDevDriveにするという作り方をします。注意点として、最低サイズが50GBです。50GB以上の空きパーティションか空き領域があれば、作成できるということになります。

とりあえず導入するための手順であれば、MSのドキュメントがちゃんとまとまっていますので、そちらを見るのが良いです。

[Windows 11 で Dev Drive を設定する | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/dev-drive/)


## DevDriveのウイルススキャンの影響を減らす

DevDriveのパフォーマンスが高いとされる理由の一つに、Defenderのウイルススキャンの影響を減らせるという点があります。「パフォーマンスモード」と呼ばれているようですが、ウイルススキャンを遅延させることで速度を上げるようです。これを使わないと、DevDriveのメリットは半減以下です。

幸い、DevDriveを作るだけで、デフォルトでパフォーマンスモードの対象になりました。この点は分かりやすいです。

### 意外な注意点

Defenderのアップデートが必要です。

Windows11 23H2を新規インストールすると、Defenderのバージョンが古くて、DevDriveパフォーマンスモードの条件を満たしません。2023/12前半時点では、2回くらい手動でアップデートを促してやらないと、条件を満たすバージョンになりませんでした。見た目にはDevDriveになっているので気付きづらく、今のところ注意が必要です。

参考：[パフォーマンス モードを使用して Dev Drive を保護する | Microsoft Learn](https://learn.microsoft.com/ja-jp/microsoft-365/security/defender-endpoint/microsoft-defender-endpoint-antivirus-performance-mode?view=o365-worldwide)

## DevDriveとAzure Pipelinesの組み合わせ方

まず、Azure PipelinesのSelf Hosted Agent（長いので、以下Agent）でビルドする時は、こんな感じになります。

```plantuml
@startuml

component "Azure VM" as vm {
    component "Agent" as agent
    component "VisualStudio（msbuild）" as build
    component "ローカルの一時フォルダ" as folder{
        component "ソースコード" as code
    }
}

agent --> build
build --> code

@enduml
```

この「ローカルの一時フォルダ」を、DevDriveにすれば良いことになります。幸い、Agentはセットアップする時に一時フォルダの場所をフルパスで指定できます。パスが存在していなくてもAgentは起動できるので、あとからDevDriveを作成・マウントしても問題ないので、このあとに出てくる一時ディスクを使えます。

## Azure VMの一時ディスクがお勧め

Azure VMには、OSディスクとデータディスクの他に、一時ディスクというものがあります。名前の通りでVM停止時などにデータが失われる可能性がありますが、必ずOSと同じハードに物理接続されているディスクであり、元々ページファイルなどを置く想定のディスクなので速度が期待できます。また、VMの料金に含まれています。

この特性は、一時的に使うだけのビルドPCの用途にはぴったりです。ただし、データが失われた場合はDevDriveを作り直す必要があります。これを毎回手動でやるのは厳しいですが・・・スクリプトで実現できました。次にそのやり方をまとめます。

## 一時ディスク上のDevDriveを使った、ビルド環境の運用方法

ここまでの内容を踏まえて、次のようにビルド環境を構築・運用できます。

1. Agentをセットアップして、一時フォルダのパスを存在しないドライブにしておく（「V:\AgentWork」など）
1. 「一時ディスク（Dドライブ）へvhdファイルを作成・マウントしてDevDriveに設定し、1で設定したドライブレターを与える」というスクリプトを作成し、タスクスケジューラの「スタートアップ時」イベントに登録する


これで、VMを起動するだけで使える、DevDriveでのビルド環境のできあがりです。次の図のようなものが実現できました。

```plantuml
@startuml

    component "Azure VM (Windows)" as vm {
        component "スクリプト" as script

        component "一時ディスク（Dドライブ）" as drive {
            component "vhdファイル" as vhd
        }

        component "DevDrive" as devDrive{
        component "ソースコード" as code
        }
            
        component "Agent" as agent
        component "VisualStudio（msbuild）" as build

    }

    actor "Azureユーザー" as azure
    azure -d-> vm : 1，VMを開始
    vm --> script : 2，タスクスケジューラの「スタートアップ時」で実行
    script --> vhd : 3，作成・マウント・フォーマットする
    vhd --> devDrive : 4，マウントされる

    azure --> agent : 5，パイプライン起動

    agent --> code : 6，チェックアウト
    agent --> build : 7，ビルド実行
    build --> code : 8，ビルド実行


@enduml
```

スクリプトについては、DevDriveの設定はFormatコマンドで行えるので、DiskPartと組み合わせて次のバッチファイルで行けます。

https://gist.github.com/suusanex/67610d6d8a7cfd7abca6e0e3108507d8

## 効果は？

色々試しましたが、少しは速くなるもののあまり効果はない印象です。このあたりは、別の記事で詳しく書こうと思います。

# まとめ

Azure VM＆Azure Pipelinesを使ったビルド環境に、DevDriveを組み込むことができました。
また、スクリプトで自動作成することで、Azure VMの一時ディスク上へDevDriveを作ることができるので、現状のVMのサイズによっては追加のコスト負担無しで使うことができます。
効果はいまいちでしたが（[次の記事](https://qiita.com/spc_ksudoh/items/42d7ab8ad27c475d011c)にまとめました）、マイナスではないので、試しに組み込んでみても良いと思います。