Skip to content

Deployment

Rod Christiansen edited this page Sep 5, 2026 · 2 revisions

Deployment

How BootstrapMate gets onto a Windows device and how it gets invoked. This page covers the MSI, the scheduled task the MSI creates, delivery as an Intune Win32 app, and re-running the tool on a device that is already provisioned.

The MSI

BootstrapMate ships as a per-architecture, per-machine MSI: BootstrapMate-x64-<version>.msi and BootstrapMate-arm64-<version>.msi. There is no combined package — deploy the one that matches the device architecture.

The MSI installs into C:\Program Files\BootstrapMate:

File What it is
managedbootstrapinstall.exe The CLI. This is what actually runs a manifest.
BootstrapMate.exe The WinUI 3 GUI, plus its Windows App SDK payload.

[INSTALLDIR] is appended to the system PATH, so managedbootstrapinstall is callable by name from any elevated shell after a session refresh. A Start Menu shortcut named BootstrapMate points at the GUI.

The MSI also writes these values under HKLM\SOFTWARE\BootstrapMate: InstallPath, Version, InstallDate, Architecture and LastRunVersion — all set to install-time values, not run-time values. Read the detection section before you build a rule on any of them.

A machine-level fallback setting is written to HKLM\SOFTWARE\BootstrapMate\SettingsManifestUrl, carrying the manifest URL baked into the build. Override it with policy or your own registry write unless that URL is genuinely yours. See Preferences for the full precedence chain.

MajorUpgrade is configured with AllowSameVersionUpgrades="no" and AllowDowngrades="no", so installing an MSI whose version equals the installed one does nothing, and an older MSI is refused with A newer version of [ProductName] is already installed.

What happens at InstallFinalize

Two custom actions run during a fresh install:

  1. RegisterSelfHealTask, sequenced After="InstallFiles", creates the scheduled task described below.
  2. RunBootstrapMate, sequenced Before="InstallFinalize" with Condition="NOT REMOVE", Execute="deferred", Impersonate="no" and Return="ignore", shells schtasks.exe /Run /TN "BootstrapMate Self-Heal".

The install-time run therefore happens through the scheduled task, not by invoking the exe from the MSI. The comment in installer/Product.wxs records why: a deferred custom action calling managedbootstrapinstall.exe directly deadlocks, because the child msiexec processes it spawns for MSI packages wait on the Windows Installer mutex the parent MSI still holds.

Two consequences follow. The bootstrap run inherits the task's SYSTEM context rather than the MSI's. And because the custom action starts the task rather than waiting on it, and its return code is ignored, the MSI can reach InstallFinalize and report success while packages are still installing.

On uninstall, UnregisterSelfHealTask runs Before="RemoveFiles" under Condition="REMOVE=\"ALL\"" and deletes the task.

The self-heal scheduled task

One task is created, named BootstrapMate Self-Heal. The MSI registers it with:

schtasks /Create /TN "BootstrapMate Self-Heal" /TR "\"[INSTALLDIR]managedbootstrapinstall.exe\" --force --silent --no-dialog" /SC DAILY /ST 03:00 /RU SYSTEM /RL HIGHEST /F

So: daily at 03:00 local time, as SYSTEM, at highest run level, with no console output and no progress dialog. It passes no --url, so the manifest URL is resolved through the normal precedence chain — policy, then machine settings, then the baked-in default.

--force is documented in --help as deprecated, but the task passes it on every daily run, and --force still triggers ClearAllCachesAggressive(). Every scheduled run therefore wipes the BootstrapMate cache directory and the Chocolatey cache directories as well.

Inspect the task with:

schtasks /Query /TN "BootstrapMate Self-Heal" /V /FO LIST

Delivering it as an Intune Win32 app

build.ps1 produces BootstrapMate-<arch>-<version>.intunewin alongside the MSI. Create one Win32 app per architecture.

Install command:

msiexec /i "BootstrapMate-x64-<version>.msi" /qn /norestart

Uninstall command — the ProductCode is not declared in the WiX source and WiX generates a new one for every build, so uninstall by referencing the MSI in the package rather than a hard-coded GUID:

msiexec /x "BootstrapMate-x64-<version>.msi" /qn /norestart

Install behaviour is System. The binaries are published self-contained, so there is no .NET runtime prerequisite. Add an architecture requirement rule to each app so the x64 package never lands on an ARM64 device or the reverse.

Detection

This is the part that most often goes wrong, because two different registry roots are in play.

  • HKLM\SOFTWARE\BootstrapMate is written by the MSI at install time. Its Version and LastRunVersion values tell you the package is installed. They say nothing about whether a bootstrap run ever succeeded.
  • HKLM\SOFTWARE\Cimian\BootstrapMate is written by a successful run. LastRunVersion under this key is set at the end of a run that neither threw nor had a package fail, so a device whose packages keep failing keeps failing this detection and keeps being retried. Per-phase status lives under HKLM\SOFTWARE\Cimian\BootstrapMate\Status\SetupAssistant and ...\Status\Userland.

For Win32 app detection — "is the MSI installed" — use the MSI-written key:

HKLM\SOFTWARE\BootstrapMate → Version, string, equals the MSI version

Note the MSI version is in YY.M.d.HHmm form, while the version a run records is YYYY.MM.DD.HHmm. They are not interchangeable strings.

To answer "did the bootstrap actually run", query the run-written key instead:

Get-ItemProperty 'HKLM:\SOFTWARE\Cimian\BootstrapMate' -Name LastRunVersion

Be aware of what the shipped example scripts in examples/detection-scripts/ really do. esp-detection.ps1, userland-detection.ps1 and generic-detection.ps1 all read HKLM:\SOFTWARE\BootstrapMateLastRunVersion and compare it to a hard-coded 2025.08.30.1300. That value is the one the MSI writes, so these scripts detect the MSI install, not a completed bootstrap — and because the expected version is hard-coded to a literal, they return exit 1 on any build other than that one. Their diagnostic output also reads HKLM:\SOFTWARE\BootstrapMate\Status\* and values named BootstrapStatus, LastError, InstallationStarted, CompletionTime and PackageArchitecture, none of which any code in this repo writes. Treat them as a record of an older layout, not as working detection. See Troubleshooting and Gotchas.

Redeploying and re-running

The daily task already re-runs the whole manifest, so on a healthy device convergence is automatic. To force a run now:

schtasks /Run /TN "BootstrapMate Self-Heal"

To run interactively from an elevated console, with output on screen:

managedbootstrapinstall.exe --url https://example.com/bootstrap/manifest.json --verbose

Points to know when re-running:

  • Only one instance runs at a time. A second invocation waits on the Global\BootstrapMate.SingleInstance mutex for up to 30 minutes, logging Another BootstrapMate instance is already running - waiting for it to finish, then exits 1.
  • The cache is cleared at the start of every run, so downloads are always fresh. Re-running does not reuse a previously downloaded package.
  • There is no state that marks an item as already done. Every run processes every item in the manifest; idempotence is the packages' responsibility.
  • --silent without elevation always fails. The CLI refuses to raise a UAC prompt in silent mode and returns 1. Run elevated, or via the SYSTEM task.

Reinstalling the MSI at the same version will not upgrade, and so will not re-fire the install-time run. Use schtasks /Run for that.

See also

Clone this wiki locally