Files
AutopilotMonitor/scripts/Bootstrap
Oliver Kieselbach 3016c15919 feat(agent): event-driven TenantId wait via RegistryWatcher
Bridges the OOBE / hybrid-AAD-join race window where the agent fires
before AADTenantID / CloudDomainJoin\TenantInfo are written to the
registry. Field evidence (GardnerMedia, 2026-04-30): the agent fired
~5 minutes before the AAD device cert landed; an event-driven wait of
10 minutes recovers the run instead of bailing and waiting for next
reboot.

Mechanism (V2 only — V1 keeps the fast-fail path):
* New TenantIdAwaiter.WaitForTenantId(timeoutSec, logger, ct) sets up
  a CompositeRegistryChangeSignal over HKLM\SOFTWARE\Microsoft\Enrollments
  and HKLM\SYSTEM\...\CloudDomainJoin (Registry64 view, recursive,
  Name+LastSet+ThreadAgnostic filter), re-probes silently on every
  change, and returns the resolved TenantId or null on timeout.
* 30 s belt-and-suspenders periodic re-probe protects against a missed
  RegNotifyChangeKeyValue notification.
* 100 ms debounce after each signal so multi-value enrollment
  transactions land before the re-probe.

Configuration:
* Opt-in via --tenant-id-wait <sec>. Default 0 = no wait (legacy
  fast-fail). The PS1 bootstrap script owns the production default;
  the agent itself never assumes one.
* CLI value persisted to bootstrap-config.json by --install so the
  Scheduled Task picks it up on each restart.
* Install-AutopilotMonitor-v2.ps1 sets $tenantIdWaitSec = 600. Admins
  override per tenant by editing this single line and re-deploying.

Testability:
* IRegistryChangeSignal abstraction lets unit tests drive the loop
  with a fake signal and scripted probe — 8 deterministic cases cover
  timeout, signal hit, multi-signal, cancellation, periodic re-probe,
  whitespace/empty/null handling, zero-timeout short-circuit, and
  argument validation.
* RegistryWatcherIntegrationTests exercise the real Win32 plumbing
  (RegNotifyChangeKeyValue → Changed event) against HKCU so no
  elevation is required and the test runs in standard CI.
* Both classes opt out of xUnit parallelisation — the timing-sensitive
  Thread.Sleep handshakes are unreliable on a busy parallel pool.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 21:54:43 +02:00
..
2026-03-31 23:54:46 +02:00