bugfix bootstrapper and update architecture.md

This commit is contained in:
Oliver Kieselbach
2026-03-31 22:49:51 +02:00
parent e8903ff413
commit 5f1bd1c470
3 changed files with 433 additions and 143 deletions
+9 -17
View File
@@ -39,8 +39,8 @@
.CHANGELOG
2026-03-31 Replaced OS age + MDM pre-flight checks with multi-signal guard:
registry deployment marker, OOBE state, WMI/filesystem user profile
detection, and 12h bootstrap window. Fixes ghost sessions on hybrid
registry deployment marker, WMI/filesystem user profile detection,
and 12h bootstrap window. OOBEInProgress dropped (unreliable). Fixes ghost sessions on hybrid
join pre-provisioned devices where Intune re-targets the bootstrap
script after self-destruct.
2026-03-30 Fixed non-ASCII characters (em-dashes, Unicode symbols) that broke
@@ -111,10 +111,10 @@ try {
# -- Pre-flight: Multi-signal guard to prevent installation on non-provisioning devices --
# Layered approach: each guard catches a different scenario.
# Guard 1: Ghost re-installs (registry marker from previous deployment)
# Guard 2: Productive devices not in OOBE
# Guard 3: Productive devices with stale OOBEInProgress flag (real user profile exists)
# Guard 4: Ultra edge case (no key, OOBE stuck, no user, but device running too long)
# Guard 5: Agent binary already present from a previous run
# Guard 2: Productive devices (real user profile exists — WMI + filesystem)
# Guard 3: Bootstrap window expired (device uptime > 12h without agent)
# Guard 4: Agent binary already present from a previous run
# NOTE: OOBEInProgress is NOT used — it is unreliable (observed =0 during active enrollment).
# Guard 1: Agent was already deployed on this device (registry marker survives self-destruct)
$deployed = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\AutopilotMonitor' -Name 'Deployed' -ErrorAction SilentlyContinue).Deployed
@@ -123,15 +123,7 @@ try {
exit 0
}
# Guard 2: Device must be in OOBE (= actively provisioning)
$oobeInProgress = (Get-ItemProperty -Path 'HKLM:\SYSTEM\Setup' -Name 'OOBEInProgress' -ErrorAction SilentlyContinue).OOBEInProgress
Write-Log "OOBEInProgress=$oobeInProgress"
if ($oobeInProgress -ne 1) {
Write-Log "SKIP: Device is not in OOBE (OOBEInProgress=$oobeInProgress). Not a fresh enrollment."
exit 0
}
# Guard 3: No real user profile should exist yet (catches stale OOBEInProgress on productive devices)
# Guard 2: No real user profile should exist yet (primary productive-device guard)
# Combines WMI (Win32_UserProfile.Special flag) and filesystem for maximum reliability.
$excludePattern = '^(defaultuser\d*|Public|Default( User)?|All Users)$'
@@ -154,7 +146,7 @@ try {
exit 0
}
# Guard 4: Bootstrap window check (ultra edge case: no key, OOBE stuck, no user profiles)
# Guard 3: Bootstrap window check (no key, no user profiles, but device running too long)
# NOT "how long may enrollment take" -- agent handles that internally (6h emergency break).
# This is "how old can the OOBE state be before I no longer trust it for initial install".
# 12h bootstrap window vs 6h agent emergency break = consistent layered approach.
@@ -167,7 +159,7 @@ try {
exit 0
}
# Guard 5: Is the agent already installed? (leftover from previous run)
# Guard 4: Is the agent already installed? (leftover from previous run)
if (Test-Path $AgentBinPath) {
$existingAgent = Get-ChildItem -Path $AgentBinPath -Filter "AutopilotMonitor.Agent.exe" -ErrorAction SilentlyContinue
if ($existingAgent) {
+17 -27
View File
@@ -58,22 +58,13 @@ if ($deployed) {
Write-Step -Status 'PASS' -Message 'Guard 1: No previous deployment marker found.'
}
# Guard 2: Device must be in OOBE (= actively provisioning)
$oobeInProgress = Get-RegistryValueSafe -Path 'HKLM:\SYSTEM\Setup' -Name 'OOBEInProgress'
if ($oobeInProgress -ne 1) {
Write-Step -Status 'SKIP' -Message "Guard 2: Device is not in OOBE (OOBEInProgress=$oobeInProgress). Not a fresh enrollment."
$wouldInstall = $false
$reasons.Add("Not in OOBE (OOBEInProgress=$oobeInProgress)")
} else {
Write-Step -Status 'PASS' -Message 'Guard 2: Device is in OOBE (OOBEInProgress=1).'
}
# Guard 3: No real user profile should exist yet
# Guard 2: No real user profile should exist yet (primary productive-device guard)
# NOTE: OOBEInProgress is NOT used -- it is unreliable (observed =0 during active enrollment).
$excludePattern = '^(defaultuser\d*|Public|Default( User)?|All Users)$'
$wmiProfileQueryFailed = $false
$profileNames = @(
# WMI/CIM view
# WMI/CIM view -- Special flag reliably excludes SYSTEM/LocalService/NetworkService
try {
Get-CimInstance Win32_UserProfile -ErrorAction Stop |
Where-Object {
@@ -88,7 +79,7 @@ $profileNames = @(
$wmiProfileQueryFailed = $true
}
# Filesystem view
# Filesystem view -- catches profiles WMI might miss
(Get-ChildItem 'C:\Users' -Directory -ErrorAction SilentlyContinue).Name
) |
Where-Object {
@@ -97,23 +88,23 @@ Where-Object {
Select-Object -Unique
if ($wmiProfileQueryFailed) {
Write-Step -Status 'WARN' -Message 'Guard 3: WMI profile query failed, filesystem check still applied.'
Write-Step -Status 'WARN' -Message 'Guard 2: WMI profile query failed, filesystem check still applied.'
}
if ($profileNames) {
$names = ($profileNames | Select-Object -First 3) -join ', '
Write-Step -Status 'SKIP' -Message "Guard 3: Real user profile(s) found ($names). Device appears productive."
Write-Step -Status 'SKIP' -Message "Guard 2: Real user profile(s) found ($names). Device appears productive."
$wouldInstall = $false
$reasons.Add("Real user profile(s) found: $names")
} else {
Write-Step -Status 'PASS' -Message 'Guard 3: No real user profiles found (combined WMI/filesystem view).'
Write-Step -Status 'PASS' -Message 'Guard 2: No real user profiles found (combined WMI/filesystem view).'
}
# Guard 4: Bootstrap window check
# NOT "how long may enrollment take" — agent handles that internally (6h emergency break).
# Guard 3: Bootstrap window check
# NOT "how long may enrollment take" -- agent handles that internally (6h emergency break).
# This is "how old can the OOBE state be before I no longer trust it for initial install".
# 12h bootstrap window vs 6h agent emergency break = consistent layered approach.
# Sleep/standby does NOT reset uptime — only real boot/restart does.
# Sleep/standby does NOT reset uptime -- only real boot/restart does.
$lastBoot = $null
$uptimeHours = $null
@@ -122,24 +113,24 @@ try {
$uptimeHours = ((Get-Date) - $lastBoot).TotalHours
if ($uptimeHours -gt $MaxBootstrapWindowHours) {
Write-Step -Status 'SKIP' -Message "Guard 4: Device uptime is $([int]$uptimeHours)h. OOBE state is older than accepted bootstrap window of ${MaxBootstrapWindowHours}h."
Write-Step -Status 'SKIP' -Message "Guard 3: Device uptime is $([int]$uptimeHours)h. Older than accepted bootstrap window of ${MaxBootstrapWindowHours}h."
$wouldInstall = $false
$reasons.Add("Uptime exceeds bootstrap window: $([int]$uptimeHours)h > ${MaxBootstrapWindowHours}h")
} else {
Write-Step -Status 'PASS' -Message "Guard 4: Device uptime is $([int]$uptimeHours)h and within bootstrap window of ${MaxBootstrapWindowHours}h."
Write-Step -Status 'PASS' -Message "Guard 3: Device uptime is $([int]$uptimeHours)h and within bootstrap window of ${MaxBootstrapWindowHours}h."
}
}
catch {
Write-Step -Status 'WARN' -Message 'Guard 4: Could not determine LastBootUpTime / uptime.'
Write-Step -Status 'WARN' -Message 'Guard 3: Could not determine LastBootUpTime / uptime.'
}
# Guard 5 (existing): Agent binary already present
# Guard 4: Agent binary already present
if (Test-Path $AgentExePath) {
Write-Step -Status 'SKIP' -Message "Guard 5: Agent already installed at '$AgentExePath'."
Write-Step -Status 'SKIP' -Message "Guard 4: Agent already installed at '$AgentExePath'."
$wouldInstall = $false
$reasons.Add("Agent binary already present: $AgentExePath")
} else {
Write-Step -Status 'PASS' -Message "Guard 5: Agent binary not present at '$AgentExePath'."
Write-Step -Status 'PASS' -Message "Guard 4: Agent binary not present at '$AgentExePath'."
}
Write-Host ''
@@ -166,10 +157,9 @@ if ($reasons.Count -eq 0) {
Write-Host ''
Write-Host '=== Raw Values ==='
Write-Host "Deployed marker : $deployed"
Write-Host "OOBEInProgress : $oobeInProgress"
Write-Host "MaxBootstrapWindowHours : $MaxBootstrapWindowHours"
Write-Host "LastBootUpTime : $lastBoot"
Write-Host "UptimeHours : $([string]$uptimeHours)"
Write-Host "AgentExePath : $AgentExePath"
Write-Host "DetectedProfileNames : $($profileNames -join ', ')"
Write-Host ''
Write-Host ''