mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 12:42:38 +00:00
added ESP Configuration detection
This commit is contained in:
@@ -38,9 +38,9 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Collectors
|
||||
|
||||
/// <summary>
|
||||
/// Runs all device info collectors at agent startup.
|
||||
/// Returns the detected enrollment type ("v1" or "v2") and whether the profile indicates Hybrid Join.
|
||||
/// Returns the detected enrollment type, Hybrid Join flag, and ESP skip configuration from registry.
|
||||
/// </summary>
|
||||
public (string enrollmentType, bool isHybridJoin) CollectAll()
|
||||
public (string enrollmentType, bool isHybridJoin, bool? skipUserStatusPage, bool? skipDeviceStatusPage) CollectAll()
|
||||
{
|
||||
_logger.Info("EnrollmentTracker: collecting device info (at start)");
|
||||
|
||||
@@ -48,12 +48,13 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Collectors
|
||||
CollectNetworkAndDnsConfiguration();
|
||||
CollectProxyConfiguration();
|
||||
var profileResult = CollectAutopilotProfile();
|
||||
var espConfig = CollectEspConfiguration();
|
||||
CollectSecureBootStatus();
|
||||
CollectBitLockerStatus();
|
||||
CollectTpmStatus();
|
||||
CollectAadJoinStatus();
|
||||
|
||||
return profileResult;
|
||||
return (profileResult.enrollmentType, profileResult.isHybridJoin, espConfig.skipUserStatusPage, espConfig.skipDeviceStatusPage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -581,6 +582,86 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Collectors
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads ESP skip configuration from the enrollment FirstSync registry key.
|
||||
/// CSP SkipUserStatusPage/SkipDeviceStatusPage are written as DWORD:
|
||||
/// 0xFFFFFFFF = skip (ESP page not shown), 0 = show, key missing = unknown.
|
||||
/// We use defensive interpretation: non-null AND non-zero = skip.
|
||||
/// </summary>
|
||||
private (bool? skipUserStatusPage, bool? skipDeviceStatusPage) CollectEspConfiguration()
|
||||
{
|
||||
bool? skipUser = null;
|
||||
bool? skipDevice = null;
|
||||
string enrollmentGuid = null;
|
||||
|
||||
try
|
||||
{
|
||||
const string enrollmentsKeyPath = @"SOFTWARE\Microsoft\Enrollments";
|
||||
|
||||
using (var enrollmentsKey = Registry.LocalMachine.OpenSubKey(enrollmentsKeyPath))
|
||||
{
|
||||
if (enrollmentsKey == null)
|
||||
{
|
||||
_logger.Debug("EnrollmentTracker: Enrollments registry key not found — ESP config unknown");
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
// Find the MDM enrollment entry (EnrollmentType == 6)
|
||||
foreach (var guid in enrollmentsKey.GetSubKeyNames())
|
||||
{
|
||||
using (var enrollmentKey = enrollmentsKey.OpenSubKey(guid))
|
||||
{
|
||||
if (enrollmentKey == null) continue;
|
||||
|
||||
var enrollmentType = enrollmentKey.GetValue("EnrollmentType");
|
||||
if (enrollmentType == null || Convert.ToInt32(enrollmentType) != 6)
|
||||
continue;
|
||||
|
||||
enrollmentGuid = guid;
|
||||
|
||||
// Read FirstSync subkey
|
||||
using (var firstSyncKey = enrollmentKey.OpenSubKey("FirstSync"))
|
||||
{
|
||||
if (firstSyncKey == null)
|
||||
{
|
||||
_logger.Debug($"EnrollmentTracker: FirstSync subkey not found for enrollment {guid}");
|
||||
break;
|
||||
}
|
||||
|
||||
var rawSkipUser = firstSyncKey.GetValue("SkipUserStatusPage");
|
||||
if (rawSkipUser != null)
|
||||
skipUser = Convert.ToInt32(rawSkipUser) != 0;
|
||||
|
||||
var rawSkipDevice = firstSyncKey.GetValue("SkipDeviceStatusPage");
|
||||
if (rawSkipDevice != null)
|
||||
skipDevice = Convert.ToInt32(rawSkipDevice) != 0;
|
||||
}
|
||||
|
||||
break; // Found the MDM enrollment entry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var data = new Dictionary<string, object>
|
||||
{
|
||||
{ "source", "registry_firstsync" }
|
||||
};
|
||||
if (enrollmentGuid != null) data["enrollmentGuid"] = enrollmentGuid;
|
||||
if (skipUser.HasValue) data["skipUserStatusPage"] = skipUser.Value;
|
||||
if (skipDevice.HasValue) data["skipDeviceStatusPage"] = skipDevice.Value;
|
||||
|
||||
var summary = $"SkipUser={skipUser?.ToString() ?? "unknown"}, SkipDevice={skipDevice?.ToString() ?? "unknown"}";
|
||||
_logger.Info($"EnrollmentTracker: ESP configuration detected — {summary}");
|
||||
EmitDeviceInfoEvent("esp_config_detected", $"ESP configuration: {summary}", data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warning($"EnrollmentTracker: failed to collect ESP configuration: {ex.Message}");
|
||||
}
|
||||
|
||||
return (skipUser, skipDevice);
|
||||
}
|
||||
|
||||
private void EmitDeviceInfoEvent(string eventType, string message, Dictionary<string, object> data)
|
||||
{
|
||||
_emitEvent(new EnrollmentEvent
|
||||
|
||||
+4
@@ -107,6 +107,10 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Tracking
|
||||
public bool EnrollmentCompleteEmitted { get; set; }
|
||||
public string EnrollmentType { get; set; }
|
||||
|
||||
// ESP configuration from registry (FirstSync\SkipUserStatusPage / SkipDeviceStatusPage)
|
||||
public bool? SkipUserStatusPage { get; set; }
|
||||
public bool? SkipDeviceStatusPage { get; set; }
|
||||
|
||||
// Signal timestamps for audit trail
|
||||
public DateTime? EspFirstSeenUtc { get; set; }
|
||||
public DateTime? EspFinalExitUtc { get; set; }
|
||||
|
||||
@@ -57,6 +57,11 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Tracking
|
||||
private Timer _deviceOnlyEspTimer;
|
||||
private const int DeviceOnlyEspTimerMinutes = 5;
|
||||
|
||||
// ESP configuration from registry (FirstSync\SkipUserStatusPage / SkipDeviceStatusPage)
|
||||
// null = unknown (registry keys not present), true = skip, false = show
|
||||
private bool? _skipUserStatusPage;
|
||||
private bool? _skipDeviceStatusPage;
|
||||
|
||||
// Safety-net timer for waiting_for_hello state
|
||||
private Timer _waitingForHelloSafetyTimer;
|
||||
private const int WaitingForHelloSafetyTimeoutSeconds = 420; // 7 min — longer than Hello chain (330s)
|
||||
@@ -241,7 +246,11 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Tracking
|
||||
{
|
||||
var result = _deviceInfoCollector.CollectAll();
|
||||
_enrollmentType = result.enrollmentType;
|
||||
_skipUserStatusPage = result.skipUserStatusPage;
|
||||
_skipDeviceStatusPage = result.skipDeviceStatusPage;
|
||||
_stateData.EnrollmentType = _enrollmentType;
|
||||
_stateData.SkipUserStatusPage = _skipUserStatusPage;
|
||||
_stateData.SkipDeviceStatusPage = _skipDeviceStatusPage;
|
||||
_stateDirty = true;
|
||||
}
|
||||
|
||||
@@ -873,10 +882,44 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Tracking
|
||||
// during AccountSetup), the composite signal can fire immediately.
|
||||
TryEmitEnrollmentComplete("esp_hello_composite");
|
||||
}
|
||||
else if (_skipUserStatusPage == true)
|
||||
{
|
||||
// Registry definitively says no AccountSetup expected → immediate device-only classification
|
||||
_logger.Info($"EnrollmentTracker: ESP phase exiting from '{_lastEspPhase ?? "unknown"}' — SkipUserStatusPage=true, classified as device-only ESP (registry)");
|
||||
|
||||
_espFinalExitSeen = true;
|
||||
_stateData.EspFinalExitSeen = true;
|
||||
_stateData.EspFinalExitUtc = DateTime.UtcNow;
|
||||
RecordSignal("device_only_esp_registry");
|
||||
|
||||
_emitEvent(new EnrollmentEvent
|
||||
{
|
||||
SessionId = _sessionId,
|
||||
TenantId = _tenantId,
|
||||
EventType = "esp_phase_changed",
|
||||
Severity = EventSeverity.Info,
|
||||
Source = "EnrollmentTracker",
|
||||
Phase = EnrollmentPhase.FinalizingSetup,
|
||||
Message = "ESP phase: FinalizingSetup (device-only ESP — SkipUserStatusPage confirmed via registry)",
|
||||
Data = new Dictionary<string, object>
|
||||
{
|
||||
{ "espPhase", "FinalizingSetup" },
|
||||
{ "autoDetected", true },
|
||||
{ "triggeredBy", "device_only_esp_registry" },
|
||||
{ "previousPhase", _lastEspPhase ?? "unknown" },
|
||||
{ "skipUserStatusPage", true }
|
||||
}
|
||||
});
|
||||
|
||||
CollectDeviceInfoAtFinalizingSetup("device_only_esp_registry");
|
||||
_espAndHelloTracker?.StartHelloWaitTimer();
|
||||
TryEmitEnrollmentComplete("device_only_esp_registry");
|
||||
}
|
||||
else
|
||||
{
|
||||
// DeviceSetup phase exiting without desktop — intermediate transition (Device→Account) or device-only ESP
|
||||
_logger.Info($"EnrollmentTracker: ESP phase exiting from '{_lastEspPhase ?? "unknown"}' - intermediate transition, starting device-only ESP detection timer ({DeviceOnlyEspTimerMinutes}min)");
|
||||
// Registry keys unknown or SkipUserStatusPage=false → fallback to timer-based detection
|
||||
var fallbackReason = _skipUserStatusPage == null ? "registry keys not found" : "SkipUserStatusPage=false";
|
||||
_logger.Info($"EnrollmentTracker: ESP phase exiting from '{_lastEspPhase ?? "unknown"}' — {fallbackReason}, starting device-only ESP detection timer ({DeviceOnlyEspTimerMinutes}min)");
|
||||
|
||||
// Start device-only ESP detection timer
|
||||
_deviceOnlyEspTimer?.Dispose();
|
||||
@@ -928,6 +971,8 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Tracking
|
||||
_isWaitingForHello = loaded.IsWaitingForHello;
|
||||
_enrollmentCompleteEmitted = loaded.EnrollmentCompleteEmitted;
|
||||
_enrollmentType = loaded.EnrollmentType ?? _enrollmentType;
|
||||
_skipUserStatusPage = loaded.SkipUserStatusPage;
|
||||
_skipDeviceStatusPage = loaded.SkipDeviceStatusPage;
|
||||
_stateData = loaded;
|
||||
|
||||
_logger.Info($"EnrollmentTracker: state restored — espEverSeen={_espEverSeen}, espFinalExitSeen={_espFinalExitSeen}, desktopArrived={_desktopArrived}, lastEspPhase={_lastEspPhase}, enrollmentCompleteEmitted={_enrollmentCompleteEmitted}");
|
||||
@@ -1083,7 +1128,9 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Tracking
|
||||
{ "helloResolved", _espAndHelloTracker?.IsHelloCompleted ?? false },
|
||||
{ "helloPolicyConfigured", _espAndHelloTracker?.IsPolicyConfigured ?? false },
|
||||
{ "enrollmentType", _enrollmentType },
|
||||
{ "lastEspPhase", _lastEspPhase ?? "none" }
|
||||
{ "lastEspPhase", _lastEspPhase ?? "none" },
|
||||
{ "skipUserStatusPage", _skipUserStatusPage?.ToString() ?? "unknown" },
|
||||
{ "skipDeviceStatusPage", _skipDeviceStatusPage?.ToString() ?? "unknown" }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
212
|
||||
214
|
||||
|
||||
Reference in New Issue
Block a user