SVN と CruiseControl.NET+NAnt で運用してきたレガシーな .NET Framework 4.8 プロジェクトを、GitLab CI/CD 上で自動ビルド・自動テストできるようにしたい――そんなときに必要になるのが、Visual Studio をフルインストールせずに msbuild ベースのパイプラインを構成する具体的な手順です。本記事では、移行の全体像から、GitLab Runner の設定例、SDK スタイルプロジェクトへの段階的なモダナイズ戦略まで、現場でそのまま使えるレシピとして整理します。
.NET Framework 4.8 レガシープロジェクトを CI/CD 対応させる全体像
まずは、SVN+CruiseControl.NET+NAnt という古い構成から、GitLab CI/CD に移行する際の「全体像」を押さえておきます。ゴールは次のようなイメージです。
- ソースコード管理:SVN → GitLab(Git)
- ビルドエンジン:CruiseControl.NET → GitLab CI
- ビルドスクリプト:NAnt → PowerShell(あるいはバッチ)+ msbuild
- ビルド環境:Visual Studio 本体 → Visual Studio Build Tools のみ
- 将来:旧 csproj → SDK スタイルプロジェクト → .NET 7/8 などへ段階的モダナイズ
このときの典型的な悩みが「Windows VM 上で msbuild が見つからずビルドできない」「NAnt を捨てたいが、どう書き換えればよいか」「GitLab CI の .gitlab-ci.yml をどう書けばよいか」といった点です。そこでまず、主な課題と解決策を一覧にしておきます。
| 課題 | 解決策・手順 | 備考・補足 |
|---|---|---|
| msbuild が見つからない / Visual Studio を入れたくない | 「Visual Studio 2022 Build Tools」(GUI なし・無料)をビルドサーバーや Windows コンテナにインストールし、.NET デスクトップ ビルドツールを有効化する。 | vs_BuildTools.exe からインストール。これだけで msbuild.exe とテストランナーが揃う。 |
| NAnt 依存を取り除きたい | NAnt のビルドスクリプトを PowerShell やバッチに書き換え、msbuild コマンドを直接叩く構成に変更する。 | 複雑な XML スクリプトを脱却し、誰でも読めるスクリプトにすることで保守性が大きく向上。 |
| NuGet パッケージの復元 | packages.config プロジェクトは nuget.exe restore もしくは msbuild /t:Restore を事前に実行する。 | .NET Framework 4.x では dotnet restore が効かないケースが多いため注意。 |
| SDK スタイルへ移行したい | まずはライブラリプロジェクトから SDK スタイルへ変換する。ASP.NET WebForms は MSBuild.SDK.SystemWeb などの SDK を利用して段階的に移行する。 | ライブラリは .NET Standard 2.0 化しておくと、将来の .NET 8/9 への移行が楽になる。 |
| GitLab CI へ組み込みたい | Windows Runner に Build Tools を事前インストールし、.gitlab-ci.yml から nuget restore → msbuild → テスト → アーティファクト出力の流れを自動化する。 | 自前 VM でも、Microsoft の .NET Framework SDK コンテナでもよい。組織のポリシーに合わせて選択。 |
既存レガシー環境の棚卸しと移行戦略
いきなり GitLab CI を書き始めるのではなく、まずは「何をどの順番で捨てるか・残すか」を決めておくと失敗しにくくなります。
現状の把握チェックリスト
- ソース管理は SVN か? ブランチ運用はどうなっているか?
- CruiseControl.NET の設定 XML 内で、どの NAnt ターゲットが呼ばれているか?
- NAnt スクリプトではどのタスク(compile / nunit / zip / ftp など)を使っているか?
- プロジェクトの種類:Class Library / Console / WPF / WinForms / ASP.NET WebForms / WCF など
- NuGet 管理方式:packages.config か、既に PackageReference を使っているか?
この情報がまとまっていれば、「最初の一歩」は次のように決められます。
- SVN → GitLab への移行は、現行のリリースブランチ単位で履歴を最低限残す
- CruiseControl.NET は「見るだけ」にしておき、新 CI が安定してから停止する
- NAnt スクリプトは、まず「ビルド周り」だけ PowerShell に移植し、デプロイ作業は後で置き換える
一気にすべてをモダン化しようとすると必ず詰まるので、「ビルドが再現できること」を最優先にした段階的移行が現実的です。
ビルド環境の準備:Visual Studio Build Tools と msbuild
.NET Framework 4.8 のレガシープロジェクトを CI でビルドするうえでの最初の壁が、「msbuild がどこにも入っていない」問題です。開発者マシンには Visual Studio 本体が入っていても、ビルドサーバーや GitLab Runner の Windows VM には何も入っていない、というケースが多くあります。
Visual Studio 2022 Build Tools を入れる
Visual Studio 本体ではなく、ビルド専用の「Visual Studio Build Tools」をインストールすることで、次のものが使えるようになります。
- msbuild.exe(32bit/64bit)
- C# コンパイラ(csc.exe)
- テスト実行ツール(vstest.console.exe など)
- 一部の NuGet 関連ツール
インストール時に必ずチェックしておきたいワークロード/コンポーネントの例です。
- .NET デスクトップ開発ツール(.NET Framework 4.x のビルドに必要)
- MSBuild ツール
- NuGet パッケージ マネージャー
- 必要であれば C++ ビルドツール(C++/CLI との混在プロジェクトがある場合)
インストールが完了したら、コマンドプロンプトや PowerShell から次のように確認します。
where msbuild
正しくインストールされていれば、例えば次のようなパスが表示されます。
C:\Program Files\Microsoft Visual Studio\2022\BuildTools\MSBuild\Current\Bin\MSBuild.exe
64bit 版を使いたい場合は MSBuild\Current\Bin\amd64 配下の msbuild.exe を使います。ソリューションの規模が大きい場合は 64bit 版を使うことでメモリ不足エラーを回避しやすくなります。
PATH への追加とバージョン固定
GitLab Runner で msbuild を呼び出すときに、毎回フルパスを書くのは面倒なので、ビルドサーバーの環境変数に msbuild のパスを足しておくと便利です。
setx PATH "%PATH%;C:\Program Files\Microsoft Visual Studio\2022\BuildTools\MSBuild\Current\Bin"
ただし、将来別バージョンの Build Tools を追加インストールする可能性がある場合は、あえて PATH には追加せず、スクリプト側でフルパスを指定する方が「どのバージョンでビルドしているか」が明確になります。
NAnt スクリプトを PowerShell+msbuild に置き換える
次のステップは、CruiseControl.NET から呼ばれている NAnt スクリプトを、よりシンプルな PowerShell スクリプトに置き換えることです。ここでは、よくある NAnt の構成を例に変換してみます。
典型的な NAnt スクリプトの例
<project name="SampleApp" default="build">
<property name="configuration" value="Release" />
<target name="clean">
<delete dir="build" />
</target>
<target name="restore-packages">
<exec program="nuget.exe" commandline="restore SampleApp.sln" />
</target>
<target name="build" depends="clean, restore-packages">
<exec program="msbuild.exe"
commandline="SampleApp.sln /p:Configuration=${configuration}" />
</target>
</project>
これを PowerShell に置き換えると次のようになります。
param(
[string]$Configuration = "Release"
)
$ErrorActionPreference = "Stop"
$solution = "SampleApp.sln"
$msbuild = "C:\Program Files\Microsoft Visual Studio\2022\BuildTools\MSBuild\Current\Bin\MSBuild.exe"
$nuget = ".\tools\nuget.exe" # リポジトリ内に格納しておく前提
Write-Host "=== Clean ==="
if (Test-Path "build") {
Remove-Item "build" -Recurse -Force
}
Write-Host "=== NuGet Restore ==="
& $nuget restore $solution
Write-Host "=== MSBuild ==="
& $msbuild $solution /p:Configuration=$Configuration /m
Write-Host "Build completed."
ポイントは次の通りです。
- NAnt のターゲットを、そのまま PowerShell の処理ブロックとして書き直す
- nuget.exe や msbuild.exe のパスは変数にまとめておく(後から差し替えやすくする)
- エラー時には即座に停止するよう
$ErrorActionPreference = "Stop"を設定 - 最初はリポジトリ直下に
build.ps1として置き、ローカルでも GitLab CI でも同じスクリプトを使う
この PowerShell スクリプトが安定して動くようになれば、CruiseControl.NET 側から NAnt の代わりに build.ps1 を呼ぶこともできますし、GitLab CI からも同じスクリプトを使い回せます。
NuGet パッケージの復元戦略(packages.config と PackageReference)
.NET Framework 4.8 プロジェクトでは、NuGet パッケージの管理方法が混在していることがよくあります。CI に組み込む前に、各プロジェクトがどちらの方式かを確認しておきましょう。
packages.config 方式の場合
古いプロジェクトでは、ソリューション直下に packages フォルダがあり、各プロジェクトに packages.config が置かれている形が一般的です。この場合、CI では次のいずれかの方法で復元します。
- nuget.exe を使う
nuget.exe restore SampleApp.sln
- msbuild の Restore ターゲットを使う
msbuild SampleApp.sln /t:Restore
どちらでもよいですが、既に nuget.exe を運用で使っているのであれば、まずは nuget.exe を CI にもそのまま持ち込む方が安全です。
PackageReference 方式の場合
最近の Visual Studio で「パッケージ管理形式を変更」したプロジェクトは、csproj 内で <PackageReference> を使う形になっています。この場合は、次のように msbuild だけで復元とビルドを行うことができます。
msbuild SampleApp.sln /t:Restore,Build /p:Configuration=Release
.NET Framework 4.8 でも PackageReference は利用可能なので、packages.config の多いソリューションでも、少しずつ PackageReference へ移行していくと CI の設定がシンプルになります。
GitLab Runner(Windows)の準備と構成パターン
.NET Framework 4.8 は Windows 専用ランタイムのため、GitLab CI でも Windows Runner が必要になります。代表的な構成パターンは次の 2 つです。
| パターン | 概要 | メリット | デメリット |
|---|---|---|---|
| 自前 Windows VM + Shell Executor | オンプレやクラウドの Windows Server / Windows 11 に Runner をインストールし、PowerShell スクリプトを実行する。 | 既存資産(ファイル共有、IIS など)との連携が楽。トラブルシュートもしやすい。 | イメージの再現性が低く、「あのサーバーだけ動く」状態になりやすい。 |
| Windows コンテナ + Docker Executor | Visual Studio Build Tools や .NET Framework SDK 入りの Windows コンテナイメージを用意し、その上でビルドを行う。 | イメージを固定すれば、どこでも同じ環境でビルドできる。スケールアウトも容易。 | Windows コンテナの扱いに慣れが必要。イメージビルドのメンテナンスが増える。 |
まずは「自前 Windows VM + Shell Executor」で始め、運用が安定してきたら Windows コンテナ化を検討する、という順序がおすすめです。
Windows Runner の登録イメージ
GitLab Runner をインストールしたら、管理者権限の PowerShell で次のように登録します。
gitlab-runner register `
--url https://gitlab.example.com/ `
--registration-token XXXXXXXXXXXX `
--executor shell `
--description "windows-build-runner" `
--tag-list "windows,dotnet48" `
--non-interactive
これで、.gitlab-ci.yml 側から tags: ["windows", "dotnet48"] を指定することで、この Runner 上でジョブが実行できるようになります。
GitLab CI の基本構成:.gitlab-ci.yml の例
ここから、実際に .gitlab-ci.yml をどのように書くかを見ていきます。最もシンプルな例として、次のようなステージ構成を考えます。
| ステージ | 役割 | 主な処理 |
|---|---|---|
| restore | NuGet パッケージの復元 | nuget.exe restore / msbuild /t:Restore |
| build | ソリューションのビルド | msbuild /p:Configuration=Release |
| test | 単体テストの実行 | vstest.console.exe / MSTest など |
| pack | アーティファクトの作成 | zip 作成、nuget pack、配布物フォルダへの集約など |
.gitlab-ci.yml サンプル(Windows Shell Executor 向け)
stages:
- restore
- build
- test
- pack
variables:
CONFIGURATION: "Release"
SOLUTION_FILE: "SampleApp.sln"
default:
tags:
- "windows"
- "dotnet48"
before_script:
- 'echo Running on %COMPUTERNAME%'
- 'echo Using configuration: %CONFIGURATION%'
restore:
stage: restore
script:
- 'powershell -ExecutionPolicy Bypass -File .\build\restore.ps1 -Solution %SOLUTION_FILE%'
artifacts:
paths:
- packages/
expire_in: 1h
build:
stage: build
script:
- 'powershell -ExecutionPolicy Bypass -File .\build\build.ps1 -Solution %SOLUTION_FILE% -Configuration %CONFIGURATION%'
artifacts:
paths:
- src/**/bin/%CONFIGURATION%/
expire_in: 1 week
test:
stage: test
script:
- 'powershell -ExecutionPolicy Bypass -File .\build\test.ps1'
artifacts:
when: always
paths:
- TestResults/
expire_in: 1 week
pack:
stage: pack
script:
- 'powershell -ExecutionPolicy Bypass -File .\build\pack.ps1'
artifacts:
paths:
- artifacts/
expire_in: 1 month
only:
- main
- tags
ここでは、実際のビルドロジックはすべて PowerShell スクリプト(build\restore.ps1 など)に寄せ、.gitlab-ci.yml からはそれらを呼び出すだけにしています。この構造にしておくと、ローカル開発環境でも同じ PowerShell スクリプトを叩くことで、「CI と同じ手順でビルド・テストする」ことができます。
テスト自動化:vstest.console.exe や MSTest の活用
CI/CD では、ビルドが通るだけでなく、自動テストも一緒に回しておくことが重要です。.NET Framework 4.8 では次のようなテストランナーがよく使われます。
- MSTest(.testproj)
- NUnit
- xUnit.net
Visual Studio Build Tools を入れていれば、通常 vstest.console.exe が利用可能です。例えば、テスト DLL をまとめて実行する PowerShell スクリプトは次のように書けます。
$testDlls = Get-ChildItem ".\test" -Recurse -Filter "*Tests.dll"
$vstest = "C:\Program Files\Microsoft Visual Studio\2022\BuildTools\Common7\IDE\Extensions\TestPlatform\vstest.console.exe"
$resultsDir = "TestResults"
if (!(Test-Path $resultsDir)) {
New-Item -ItemType Directory -Path $resultsDir | Out-Null
}
foreach ($dll in $testDlls) {
Write-Host "Running tests in $($dll.FullName)"
& $vstest $dll.FullName `
/Logger:trx `
/ResultsDirectory:$resultsDir
}
出力された TRX ファイルは GitLab CI のジョブログからダウンロードできるように、artifacts.paths に追加しておきます。将来的に GitLab のテストレポート機能と連携したい場合は、TRX を JUnit 形式に変換するスクリプトを追加することもできます。
成果物(アーティファクト)管理と配布
レガシーな .NET Framework プロジェクトでは、ビルド成果物の配布方法も多様です。ファイルサーバーへコピー、IIS への WebDeploy、セットアップ EXE の発行、NuGet パッケージ化など、現状の運用を整理しつつ、GitLab CI のアーティファクト機能と組み合わせていきます。
代表的な成果物パターン
| 種類 | 用途 | CI/CD での扱い |
|---|---|---|
| bin/Release 一式 | オンプレサーバーへの手動デプロイ、ファイルコピー | zip に固めて artifacts として保存。運用担当者がダウンロードして適用。 |
| NuGet パッケージ | 社内共通ライブラリ、SDK など | nuget pack または msbuild /t:Pack で nupkg を作成し、社内 NuGet フィードへ push。 |
| WebDeploy パッケージ | ASP.NET Web アプリの自動デプロイ | msbuild の Web 配置ターゲットを利用して zip を生成し、必要に応じてステージング環境へ自動適用。 |
最初から自動デプロイまでやろうとせず、まずは「誰がビルドしても同じ成果物が得られる」状態を目指すのがおすすめです。そのうえで、ステージング環境への自動デプロイ、本番環境への半自動デプロイなどを段階的に足していきます。
旧式 csproj から SDK スタイルへの段階的移行
将来的に .NET 7/8 など新しい .NET へ移行したい場合、避けて通れないのが「SDK スタイルプロジェクト」への変換です。ただし、ASP.NET WebForms をいきなり .NET 7 に載せ替えるのは現実的ではないため、まずはライブラリから SDK スタイルへ移行するのがポイントです。
ライブラリプロジェクトの SDK スタイル化
クラスライブラリ(.csproj)の場合、次のような方針で SDK スタイルへ移行できます。
- 対象プロジェクトを Visual Studio 2022 で開く
- 「プロジェクトの移行」機能や拡張機能を利用して SDK スタイルに変換する
- 不要になった
<Compile Include="..." />などの項目を削除し、SDK スタイル標準の構成に整える
SDK スタイルの csproj は、非常にシンプルです。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
</Project>
この段階では、TargetFramework をあえて net48 のままにしておき、「ビルド結果は変えずにプロジェクト形式だけを変えた状態」を作ることが重要です。CI が安定して動くのを確認してから、別ブランチで .NET Standard 2.0 などへの移行を検討すると、リスクを小さくできます。
ASP.NET WebForms / System.Web プロジェクトの注意点
ASP.NET WebForms や古い MVC(System.Web ベース)のアプリは、そのままでは汎用の SDK スタイルに変換できません。この場合、次のようなアプローチを取ります。
- まずはライブラリ層(ビジネスロジック)を SDK スタイル化し、UI 層(WebForms)は旧式のまま残す
- Web プロジェクトの csproj は、専用の MSBuild SDK(例:SystemWeb 向け SDK)を利用して SDK スタイル風にする
- UI 層を .NET 7/8 の MVC / Razor Pages / Blazor などへ載せ替える場合は、別システム並みの工数がかかる前提で計画する
CI 観点では、「ビルド方法が安定しているか」が最重要なので、SDK スタイル化は小さな単位で実施し、毎回 GitLab CI のパイプラインを通して確認するのが安全です。
トラブルシューティングのチェックリスト
実際に GitLab CI で .NET Framework 4.8 のビルドを回し始めると、さまざまなエラーに出会います。よくある原因と対処のヒントを整理しておきます。
| 症状 | 原因の例 | 対処のヒント |
|---|---|---|
| msbuild が見つからない | Build Tools がインストールされていない / PATH が通っていない | ビルドサーバーに Visual Studio 2022 Build Tools をインストールし、where msbuild で確認。必要ならフルパスで指定。 |
| ビルド時にメモリ不足で落ちる | 巨大ソリューションを 32bit msbuild でビルドしている | 64bit msbuild(amd64 ディレクトリ)を使用し、/m オプションの並列度を調整する。 |
| NuGet パッケージが見つからない | packages フォルダが存在しない / プロキシ越しの接続に失敗 | nuget.exe を使って明示的に restore する。社内 NuGet サーバーを使う場合は nuget.config をリポジトリに含める。 |
| ローカルではビルド成功、CI では失敗 | CI 環境にだけ足りない SDK・コンポーネントがある | Build Tools のワークロードを見直し、ローカルと CI をできるだけ同一構成にする。「ローカルで Build Tools だけでビルドする」テストを行う。 |
| Web プロジェクトの発行時にエラー | UseWPP_CopyWebApplication などの MSBuild プロパティの違い | 既存の発行プロファイル(.pubxml)の設定を確認し、CI の msbuild パラメータと揃える。 |
段階的モダナイズのロードマップ例
最後に、レガシー .NET Framework 4.8 プロジェクトを、GitLab CI/CD と SDK スタイルへ段階的にモダナイズするためのロードマップ例をまとめます。
フェーズ 1:ビルド環境の近代化
- ビルドサーバーに Visual Studio 2022 Build Tools をインストール
- ローカルで NAnt → PowerShell+msbuild のビルドスクリプトを作成し、CruiseControl.NET でも置き換え
- NuGet 復元をスクリプト化し、誰が実行しても同じ成果物になる状態を作る
フェーズ 2:GitLab CI/CD への移行
- GitLab Runner(Windows)を用意し、Shell Executor で登録
- .gitlab-ci.yml を作成し、restore → build → test → pack のパイプラインを構築
- CruiseControl.NET を「バックアップ」として残しつつ、GitLab CI を新しい標準にしていく
フェーズ 3:SDK スタイル・テストの強化
- クラスライブラリから順に SDK スタイル(net48)へ変換
- テストプロジェクトも SDK スタイル化し、vstest でまとめて実行できるよう整理
- カバレッジ計測や静的解析ツールをパイプラインに組み込む
フェーズ 4:.NET 7/8 など新ランタイムへの移行
- ビジネスロジック層を .NET Standard 2.0 / .NET 6+ に移行
- UI 層(WebForms など)を新しいアーキテクチャへリプレース(別プロジェクト扱い)
- GitLab CI/CD で .NET Framework パイプラインと .NET 7/8 パイプラインを並行運用
このようにフェーズを区切っておくことで、「今日は Build Tools を入れるところまで」「今月はライブラリ 3 本だけ SDK スタイル化する」といった小さなマイルストーンを設定しやすくなります。結果として、日々の開発を止めることなく、レガシー資産を少しずつモダンな世界へ引き上げていくことができます。
まとめ:レガシーでも CI/CD は十分に構築できる
.NET Framework 4.8 のレガシープロジェクトでも、次のポイントさえ押さえておけば、GitLab CI/CD 上で安定した自動ビルド環境を構築できます。
- Visual Studio Build Tools を使うことで、Visual Studio 本体なしで msbuild を動かせる
- NAnt スクリプトは PowerShell+msbuild に書き換え、CI・ローカル共通のビルドスクリプトにする
- NuGet 復元は packages.config / PackageReference それぞれに合った方法を選ぶ
- GitLab Runner(Windows)と .gitlab-ci.yml を組み合わせて、restore → build → test → pack の流れを自動化する
- SDK スタイル化はライブラリから少しずつ進め、CI を通して安全に移行を確認する
レガシー環境だからといって、CI/CD をあきらめる必要はありません。むしろ、壊したくない大事なシステムだからこそ、「誰がビルドしても同じ結果が得られる」パイプラインを先に整えることが、長期的なリスクを減らす一番の近道になります。本記事の内容をベースに、自社の事情に合わせた .NET Framework 4.8 向け GitLab CI/CD パイプラインを設計してみてください。

コメント