Add GuruRMM Agent MSI installer (WiX 5) — Phase 1 MVP
Signed Windows installer using our Azure Trusted Signing pipeline. Phase 1 scope: installs signed agent to Program Files, creates ProgramData dir, Apps & Features entry with proper publisher, clean install + uninstall. Phase 2 deferred: service registration, MSI properties for site-code injection, agent install/uninstall custom actions, firewall rules. Verified end-to-end on Windows workstation: - wix build produces 1.16 MB MSI - sign.ps1 signs it against gururmm-public-trust cert profile - msiexec /qn installs silently, signature chain verifies on installed binary - msiexec /x uninstalls cleanly, retains ProgramData Tooling prerequisites documented in installer/README.md. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
5
projects/msp-tools/guru-rmm/installer/.gitignore
vendored
Normal file
5
projects/msp-tools/guru-rmm/installer/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Build artifacts — reproducible from wxs + downloaded binary
|
||||||
|
*.msi
|
||||||
|
*.wixpdb
|
||||||
|
install-test.log
|
||||||
|
src/gururmm-agent.exe
|
||||||
97
projects/msp-tools/guru-rmm/installer/README.md
Normal file
97
projects/msp-tools/guru-rmm/installer/README.md
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# GuruRMM Agent MSI Installer
|
||||||
|
|
||||||
|
Signed Windows installer for the GuruRMM agent. Builds on Windows via WiX 5,
|
||||||
|
signed with Azure Trusted Signing. Produces a `gururmm-agent-<version>.msi`
|
||||||
|
suitable for double-click install, silent install via `msiexec /qn`, or GPO
|
||||||
|
Software Installation deployment.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
**Phase 1 (current):** MVP — installs binary to `C:\Program Files\GuruRMM\`,
|
||||||
|
creates `C:\ProgramData\GuruRMM\` data directory, Apps & Features entry with
|
||||||
|
proper publisher, clean silent install + uninstall.
|
||||||
|
|
||||||
|
**Phase 2 (planned):**
|
||||||
|
|
||||||
|
- `ServiceInstall` element to register the Windows service on install
|
||||||
|
- MSI properties for `SITE_CODE`, `SERVER_URL`, `API_KEY` passed at install time
|
||||||
|
- Custom actions to invoke the agent's native `install` / `uninstall` subcommands
|
||||||
|
- Firewall rule registration (if the tunnel subscriber path requires inbound)
|
||||||
|
- Start menu entry (optional; most customers don't need it for background agent)
|
||||||
|
|
||||||
|
## Prerequisites (build host)
|
||||||
|
|
||||||
|
- Windows 10 / 11 / Server 2019+ (WiX v5 is Windows-only per upstream)
|
||||||
|
- .NET SDK 8 — `winget install --id Microsoft.DotNet.SDK.8 -e`
|
||||||
|
- WiX v5 — `dotnet tool install --global wix --version 5.0.2`
|
||||||
|
- Windows SDK signtool — typically already present if Visual Studio Build Tools
|
||||||
|
or Windows SDK is installed
|
||||||
|
- Azure Trusted Signing `sign.ps1` + dlib at `C:\tools\trusted-signing\`
|
||||||
|
- `az login` active session with the `gururmm-build-signer` SP, or an
|
||||||
|
interactive user with the `Artifact Signing Certificate Profile Signer`
|
||||||
|
role on the `gururmm-public-trust` certificate profile
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd installer
|
||||||
|
.\build-msi.ps1 -Version 0.6.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Defaults:
|
||||||
|
- Downloads `gururmm-agent-windows-amd64-<version>.exe` from
|
||||||
|
`https://rmm-api.azcomputerguru.com/downloads/`
|
||||||
|
- Refuses to package an unsigned agent (verifies signature before packaging)
|
||||||
|
- Signs the resulting MSI against the `gururmm-public-trust` cert profile
|
||||||
|
- Emits `<msi>.sha256` alongside
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
- `-SkipSign` — build without signing (dev/test)
|
||||||
|
- `-KeepSource` — don't delete `src/gururmm-agent.exe` after build
|
||||||
|
- `-SourceUrl` — override download origin (e.g., for staging)
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Interactive (UAC prompt → "Verified publisher: Arizona Computer Guru LLC")
|
||||||
|
.\gururmm-agent-0.6.1.msi
|
||||||
|
|
||||||
|
# Silent (no UI, return code 0 = success, writes verbose log)
|
||||||
|
msiexec /i gururmm-agent-0.6.1.msi /qn /l*v install.log
|
||||||
|
|
||||||
|
# Silent with (future) site-code baking once Phase 2 custom actions land
|
||||||
|
msiexec /i gururmm-agent-0.6.1.msi /qn SITE_CODE=xyz123 SERVER_URL=wss://rmm-api.example.com/ws /l*v install.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Uninstall
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Via Apps & Features: "GuruRMM Agent" → Uninstall
|
||||||
|
# Or silent:
|
||||||
|
msiexec /x gururmm-agent-0.6.1.msi /qn
|
||||||
|
|
||||||
|
# By ProductCode if original MSI isn't handy:
|
||||||
|
msiexec /x {PRODUCT-CODE-GUID-HERE} /qn
|
||||||
|
```
|
||||||
|
|
||||||
|
Uninstall removes `C:\Program Files\GuruRMM\` contents but **preserves
|
||||||
|
`C:\ProgramData\GuruRMM\`** (logs, config, device identity). Manually delete
|
||||||
|
that directory if doing a full purge.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `gururmm.wxs` | WiX installer definition — canonical source |
|
||||||
|
| `build-msi.ps1` | Build + sign wrapper |
|
||||||
|
| `src/gururmm-agent.exe` | Downloaded signed agent at build time (gitignored) |
|
||||||
|
| `gururmm-agent-*.msi` | Build output (gitignored) |
|
||||||
|
| `gururmm-agent-*.wixpdb` | WiX debug symbols (gitignored) |
|
||||||
|
| `install-test.log` | Install log from local smoke tests (gitignored) |
|
||||||
|
|
||||||
|
## UpgradeCode
|
||||||
|
|
||||||
|
The UpgradeCode `4c0aef59-9d08-4781-a3b4-a1c99b3b2e28` is the **permanent
|
||||||
|
identity** of the GuruRMM agent product family. Never change it. All future
|
||||||
|
versions must ship with this same UpgradeCode so MSI upgrades work
|
||||||
|
automatically via `msiexec /i newer.msi`.
|
||||||
75
projects/msp-tools/guru-rmm/installer/build-msi.ps1
Normal file
75
projects/msp-tools/guru-rmm/installer/build-msi.ps1
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Build + sign a GuruRMM Agent MSI installer.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Downloads the signed agent binary for the target version, packages it into
|
||||||
|
an MSI via WiX, signs the MSI with Azure Trusted Signing, and writes the
|
||||||
|
result to the current directory.
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
- .NET SDK 8
|
||||||
|
- wix global tool (dotnet tool install --global wix --version 5.0.2)
|
||||||
|
- Azure Trusted Signing access via sign.ps1 at C:\tools\trusted-signing\
|
||||||
|
- az login session (DefaultAzureCredential)
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\build-msi.ps1 -Version 0.6.1
|
||||||
|
.\build-msi.ps1 -Version 0.6.1 -SourceUrl https://rmm-api.azcomputerguru.com/downloads -SkipSign
|
||||||
|
#>
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory)] [string] $Version,
|
||||||
|
[string] $SourceUrl = 'https://rmm-api.azcomputerguru.com/downloads',
|
||||||
|
[string] $WixExe = "$env:USERPROFILE\.dotnet\tools\wix.exe",
|
||||||
|
[string] $SignScript = 'C:\tools\trusted-signing\sign.ps1',
|
||||||
|
[switch] $SkipSign,
|
||||||
|
[switch] $KeepSource
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
Set-Location $PSScriptRoot
|
||||||
|
|
||||||
|
if (-not (Test-Path $WixExe)) { throw "wix.exe not found at $WixExe" }
|
||||||
|
|
||||||
|
$srcDir = Join-Path $PSScriptRoot 'src'
|
||||||
|
if (-not (Test-Path $srcDir)) { New-Item -ItemType Directory -Path $srcDir | Out-Null }
|
||||||
|
|
||||||
|
$exePath = Join-Path $srcDir 'gururmm-agent.exe'
|
||||||
|
$downloadUrl = "$SourceUrl/gururmm-agent-windows-amd64-$Version.exe"
|
||||||
|
|
||||||
|
Write-Host "[1] Downloading signed agent $Version from $downloadUrl ..." -ForegroundColor Cyan
|
||||||
|
Invoke-WebRequest -Uri $downloadUrl -OutFile $exePath -UseBasicParsing
|
||||||
|
$sig = Get-AuthenticodeSignature $exePath
|
||||||
|
if ($sig.Status -ne 'Valid') {
|
||||||
|
throw "Downloaded agent has invalid or missing signature: $($sig.Status). Refusing to package an unsigned agent."
|
||||||
|
}
|
||||||
|
Write-Host " signed by: $($sig.SignerCertificate.Subject)" -ForegroundColor Gray
|
||||||
|
|
||||||
|
$msiName = "gururmm-agent-$Version.msi"
|
||||||
|
Write-Host "[2] Building $msiName via WiX ..." -ForegroundColor Cyan
|
||||||
|
& $WixExe build gururmm.wxs -arch x64 -o $msiName -d "Version=$Version"
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "wix build failed (exit $LASTEXITCODE)" }
|
||||||
|
|
||||||
|
if (-not $SkipSign) {
|
||||||
|
if (-not (Test-Path $SignScript)) { throw "sign.ps1 not found at $SignScript" }
|
||||||
|
Write-Host "[3] Signing $msiName ..." -ForegroundColor Cyan
|
||||||
|
& $SignScript -File (Join-Path $PSScriptRoot $msiName) `
|
||||||
|
-Description "GuruRMM Agent Installer v$Version" `
|
||||||
|
-Url 'https://www.azcomputerguru.com' `
|
||||||
|
-Verify
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "signing failed (exit $LASTEXITCODE)" }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $KeepSource) {
|
||||||
|
Remove-Item $exePath -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
$msiPath = Join-Path $PSScriptRoot $msiName
|
||||||
|
$hash = (Get-FileHash $msiPath -Algorithm SHA256).Hash.ToLower()
|
||||||
|
$hash | Set-Content "$msiPath.sha256"
|
||||||
|
"$hash $msiName" | Set-Content "$msiPath.sha256"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[DONE]" -ForegroundColor Green
|
||||||
|
Write-Host " msi: $msiPath"
|
||||||
|
Write-Host " sha256: $hash"
|
||||||
58
projects/msp-tools/guru-rmm/installer/gururmm.wxs
Normal file
58
projects/msp-tools/guru-rmm/installer/gururmm.wxs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
GuruRMM Agent Windows Installer
|
||||||
|
Builds an MSI that installs the signed agent binary, creates the runtime
|
||||||
|
data directory, registers the Windows service, and supports clean
|
||||||
|
upgrade + uninstall via Programs and Features.
|
||||||
|
|
||||||
|
Build: wix build gururmm.wxs -arch x64 -o gururmm-agent-VERSION.msi
|
||||||
|
Sign: (use sign.ps1 against the resulting .msi before shipping)
|
||||||
|
-->
|
||||||
|
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
|
||||||
|
<Package Name="GuruRMM Agent"
|
||||||
|
Manufacturer="Arizona Computer Guru LLC"
|
||||||
|
Version="0.6.1"
|
||||||
|
UpgradeCode="4c0aef59-9d08-4781-a3b4-a1c99b3b2e28"
|
||||||
|
Scope="perMachine"
|
||||||
|
InstallerVersion="500">
|
||||||
|
|
||||||
|
<SummaryInformation Description="GuruRMM Agent — Remote monitoring and management agent"
|
||||||
|
Manufacturer="Arizona Computer Guru LLC" />
|
||||||
|
|
||||||
|
<MajorUpgrade DowngradeErrorMessage="A newer version of GuruRMM Agent is already installed. Uninstall the newer version first if you need to downgrade." />
|
||||||
|
|
||||||
|
<MediaTemplate EmbedCab="yes" CompressionLevel="high" />
|
||||||
|
|
||||||
|
<!-- Install location: C:\Program Files\GuruRMM\ -->
|
||||||
|
<StandardDirectory Id="ProgramFiles64Folder">
|
||||||
|
<Directory Id="INSTALLFOLDER" Name="GuruRMM" />
|
||||||
|
</StandardDirectory>
|
||||||
|
|
||||||
|
<!-- Runtime data + logs: C:\ProgramData\GuruRMM\ -->
|
||||||
|
<StandardDirectory Id="CommonAppDataFolder">
|
||||||
|
<Directory Id="DATAFOLDER" Name="GuruRMM" />
|
||||||
|
</StandardDirectory>
|
||||||
|
|
||||||
|
<ComponentGroup Id="AgentComponents" Directory="INSTALLFOLDER">
|
||||||
|
<Component Id="AgentExe" Guid="9b3a6b4f-b6e6-4baf-9dfa-4c6a67cff11c">
|
||||||
|
<File Id="AgentExeFile"
|
||||||
|
Source="src\gururmm-agent.exe"
|
||||||
|
Name="gururmm-agent.exe"
|
||||||
|
KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
</ComponentGroup>
|
||||||
|
|
||||||
|
<!-- ProgramData GuruRMM folder (created empty; agent populates config + logs) -->
|
||||||
|
<ComponentGroup Id="DataDirComponents" Directory="DATAFOLDER">
|
||||||
|
<Component Id="DataDir" Guid="3f2b51c7-9e22-4c11-94d6-f1e6a9e4d8a0" KeyPath="yes">
|
||||||
|
<CreateFolder />
|
||||||
|
</Component>
|
||||||
|
</ComponentGroup>
|
||||||
|
|
||||||
|
<Feature Id="MainFeature" Title="GuruRMM Agent" Level="1" AllowAbsent="no">
|
||||||
|
<ComponentGroupRef Id="AgentComponents" />
|
||||||
|
<ComponentGroupRef Id="DataDirComponents" />
|
||||||
|
</Feature>
|
||||||
|
|
||||||
|
</Package>
|
||||||
|
</Wix>
|
||||||
Reference in New Issue
Block a user