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
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/user registry, 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/user 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": []
}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 GUI settings to the registry
--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 (BootstrapMate.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