diff --git a/.github/workflows/msi.yaml b/.github/workflows/msi.yaml
new file mode 100644
index 0000000..b295745
--- /dev/null
+++ b/.github/workflows/msi.yaml
@@ -0,0 +1,26 @@
+name: Build the MSI (Installer)
+
+on:
+ workflow_dispatch:
+
+jobs:
+ build-msi:
+ name: 🪟 Build the MSI
+ runs-on: windows-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: ⚙️ Setup dotnet
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 7.0.x
+
+ - name: 🔨 Build the MSI
+ run: ./msi/build.ps1
+
+ - name: 📦 Upload the MSI
+ uses: actions/upload-artifact@v4
+ with:
+ name: "WSL USB Manager.msi"
+ path: "msi/bin/Release/WSL USB Manager.msi"
diff --git a/msi/.gitignore b/msi/.gitignore
new file mode 100644
index 0000000..9645371
--- /dev/null
+++ b/msi/.gitignore
@@ -0,0 +1,4 @@
+obj
+.wix
+bin
+staging
\ No newline at end of file
diff --git a/msi/Package.wxs b/msi/Package.wxs
new file mode 100644
index 0000000..7095b51
--- /dev/null
+++ b/msi/Package.wxs
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/msi/README.md b/msi/README.md
new file mode 100644
index 0000000..58c0a8e
--- /dev/null
+++ b/msi/README.md
@@ -0,0 +1,7 @@
+# Create an MSI (Installer)
+
+From the root of the repository, run the build script:
+
+```powershell
+./msi/build.ps1
+```
diff --git a/msi/WixUIBannerBmp.png b/msi/WixUIBannerBmp.png
new file mode 100644
index 0000000..1c8fcbb
Binary files /dev/null and b/msi/WixUIBannerBmp.png differ
diff --git a/msi/WixUIDialogBmp.png b/msi/WixUIDialogBmp.png
new file mode 100644
index 0000000..592f03c
Binary files /dev/null and b/msi/WixUIDialogBmp.png differ
diff --git a/msi/build.ps1 b/msi/build.ps1
new file mode 100644
index 0000000..273e406
--- /dev/null
+++ b/msi/build.ps1
@@ -0,0 +1,26 @@
+# Copyright (c) 2024 JP Hutchins
+# SPDX-License-Identifier: MIT
+
+# Build the release
+cargo build --release
+
+# Clean the msi directory
+dotnet clean msi
+
+# mkdir the staging directory if it doesn't exist
+New-Item -ItemType Directory -Path msi/staging -Force
+
+# Copy the files to the staging directory
+Copy-Item target/release/wsl-usb-manager.exe msi/staging/wsl-usb-manager.exe
+Copy-Item LICENSE.md msi/staging/LICENSE.md
+
+# Run a dotnet build with some environment variables set
+$env:COMPANY_NAME = "WSL USB Manager"
+$env:PRODUCT_NAME = "WSL USB Manager"
+$env:APP_NAME = "wsl-usb-manager"
+$env:EXE_NAME = "wsl-usb-manager.exe"
+$env:VERSION = (Get-Content Cargo.toml | Select-String -Pattern '^version\s*=\s*"(.*)"' | ForEach-Object { $_.Matches[0].Groups[1].Value })
+$env:PORTABLE_PATH = "staging"
+$env:MSI_NAME = "WSL USB Manager"
+
+dotnet build msi --configuration Release
diff --git a/msi/wsl-usb-manager.wixproj b/msi/wsl-usb-manager.wixproj
new file mode 100644
index 0000000..cde9bf3
--- /dev/null
+++ b/msi/wsl-usb-manager.wixproj
@@ -0,0 +1,25 @@
+
+
+
+
+
+ ApplicationFiles=$(PORTABLE_PATH)/
+
+ $(MSI_NAME)
+
+
+
+
+
+
+
+
+
+ HarvestedComponents
+ APPLICATIONFOLDER
+ true
+ true
+ var.ApplicationFiles
+
+
+
\ No newline at end of file