From 55b077659de6dddd36b81944b682ec7d465cf228 Mon Sep 17 00:00:00 2001 From: Oliver Kieselbach Date: Tue, 31 Mar 2026 23:30:43 +0200 Subject: [PATCH] bug fix bootstrap --- .github/workflows/build-agent.yml | 8 ++++ .../Bootstrap/Install-AutopilotMonitor.ps1 | 29 ++++++++++---- .../Bootstrap/Test-ShouldBootstrapAgent.ps1 | 38 +++++++++++++++---- .../AutopilotMonitor.Agent/buildcounter.txt | 2 +- .../app/docs/sections/SectionAgentSetup.tsx | 14 ++++++- 5 files changed, 74 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build-agent.yml b/.github/workflows/build-agent.yml index 76d6337d..fe2cd3f7 100644 --- a/.github/workflows/build-agent.yml +++ b/.github/workflows/build-agent.yml @@ -75,6 +75,7 @@ jobs: run: | dotnet restore src/Agent/AutopilotMonitor.Agent/AutopilotMonitor.Agent.csproj dotnet restore src/Agent/AutopilotMonitor.SummaryDialog/AutopilotMonitor.SummaryDialog.csproj + dotnet restore src/Agent/AutopilotMonitor.Agent.Core.Tests/AutopilotMonitor.Agent.Core.Tests.csproj - name: Build Agent run: > @@ -90,6 +91,13 @@ jobs: --configuration Release --no-restore + - name: Run Agent unit tests + run: > + dotnet test src/Agent/AutopilotMonitor.Agent.Core.Tests/AutopilotMonitor.Agent.Core.Tests.csproj + --configuration Release + --no-restore + --verbosity quiet + - name: Update buildcounter.txt if: inputs.build_number == '' shell: pwsh diff --git a/scripts/Bootstrap/Install-AutopilotMonitor.ps1 b/scripts/Bootstrap/Install-AutopilotMonitor.ps1 index 26c953a7..e2119dd1 100644 --- a/scripts/Bootstrap/Install-AutopilotMonitor.ps1 +++ b/scripts/Bootstrap/Install-AutopilotMonitor.ps1 @@ -111,10 +111,12 @@ 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 (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 2: Productive devices (real user profile exists -- WMI + filesystem) + # Guard 3: Productive devices (a real user has logged on before) + # Guard 4: Productive devices (explorer.exe running = user desktop active) + # Guard 5: Bootstrap window expired (device uptime > 12h without agent) + # Guard 6: 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 @@ -146,7 +148,20 @@ try { exit 0 } - # Guard 3: Bootstrap window check (no key, no user profiles, but device running too long) + # Guard 3: LastLoggedOnUser -- during Device ESP no real user has logged on yet + $lastLoggedOnUser = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI' -Name 'LastLoggedOnUser' -EA SilentlyContinue).LastLoggedOnUser + if ($lastLoggedOnUser -and $lastLoggedOnUser -notmatch 'defaultuser\d*') { + Write-Log "SKIP: LastLoggedOnUser found ($lastLoggedOnUser). Device appears productive." + exit 0 + } + + # Guard 4: explorer.exe running -- during Device ESP no desktop shell is active + if (Get-Process explorer -ErrorAction SilentlyContinue) { + Write-Log "SKIP: explorer.exe is running. Device appears beyond initial enrollment." + exit 0 + } + + # Guard 5: 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. @@ -159,7 +174,7 @@ try { exit 0 } - # Guard 4: Is the agent already installed? (leftover from previous run) + # Guard 6: 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) { @@ -168,7 +183,7 @@ try { } } - Write-Log "Pre-flight checks passed -- device is in OOBE, no prior deployment, no real user profiles" + Write-Log "Pre-flight checks passed -- no prior deployment, no real user profiles, no logged-on user, no explorer" # Download and extract agent binaries $agentExePath = Join-Path $AgentBinPath "AutopilotMonitor.Agent.exe" diff --git a/scripts/Bootstrap/Test-ShouldBootstrapAgent.ps1 b/scripts/Bootstrap/Test-ShouldBootstrapAgent.ps1 index 48a3f546..39cedcc4 100644 --- a/scripts/Bootstrap/Test-ShouldBootstrapAgent.ps1 +++ b/scripts/Bootstrap/Test-ShouldBootstrapAgent.ps1 @@ -100,7 +100,27 @@ if ($profileNames) { Write-Step -Status 'PASS' -Message 'Guard 2: No real user profiles found (combined WMI/filesystem view).' } -# Guard 3: Bootstrap window check +# Guard 3: LastLoggedOnUser -- during Device ESP no real user has logged on yet +$lastLoggedOnUser = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI' -Name 'LastLoggedOnUser' -EA SilentlyContinue).LastLoggedOnUser +if ($lastLoggedOnUser -and $lastLoggedOnUser -notmatch 'defaultuser\d*') { + Write-Step -Status 'SKIP' -Message "Guard 3: LastLoggedOnUser found ($lastLoggedOnUser). Device appears productive." + $wouldInstall = $false + $reasons.Add("LastLoggedOnUser found: $lastLoggedOnUser") +} else { + Write-Step -Status 'PASS' -Message "Guard 3: No real LastLoggedOnUser (value: '$lastLoggedOnUser')." +} + +# Guard 4: explorer.exe running -- during Device ESP no desktop shell is active +$explorerRunning = $null -ne (Get-Process explorer -ErrorAction SilentlyContinue) +if ($explorerRunning) { + Write-Step -Status 'SKIP' -Message 'Guard 4: explorer.exe is running. Device appears beyond initial enrollment.' + $wouldInstall = $false + $reasons.Add('explorer.exe is running') +} else { + Write-Step -Status 'PASS' -Message 'Guard 4: explorer.exe is not running.' +} + +# Guard 5: 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. @@ -113,24 +133,24 @@ try { $uptimeHours = ((Get-Date) - $lastBoot).TotalHours if ($uptimeHours -gt $MaxBootstrapWindowHours) { - Write-Step -Status 'SKIP' -Message "Guard 3: Device uptime is $([int]$uptimeHours)h. Older than accepted bootstrap window of ${MaxBootstrapWindowHours}h." + Write-Step -Status 'SKIP' -Message "Guard 5: 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 3: Device uptime is $([int]$uptimeHours)h and within bootstrap window of ${MaxBootstrapWindowHours}h." + Write-Step -Status 'PASS' -Message "Guard 5: Device uptime is $([int]$uptimeHours)h and within bootstrap window of ${MaxBootstrapWindowHours}h." } } catch { - Write-Step -Status 'WARN' -Message 'Guard 3: Could not determine LastBootUpTime / uptime.' + Write-Step -Status 'WARN' -Message 'Guard 5: Could not determine LastBootUpTime / uptime.' } -# Guard 4: Agent binary already present +# Guard 6: Agent binary already present if (Test-Path $AgentExePath) { - Write-Step -Status 'SKIP' -Message "Guard 4: Agent already installed at '$AgentExePath'." + Write-Step -Status 'SKIP' -Message "Guard 6: Agent already installed at '$AgentExePath'." $wouldInstall = $false $reasons.Add("Agent binary already present: $AgentExePath") } else { - Write-Step -Status 'PASS' -Message "Guard 4: Agent binary not present at '$AgentExePath'." + Write-Step -Status 'PASS' -Message "Guard 6: Agent binary not present at '$AgentExePath'." } Write-Host '' @@ -157,9 +177,11 @@ if ($reasons.Count -eq 0) { Write-Host '' Write-Host '=== Raw Values ===' Write-Host "Deployed marker : $deployed" +Write-Host "DetectedProfileNames : $($profileNames -join ', ')" +Write-Host "LastLoggedOnUser : $lastLoggedOnUser" +Write-Host "ExplorerRunning : $explorerRunning" Write-Host "MaxBootstrapWindowHours : $MaxBootstrapWindowHours" Write-Host "LastBootUpTime : $lastBoot" Write-Host "UptimeHours : $([string]$uptimeHours)" Write-Host "AgentExePath : $AgentExePath" -Write-Host "DetectedProfileNames : $($profileNames -join ', ')" Write-Host '' diff --git a/src/Agent/AutopilotMonitor.Agent/buildcounter.txt b/src/Agent/AutopilotMonitor.Agent/buildcounter.txt index 3e40c389..c128405c 100644 --- a/src/Agent/AutopilotMonitor.Agent/buildcounter.txt +++ b/src/Agent/AutopilotMonitor.Agent/buildcounter.txt @@ -1 +1 @@ -738 +739 diff --git a/src/Web/autopilot-monitor-web/app/docs/sections/SectionAgentSetup.tsx b/src/Web/autopilot-monitor-web/app/docs/sections/SectionAgentSetup.tsx index ec14a493..234a53b2 100644 --- a/src/Web/autopilot-monitor-web/app/docs/sections/SectionAgentSetup.tsx +++ b/src/Web/autopilot-monitor-web/app/docs/sections/SectionAgentSetup.tsx @@ -36,7 +36,19 @@ export function SectionAgentSetup() { Win32_UserProfile.Special{" "} ) and filesystem checks under{" "} C:\Users{" "} - — system profiles (defaultuser*, Public, Default) are excluded. Real user profiles indicate the device is already in productive use. This is the primary guard against accidental installation on non-provisioning devices. + — system profiles (defaultuser*, Public, Default) are excluded. Real user profiles indicate the device is already in productive use. + +
  • + ✓ + No previous user logon: Checks{" "} + LastLoggedOnUser{" "} + in the LogonUI registry — during Device ESP no real user has logged on yet. A real username indicates the device has been used interactively. +
  • +
  • + ✓ + No desktop shell: Checks if{" "} + explorer.exe{" "} + is running — during Device ESP the enrollment status page is shown instead of the Windows desktop. A running explorer process indicates the device is beyond initial enrollment.
  • ✓