A bootstrapping tool for Windows device provisioning that downloads and installs packages during Windows Out of Box Experience (OOBE) Enrollment Status Page (ESP) or after user login.
- Dual Phase Support: Setup Assistant (pre-login/ESP) and Userland (post-login)
- Package Types: MSI, EXE, PowerShell scripts, Chocolatey packages (.nupkg), sbin-installer packages (.pkg)
- Primary Package Manager: sbin-installer (lightweight, fast, no cache management) with Chocolatey fallback
- Registry Status Tracking: Provides completion status for Intune detection scripts
- Architecture Support: x64 and ARM64 with conditional installation
- Admin Escalation: Automatic privilege elevation for packages requiring admin rights
Before building, set up your environment variables:
-
Copy the example environment file:
Copy-Item .env.example .env -
Edit
.envwith your organization's settings:# Your code signing certificate Common Name ENTERPRISE_CERT_CN=Your Organization Code Signing Certificate # Your bootstrap manifest URL BOOTSTRAP_MANIFEST_URL=https://example.com/bootstrap/management.json # Optional: Specific certificate thumbprint # CERT_THUMBPRINT=1234567890ABCDEF1234567890ABCDEF12345678
-
Install your code signing certificate in the Current User certificate store
Settings come from policy (HKLM\SOFTWARE\Policies\BootstrapMate, written by Intune or Group
Policy) and then machine settings (HKLM\SOFTWARE\BootstrapMate\Settings). Both are read from
the 64-bit registry view only. A copy of the settings key in WOW6432Node is stale. The MSI
removes it, and the CLI removes it once and logs which values it removed. The policy key is
shared between the two views and is never removed.
AuthorizationHeader and ReportingHeader carry credentials, so they live in
HKLM\SOFTWARE\BootstrapMate\Secrets. Only SYSTEM and Administrators can read that key.
Deliver a header through policy as usual. The next elevated run moves it into that key and
blanks the copy in policy, which every user can read. The policy value is emptied, not deleted,
so the setting still shows as managed. The GUI shows a saved header masked and saves a new one
only after Unlock. BootstrapMate never writes a header to its logs, session.json or
last-run.json, and it redacts --headers from every logged command line.
When a run completes, BootstrapMate can POST a vendor-neutral JSON run summary to an optional endpoint, turning "did this PC provision cleanly?" into a fleet-dashboard query. The payload is plain JSON and not tied to any specific backend — any service accepting a JSON POST (a custom collector, ReportMate, MunkiReport, etc.) can consume it. Both the Windows and macOS clients emit the same schema.
Configure via Intune CSP / Group Policy (the bundled ADMX), the machine registry (HKLM\SOFTWARE\BootstrapMate\Settings), or both keys below:
| Key | Type | Effect |
|---|---|---|
ReportingUrl |
string | Endpoint to POST the run summary to. When unset, no report is sent. |
ReportingHeader |
string | Optional Authorization header value sent with the POST. |
The POST is best-effort: it is bounded by a short timeout and never fails the run (a slow or unreachable endpoint can delay completion by up to that timeout). Payload fields include tool, platform, version, runId, success, startTime/endTime, durationSeconds, architecture, hostname, serialNumber, manifestUrl, and per-phase outcomes.
Before any MSI or EXE installer is executed elevated, BootstrapMate verifies its Authenticode signature with WinVerifyTrust. A successful download only proves where the bytes came from — not who produced them. The signature gate ensures an installer carries a signature that chains to a trusted root (and, when configured, matches an expected publisher) before it runs as an elevated process.
Behaviour is controlled via Intune CSP / Group Policy (the bundled ADMX), the machine registry, or per-item manifest fields.
Policy / registry keys (HKLM\SOFTWARE\Policies\BootstrapMate for policy; HKLM\SOFTWARE\BootstrapMate\Settings for machine settings):
| Key | Type | Default | Effect |
|---|---|---|---|
VerifyPackageSignatures |
DWORD | 1 |
Verify every MSI/EXE installer before running it. |
ExpectedPublisher |
string | unset | Require installers to be signed by a certificate whose common name/subject contains this value. When unset, any Windows-trusted signature is accepted. |
AllowUnsigned |
DWORD | 0 |
Permit unsigned/untrusted installers (logged as a warning). A publisher mismatch is never bypassed, even with this set. |
Per-item manifest overrides (fall back to the global config): expectedPublisher, allowUnsigned.
Only msi and exe items are Authenticode-gated; nupkg/pkg/ps1 items continue to rely on their existing handling.
# Build signed executables + MSI + .intunewin (production)
.\build.ps1
# Development build (unsigned - for testing only)
.\build.ps1 -AllowUnsigned
# Build specific architecture
.\build.ps1 -Architecture x64
# Build without MSI/IntuneWin packages
.\build.ps1 -SkipMSI
# Run with a manifest URL
.\publish\executables\x64\managedbootstrapinstall.exe --url "https://example.com/bootstrap/management.json"
# Check status (useful for troubleshooting)
.\publish\executables\x64\managedbootstrapinstall.exe --status
# Clear status (for testing)
.\publish\executables\x64\managedbootstrapinstall.exe --clear-statusThe CLI's exit code is the only thing an Intune app, a scheduled task or a wrapper script sees, so each outcome has its own value:
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Usage error (unrecognised argument, missing value), or the manifest could not be fetched or processed |
| 2 | The run completed but one or more packages failed |
| 3 | Administrator privileges are required and were not obtained (nothing was attempted) |
Code 3 is deliberately distinct from 1: an unelevated --silent run installed
nothing and is a configuration mistake, not a failed installation.
An optional top-level preflight array in the manifest runs before every other
stage. Its items use the same schema as setupassistant (name, file, url,
arguments, type) and must be PowerShell scripts (ps1). They run in manifest
order, ahead of the type-based reordering the later stages use. The preflight
script's exit code decides the rest of the run:
| Exit code | Mode | What runs |
|---|---|---|
0 |
Skip | Nothing. The run ends successfully. |
2 |
Baseline | setupassistant items, with no dialog, no userland stage and no reboot. |
| any other positive | Provision | The full bootstrap: setupassistant, then userland. |
| negative, or the script cannot be downloaded or started | Failed | Nothing further. The run exits 1. |
With several preflight scripts, the first to return Skip, Baseline or Failed
decides; Provision moves on to the next one. A manifest without preflight runs
the full bootstrap, as before.
These are the preflight script's exit codes. They are separate from the CLI's own
exit codes above: a baseline run that installs cleanly exits 0.
Baseline mode is for a machine that is already provisioned and in use. The daily
Self-Heal task is how it reaches those machines: it brings the tooling in the
manifest back to the published versions without provisioning the machine again.
An MSI is skipped when its product (by ProductCode, or by UpgradeCode after a major
upgrade) is installed at the package's ProductVersion or newer. BootstrapMate also
keeps a ledger of the package files it has installed, by SHA-256 hash, in
C:\ProgramData\ManagedBootstrap\installed.json, and a baseline run skips any file
already in it. That covers scripts and EXEs, which register no product. A baseline
run on a current machine installs nothing. An item leaves itself out of baseline
runs with "baseline": false.
Every process BootstrapMate starts runs with BOOTSTRAPMATE_BASELINE_EXIT_CODE=2
in its environment. A preflight that may be run by an older build checks for it
before asking for baseline, because a build without baseline mode treats exit 2
as Provision.
The dialog opens, and system sounds are muted, only after the preflight has chosen Provision, so Skip and Baseline runs show nothing to the person at the machine. Unlike macOS there is no one-shot launcher to remove on Skip: the Windows launcher is the daily Self-Heal scheduled task, and it stays.
{
"preflight": [
{
"name": "Preflight",
"file": "preflight.ps1",
"type": "ps1",
"url": "https://example.com/bootstrap/preflight.ps1",
"arguments": []
}
],
"setupassistant": [],
"userland": []
}Each session's session.json starts with run_type set to provisioning. Once
the preflight has decided, it is rewritten with skip, baseline or
provisioning. A failed preflight stays provisioning.
C:\ProgramData\ManagedBootstrap\last-run.json holds the outcome of the most recent
run. It is written atomically when the run starts, with status running, and
again when the session closes. At the start, end_time and duration_seconds are
null and items is empty. Its status values match session.json: running,
completed, partial_failure (some items failed), failed (the preflight
failed or the manifest would not load), and interrupted: a run that died without
recording its end (a reboot, a killed process). The next run finds the record still
at running, relabels that run's session.json, and logs a warning before it
writes its own record. The time in the --last-run line is UTC
to the minute.
{
"session_id": "2026-10-04-030001",
"run_type": "baseline",
"status": "partial_failure",
"tool_version": "2026.10.04.1200",
"start_time": "2026-10-04T03:00:01.120-07:00",
"end_time": "2026-10-04T03:04:12.480-07:00",
"duration_seconds": 251,
"errors": 1,
"warnings": 0,
"items": [
{ "name": "Example Agent", "stage": "setupassistant", "result": "skipped" },
{ "name": "Example Tools", "stage": "setupassistant", "result": "failed", "error": "Download stalled: no data for 60 seconds" }
]
}result is installed, skipped or failed. error is set only on failures and
is cut to its first line, 200 characters at most.
managedbootstrapinstall.exe --last-run prints that record as a single line of at
most 1000 characters, made for an Intune remediation script's output column:
2026-10-04T10:04Z baseline partial_failure v2026.10.04.1200 installed=0 skipped=1 failed=1: Example Tools: Download stalled: no data for 60 seconds
When no run has been recorded it prints no run recorded. It always exits 0, needs
no elevation and creates no session.
--status works the same way. It only reads HKLM\SOFTWARE\BootstrapMate and
status.json, so it runs for any user, opens no session and never prompts. An
install run started without elevation and without an interactive console (a script,
a remote shell, redirected input) exits 3 instead of waiting at the elevation
prompt.
Every baseline run downloads from the package host, so baselines are rate-limited.
The check runs only after the preflight has chosen baseline, so provisioning is never
limited. It reads C:\ProgramData\ManagedBootstrap\baseline.json, which holds the
last baseline's end_time, status, consecutive_failures, the version that last
completed one (tool_version) and the version that last tried (attempt_version). last-run.json
cannot hold the clock, because every run rewrites it.
| Last baseline | Next baseline |
|---|---|
| Completed by another BootstrapMate version, and this version has not tried one | Runs |
| Interrupted (the run never recorded its end) | Runs at the next trigger |
Completed less than BaselineMinIntervalHours ago (default 144) |
Skips |
| Failed or partially failed for the first time | One retry after 24 hours, never sooner |
| Failed again after that retry | Waits the full interval |
| None: a provisioning run clears the record | Runs |
Any, with C:\ProgramData\ManagedBootstrap\.bootstrap_force present |
Runs; the preflight consumes the file |
A throttled run fetches the manifest and runs the preflight, then downloads nothing.
It logs why, ends as skip, records SetupAssistant and Userland as Skipped in
status.json, and leaves baseline.json alone. --force does not bypass the
throttle, and BootstrapMate never relaunches itself: a retry comes only from the next
normal trigger (the Intune install or the daily Self-Heal task). BaselineMinIntervalHours comes from policy or the settings registry
(0-8760; 0 disables the limit for completed runs).
A baseline run also skips an item without downloading it when the manifest says
enough: its hash is already in the install ledger, or an msi item's productCode
or upgradeCode is installed at its version (the MSI ProductVersion) or newer.
Without those fields, the file is downloaded and checked afterwards.
Everything carries the build stamp YYYY.MM.DD.HHMM: file and assembly versions,
--version, HKLM\SOFTWARE\BootstrapMate\Version (the Intune detection value),
logs, last-run.json and release tags.
The MSI ProductVersion is the only exception. Windows Installer fields are numeric
(255.255.65535), and upgrades compare only the first three fields, so the MSI uses
YY.M.DDHH.MM: 2026.10.04.1951 is 26.10.419.51. Upgrades compare to the hour.
Two builds made in the same hour compare equal and do not upgrade each other, so
publish at most one build per hour. Pass -p:FullVersion=YYYY.MM.DD.HHMM to the
installer project; it derives the ProductVersion.
Package downloads stream to disk with no overall HTTP timeout. An attempt fails when
no data arrives for NetworkTimeout seconds (default 120, range 10-600), or when it
runs longer than 30 minutes. A stalled transfer, a network error or a 5xx response
is retried up to three attempts in all, with 10- and 20-second waits between them. A
4xx response is not retried. The manifest request uses the same NetworkTimeout.
When a manifest item carries hash, a SHA-256 hex digest (optionally prefixed with
sha256:), the downloaded file must match it before it runs. A mismatch, or a hash
that is not a SHA-256 digest, fails the item. This applies to every item type,
preflight scripts included. Items without hash are not pinned; the log records
the file's SHA-256 so it can be copied into the manifest.
Chocolatey keeps its own checksum verification on. A nupkg item that genuinely
needs it off sets "ignoreChecksums": true, and the run logs a warning when it does.
A Cimian-built MSI goes to sbin-installer, which runs the package's embedded scripts,
with two exceptions that go to msiexec /i … /qn /norestart: an item that has
arguments (those are msiexec arguments, and sbin-installer's command line takes
none), and the sbin-installer package itself, under whatever name it is published.
Third-party MSIs always use msiexec.
FollowRedirects and Reboot were read from policy, the registry and the GUI, but
nothing ever acted on them. They are removed from the configuration, the ADMX and
the GUI. Redirects are always followed, and BootstrapMate never restarts the machine.
A value still set for either is logged as a warning and otherwise ignored.
DryRun is honoured by refusing to run, exactly like --dry-run: BootstrapMate has
no simulated install, so a policy-set DryRun exits 1 without installing anything.
EnableDialog set to false turns the dialog off, as NoDialog does; the ADMX has a
policy for each, and either one locks the Prefs tab's "Show progress dialog" switch. DialogIcon
sets the dialog's icon. SilentMode and VerboseMode from policy or settings turn
those modes on, and a CLI switch cannot turn them off.
When csharpDialog is managed with an authorisation key, it opens only for a caller that
passes the matching plain key in the DIALOG_AUTH_KEY environment variable, and otherwise
exits 30. BootstrapMate passes its own DIALOG_AUTH_KEY through when it has one, and
otherwise reads the key from the file named by DialogAuthKeyPath (policy or machine
settings; default C:\ProgramData\ManagedNotifications\authkey), which should be readable
only by SYSTEM and Administrators. The key is never put on the command line or logged. With
no key, or a wrong one, the dialog does not open, a warning is logged, and the run carries
on without it.
BootstrapMate tracks completion status in both 64-bit and 32-bit registry views:
HKLM\SOFTWARE\BootstrapMate\LastRunVersion # Written only after successful completion
HKLM\SOFTWARE\BootstrapMate\Status\Preflight
HKLM\SOFTWARE\BootstrapMate\Status\SetupAssistant
HKLM\SOFTWARE\BootstrapMate\Status\Userland
HKLM\SOFTWARE\WOW6432Node\BootstrapMate\Status\Preflight
HKLM\SOFTWARE\WOW6432Node\BootstrapMate\Status\SetupAssistant
HKLM\SOFTWARE\WOW6432Node\BootstrapMate\Status\Userland
Status Values: Starting, Running, Completed, Failed, Skipped
The same records are written to C:\ProgramData\ManagedBootstrap\status.json,
keyed by phase name, where Stage and Phase are the enum numbers: Stage
0 Starting, 1 Running, 2 Completed, 3 Failed, 4 Skipped; Phase 0
SetupAssistant, 1 Userland, 2 Preflight.
Every run writes a record for every phase. A phase the run did not execute is
Skipped with ExitCode 0 and a fresh CompletionTime: SetupAssistant and
Userland on a Skip run, Userland on a Baseline run, Preflight when the manifest has
none. The Preflight record's ExitCode is the deciding script's exit code (0, 2 or
the Provision code), so it shows which mode the run took.
Completion Registry Value (written only after successful run):
LastRunVersion: BootstrapMate version that successfully completed (e.g., "2025.08.30.1300")
For Intune Detection: Use HKLM\SOFTWARE\BootstrapMate\LastRunVersion as your detection key.
The most reliable way to deploy BootstrapMate is using the signed MSI installer:
# Build signed executables, MSI and .intunewin packages with an auto-detected certificate
.\build.ps1
# Deploy via Intune Win32 app using generated files:
# - BootstrapMate-x64-VERSION.msi (signed, for x64 systems)
# - BootstrapMate-arm64-VERSION.msi (signed, for ARM64 systems)
# - install-bootstrapmate.ps1 (installation script)
# - detect-bootstrapmate.ps1 (detection script)
# - BootstrapMate-x64-VERSION.intunewin (for direct upload)
# - BootstrapMate-arm64-VERSION.intunewin (for direct upload)Benefits of MSI deployment:
- ✅ Proper Windows Installer integration
- ✅ Code signed with enterprise certificate
- ✅ Automatic architecture detection
- ✅ Clean uninstall capability
- ✅ Shows in Add/Remove Programs
- ✅ Reliable upgrade path
- ✅ .intunewin packages for direct Intune upload
For simple deployments, you can package the executable with a PowerShell script:
Use this PowerShell detection script in your Intune Win32 app configuration:
# Intune Detection Script for BootstrapMate
$regPath = "HKLM:\SOFTWARE\BootstrapMate"
$expectedVersion = "2025.08.30.1300" # Update this when you deploy new versions
try {
$lastRunVersion = Get-ItemProperty -Path $regPath -Name "LastRunVersion" -ErrorAction Stop
if ($lastRunVersion.LastRunVersion -eq $expectedVersion) {
Write-Output "BootstrapMate $expectedVersion completed successfully"
exit 0 # Found - app is installed
} else {
Write-Output "Found version $($lastRunVersion.LastRunVersion), expected $expectedVersion"
exit 1 # Wrong version - trigger reinstall
}
} catch {
Write-Output "BootstrapMate not found or never completed successfully"
exit 1 # Not found - trigger install
}- Name: BootstrapMate OOBE Bootstrap
- Description: Automated software provisioning during Windows OOBE
- Publisher: Your Organization
- Category: Computer Management
- Install command:
powershell.exe -ExecutionPolicy Bypass -File install.ps1 - Uninstall command:
powershell.exe -ExecutionPolicy Bypass -Command "Remove-Item -Path 'HKLM:\SOFTWARE\BootstrapMate' -Recurse -Force -ErrorAction SilentlyContinue; Remove-Item -Path '$env:ProgramFiles\BootstrapMate' -Recurse -Force -ErrorAction SilentlyContinue" - Install behavior: System
- Device restart behavior: No specific action
- Operating system architecture: 64-bit (or configure separate packages for x64/ARM64)
- Minimum operating system: Windows 10 1903
- Disk space required: 100 MB
- Physical memory required: 512 MB
- Rules format: Use custom detection script
- Script file: Upload the detection script from above
- None (BootstrapMate is self-contained)
Create your Win32 app package with these files:
BootstrapMate-Package/
├── managedbootstrapinstall.exe # BootstrapMate executable (x64 or ARM64)
├── install.ps1 # Installation script
└── detection.ps1 # Detection script (see examples/detection-scripts/)
- Create Win32 App: Package BootstrapMate as described above
- Assign to Device Groups: Target your Autopilot device groups
- Set as Required: Deploy as required during ESP
- Configure Dependencies: Ensure this runs before other software
- Target: Device groups (Autopilot devices)
- Assignment type: Required
- Delivery optimization: Download content in background using HTTP only
In your Autopilot profile ESP settings:
- Show app installation progress: Yes
- Block device use until required apps install: Yes
- Include BootstrapMate in required apps list
BootstrapMate creates additional registry keys for troubleshooting:
HKLM\SOFTWARE\BootstrapMate\
├── LastRunVersion # Only exists after successful completion
├── BootstrapStatus # InstallationStarted, Success, Failed, Error, ArchitectureMismatch
├── InstallationStarted # Timestamp when installation began
├── CompletionTime # Timestamp when bootstrap completed
├── LastError # Error message if failed
├── ErrorTime # Timestamp of last error
├── InstallPath # Where BootstrapMate was installed
├── PackageArchitecture # Architecture of deployed package (x64/ARM64)
├── SystemArchitecture # Detected system architecture code
└── ProcessorName # Processor name for diagnostics
BootstrapMate creates detailed logs:
- Location:
C:\ProgramData\ManagedBootstrap\logs\ - File name: one file per run,
YYYY-MM-DD-HHmmss.log - Line format:
[yyyy-MM-dd HH:mm:ss] LEVEL messagein local time, whereLEVELisDEBUG,INFO,WARNorERRORpadded to five characters - Retention: files older than 30 days are deleted at the start of each run
- Architecture Mismatch: Deploy separate packages for x64 and ARM64
- Certificate Issues: Ensure your code signing certificate is deployed via Intune
- Network Connectivity: Manifest URL must be accessible during ESP
- Permission Issues: BootstrapMate automatically elevates to administrator
- sbin-installer Not Found: Deploy sbin-installer first if using .nupkg/.pkg packages for optimal performance
Check Installation:
# Verify sbin-installer is available
if (Test-Path "C:\Program Files\sbin\installer.exe") {
Write-Host "sbin-installer is installed"
& "C:\Program Files\sbin\installer.exe" --vers
} else {
Write-Host "sbin-installer not found - will use Chocolatey fallback"
}Common sbin-installer Issues:
- Package Format: Ensure .nupkg/.pkg files are valid ZIP archives
- Permissions: Verify BootstrapMate runs as administrator
- Target Path: Check target path permissions for installation
Use this PowerShell command to check BootstrapMate status on a device:
# Check BootstrapMate status
$regPath = "HKLM:\SOFTWARE\BootstrapMate"
if (Test-Path $regPath) {
Get-ItemProperty -Path $regPath | Format-List
} else {
Write-Host "BootstrapMate registry not found - never installed or completed"
}
# Check detailed status
& "$env:ProgramFiles\BootstrapMate\managedbootstrapinstall.exe" --status- Build new version — the version is the build timestamp, generated automatically
- Update detection script with new version number
- Create new Win32 app or update existing with supersedence
- Deploy to test group first
- Monitor deployment using Intune reporting
- Roll out to production groups
BootstrapMate uses format: YYYY.MM.DD.HHMM
- Example:
2025.08.30.1300(August 30, 2025, 1:00 PM)
BootstrapMate for Windows enables IT administrators to:
- Bootstrap software deployment during Windows Setup Assistant (OOBE)
- Orchestrate package installation from any web-accessible repository
- Support multiple package formats (MSI, EXE, PowerShell, Chocolatey, sbin-installer, MSIX)
- Work with any MDM solution (Intune, JAMF Pro, Workspace ONE, etc.)
- Provide real-time feedback to users and administrators
- Handle dependencies and ordering automatically
- Leverage sbin-installer for fast, lightweight package management
- MDM Trigger: MDM system deploys BootstrapMate via Win32 app or script
- First Run: the MSI runs
managedbootstrapinstall.exeatInstallFinalize - Configuration Download: Downloads package manifest from configured repository
- OOBE Package Installation: Installs system-level packages during device setup
- User Session Packages: Installs the userland packages once a user session exists
- Exit: the process exits, having written its status to the registry
- Self-Heal: a daily scheduled task (
BootstrapMate Self-Heal, 03:00 as SYSTEM) re-runs the CLI so a device that missed or failed a phase converges
BootstrapMate is a one-shot process, not a resident service. Nothing supervises it between runs: a failed run is retried by the scheduled task, or by the MDM re-running the app, not by a service restart.
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ MDM System │───►│ managedbootstrap │───►│ Package Repo │
│ (Intune, etc.) │ │ install.exe (CLI)│ │ (HTTPS/Azure) │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│
▼
┌──────────────────┐
│ Package Manifest │
│ (JSON/YAML) │
└──────────────────┘
│
▼
┌──────────────────┐
│ Software Packages│
│ MSI/EXE/PS1/MSIX │
└──────────────────┘
# Deploy as Win32 app or PowerShell script
$installCommand = "managedbootstrapinstall.exe --url https://example.com/bootstrap/bootstrapmate.json --silent"{
"setupassistant": [
{
"name": "Microsoft Teams",
"file": "teams.msi",
"type": "msi",
"url": "https://example.com/packages/teams.msi",
"arguments": ["/quiet", "ALLUSERS=1"]
},
{
"name": "System Utility",
"file": "system-utility-1.0.0.nupkg",
"type": "nupkg",
"url": "https://example.com/packages/system-utility-1.0.0.nupkg",
"arguments": ["--verbose"]
}
],
"userland": [
{
"name": "Adobe Reader",
"file": "reader.exe",
"type": "exe",
"url": "https://example.com/packages/reader.exe",
"arguments": ["/S"]
},
{
"name": "User App",
"file": "userapp-2.0.0.pkg",
"type": "pkg",
"url": "https://example.com/packages/userapp-2.0.0.pkg",
"target": "CurrentUserHomeDirectory",
"arguments": ["--verbose"]
}
]
}Note: For .nupkg and .pkg packages, target defaults to "/" (system root) when omitted.
- MSI: Windows Installer packages
- EXE: Executable installers
- PowerShell:
.ps1scripts with elevation - nupkg: NuGet packages via sbin-installer (primary) or Chocolatey (fallback)
- pkg: sbin-installer native packages (lightweight, fast, no cache)
- MSIX: Modern Windows packages
- Registry: Registry modifications
- File Copy: Direct file deployment
For .nupkg packages:
- sbin-installer: Primary choice (if available at
C:\Program Files\sbin\installer.exe) - Chocolatey: Fallback option (automatically installs if needed)
For .pkg packages:
- sbin-installer: Native format (requires sbin-installer to be installed)
BootstrapMate includes out of the box support for sbin-installer, a lightweight alternative to choco.
Advantages over Chocolatey:
- 2-4x faster package installations
- No cache management - direct package execution
- 90% less disk usage - no persistent cache
- Simple command structure -
installer --pkg <path> --target <target> - Deterministic behavior - predictable, reliable operation
Deploy sbin-installer before using .nupkg/.pkg packages:
# Option 1: MSI Installation (Recommended)
Invoke-WebRequest -Uri "https://github.com/windowsadmins/sbin-installer/releases/latest/download/sbin-installer.msi" -OutFile "sbin-installer.msi"
Start-Process msiexec -ArgumentList "/i sbin-installer.msi /quiet" -Wait
# Option 2: Include in BootstrapMate manifest as first package
{
"setupassistant": [
{
"name": "sbin-installer",
"file": "sbin-installer.msi",
"type": "msi",
"url": "https://example.com/packages/sbin-installer.msi",
"arguments": ["/quiet"]
}
]
}{
"setupassistant": [
{
"name": "System Tool",
"file": "systemtool-1.0.0.nupkg",
"type": "nupkg",
"url": "https://example.com/packages/systemtool-1.0.0.nupkg",
"arguments": ["--verbose"]
}
]
}Target Options (optional):
- Omitted →
"/"(system root) - Default "CurrentUserHomeDirectory"→ User's home folder"C:\\Custom\\Path"→ Custom installation path
- One-shot CLI, run by the MSI at install time and by a daily self-heal scheduled task
- OOBE/Autopilot integration
- Multiple package format support
- Dependency resolution
- Progress reporting
- Error handling and retry logic
- Cleanup and self-removal
- GUI progress window
- Advanced logging and telemetry
- Payload hash verification (Authenticode signature verification is already implemented)
- Rollback capabilities
- Configuration profiles
- Integration with popular MDM systems
- Windows 10/11 (1809 or later)
- No runtime prerequisite — the executable is published self-contained
- Administrative privileges
managedbootstrapinstall.exe [OPTIONS]
Options:
--url <url> URL of the bootstrapmate.json / .yaml manifest
--force Deprecated; downloads are always fresh
--verbose, -v Enable detailed logging
--silent Run with no console output
--no-dialog Disable the progress dialog
--blur-screen Show the progress dialog full screen
--dialog-title <text> Custom progress dialog title
--dialog-message <text> Custom progress dialog message
--pipe <name> Named pipe for GUI output streaming
--save-settings Save settings to the HKLM machine settings key (administrator)
--status Show current installation status
--clear-status Clear all installation status data
--clear-cache Clear caches, including failed installation files
--reset-chocolatey Complete Chocolatey reset
--version, -V Print the version and exit
--help, -h Show help informationrepository/
├── manifest.json # Package definitions
├── packages/ # Package files
│ ├── teams.msi
│ ├── reader.exe
│ └── scripts/
│ └── setup.ps1
└── config/ # Configuration files
└── settings.json
Items live under the two phase keys, setupassistant and userland. Each item must
carry name, url, file and type; the rest are optional. See
examples/bootstrapmate.json for a runnable copy.
{
"setupassistant": [
{
"name": "Sample Application",
"file": "SampleApp.msi",
"url": "https://example.com/bootstrap/packages/SampleApp.msi",
"type": "msi",
"arguments": ["/quiet", "/norestart"],
"condition": "architecture_x64",
"expectedPublisher": "Example Publisher",
"allowUnsigned": false
}
],
"userland": [
{
"name": "Modern App",
"file": "ModernApp-2.0.0.pkg",
"url": "https://example.com/bootstrap/packages/ModernApp-2.0.0.pkg",
"type": "pkg",
"target": "CurrentUserHomeDirectory"
}
]
}type is one of msi, exe, ps1, nupkg or pkg. condition accepts
architecture_x64 or architecture_arm64. There is no hash key: payload hash
verification is not implemented (tracked in issue #33). YAML manifests are accepted and
converted to the same shape.
# Clone repository
git clone https://github.com/bootstrapmate/bootstrapmate-windows.git
cd bootstrapmate-windows
# Signed build (signing is the default; a certificate is auto-detected)
.\build.ps1
# Build specific architecture
.\build.ps1 -Architecture x64
# Build and test
.\build.ps1 -Test-Architecture: Target architecture (x64, arm64, both)-Thumbprint: Specific certificate thumbprint to use-Clean: Clean build directories before building-Test: Run basic functionality tests after building-AllowUnsigned: Development build without signing (not for production)-SkipMSI: Build executables only, skipping MSI and .intunewin-ListCerts/-FindCertSubject: Inspect available code signing certificates
- Code Signing: Always sign BootstrapMate executable with your enterprise certificate
- HTTPS: Use HTTPS for all manifest and package URLs
- Certificate Deployment: Deploy your code signing certificate via Intune before BootstrapMate
- Manifest Security: Protect your bootstrap manifest URL from unauthorized access
- Package Integrity: Consider implementing hash verification for downloaded packages
- Test Architecture Combinations: Test on both x64 and ARM64 devices
- Monitor Deployments: Use Intune device compliance and app installation reports
- Staged Rollout: Deploy to pilot groups before full production
- Backup Strategy: Maintain previous working versions for rollback
- Documentation: Document your manifest structure and package dependencies
- Regular Updates: Keep BootstrapMate updated for security and functionality improvements
Three projects. There is no Windows Service, and no test project yet:
BootstrapMate.csproj # CLI (managedbootstrapinstall.exe), sources at the repo root
src/BootstrapMate.Core/ # Shared library (constants, signature verification, reporting)
src/BootstrapMate.App/ # WinUI 3 GUI (Managed Bootstrap Install.exe), launches the CLI elevated
installer/ # WiX MSI: runs the CLI at InstallFinalize, registers the self-heal task
examples/ # Example manifest and Intune detection scripts
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Original InstallApplications macOS project
- sbin-installer for lightweight package management
- BootstrapMate for Mac, the macOS counterpart
- Windows Admin community for feedback and testing