diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Core/MonitoringService.cs b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Core/MonitoringService.cs
index 95ba6138..32aeb973 100644
--- a/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Core/MonitoringService.cs
+++ b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Core/MonitoringService.cs
@@ -474,6 +474,28 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
_completionEvent.Wait();
}
+ ///
+ /// Triggers cleanup without running enrollment monitoring.
+ /// Used when enrollment complete marker is detected on startup (cleanup retry).
+ ///
+ public void TriggerCleanup()
+ {
+ _logger.Info("TriggerCleanup invoked - executing cleanup without enrollment monitoring");
+
+ if (_configuration.SelfDestructOnComplete)
+ {
+ ExecuteSelfDestruct();
+ }
+ else if (_configuration.CleanupOnExit)
+ {
+ ExecuteCleanup();
+ }
+ else
+ {
+ _logger.Info("No cleanup configured - nothing to do");
+ }
+ }
+
///
/// Stops all event collection components
///
diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/EnrollmentTracker.cs b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/EnrollmentTracker.cs
index 8db53a60..fd0dd2b4 100644
--- a/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/EnrollmentTracker.cs
+++ b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/EnrollmentTracker.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Management;
using System.Threading;
@@ -823,6 +824,9 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Tracking
Message = "Autopilot enrollment completed successfully (user session completed)"
});
+ // Write enrollment complete marker for cleanup retry detection
+ WriteEnrollmentCompleteMarker();
+
// Collect final device info (BitLocker may have been enabled during enrollment)
CollectDeviceInfoAtEnd();
@@ -855,6 +859,9 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Tracking
Message = "Autopilot enrollment completed successfully (Hello provisioning completed)"
});
+ // Write enrollment complete marker for cleanup retry detection
+ WriteEnrollmentCompleteMarker();
+
// Clean up persisted tracker state so next enrollment starts fresh
_imeLogTracker?.DeleteState();
}
@@ -904,5 +911,27 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Tracking
_summaryTimer?.Dispose();
_imeLogTracker?.Dispose();
}
+
+ ///
+ /// Writes an enrollment complete marker to the state directory.
+ /// This marker is checked on agent restart to handle cleanup retry if scheduled task fails.
+ ///
+ private void WriteEnrollmentCompleteMarker()
+ {
+ try
+ {
+ var stateDirectory = Environment.ExpandEnvironmentVariables(@"%ProgramData%\AutopilotMonitor\State");
+ Directory.CreateDirectory(stateDirectory);
+
+ var markerPath = Path.Combine(stateDirectory, "enrollment-complete.marker");
+ File.WriteAllText(markerPath, $"Enrollment completed at {DateTime.UtcNow:O}");
+
+ _logger.Info($"Enrollment complete marker written: {markerPath}");
+ }
+ catch (Exception ex)
+ {
+ _logger.Warning($"Failed to write enrollment complete marker: {ex.Message}");
+ }
+ }
}
}
diff --git a/src/Agent/AutopilotMonitor.Agent/Program.cs b/src/Agent/AutopilotMonitor.Agent/Program.cs
index 28f6244c..45fd8079 100644
--- a/src/Agent/AutopilotMonitor.Agent/Program.cs
+++ b/src/Agent/AutopilotMonitor.Agent/Program.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using AutopilotMonitor.Agent.Core.Configuration;
@@ -31,6 +32,26 @@ namespace AutopilotMonitor.Agent
{
var consoleMode = args.Contains("--console") || Environment.UserInteractive;
+ // Process guard: prevent multiple agent instances from running
+ if (IsAnotherAgentInstanceRunning())
+ {
+ var message = "Another agent process is already running. This instance will exit.";
+ if (consoleMode)
+ Console.WriteLine($"ERROR: {message}");
+
+ // Try to log if possible, but this might fail if logger isn't initialized yet
+ try
+ {
+ var logDir = Environment.ExpandEnvironmentVariables(@"%ProgramData%\AutopilotMonitor\Logs");
+ var logger = new AgentLogger(logDir, enableDebug: false);
+ logger.Warning(message);
+ }
+ catch { }
+
+ Environment.Exit(1);
+ return;
+ }
+
if (consoleMode)
{
Console.WriteLine("Autopilot Monitor Agent");
@@ -49,6 +70,13 @@ namespace AutopilotMonitor.Agent
logger.Info($"============================== Agent starting ({(consoleMode ? "console" : "background/SYSTEM")}) ==============================");
logger.Info("============================================================================================================");
+ // Check for enrollment complete marker (handles scheduled task cleanup retry)
+ if (CheckEnrollmentCompleteMarker(config, logger, consoleMode))
+ {
+ // Marker was found and handled (cleanup executed or skipped) - exit
+ return;
+ }
+
if (consoleMode)
{
Console.WriteLine($"Session ID: {config.SessionId}");
@@ -399,5 +427,89 @@ namespace AutopilotMonitor.Agent
KeepLogFile = keepLogFile
};
}
+
+ ///
+ /// Checks if another AutopilotMonitor.Agent.exe process is already running.
+ ///
+ static bool IsAnotherAgentInstanceRunning()
+ {
+ try
+ {
+ var currentProcess = Process.GetCurrentProcess();
+ var processes = Process.GetProcessesByName(currentProcess.ProcessName);
+
+ // If there's more than one process with our name, another instance is running
+ return processes.Length > 1;
+ }
+ catch
+ {
+ // If we can't check, assume no conflict
+ return false;
+ }
+ }
+
+ ///
+ /// Checks for enrollment complete marker and handles cleanup retry if needed.
+ /// Returns true if marker was found and agent should exit.
+ ///
+ static bool CheckEnrollmentCompleteMarker(AgentConfiguration config, AgentLogger logger, bool consoleMode)
+ {
+ var stateDirectory = Environment.ExpandEnvironmentVariables(@"%ProgramData%\AutopilotMonitor\State");
+ var markerPath = Path.Combine(stateDirectory, "enrollment-complete.marker");
+
+ if (!File.Exists(markerPath))
+ {
+ // No marker - proceed with normal enrollment
+ return false;
+ }
+
+ logger.Info("Enrollment complete marker detected from previous session");
+
+ if (!config.CleanupOnExit && !config.SelfDestructOnComplete)
+ {
+ // No cleanup configured - just exit
+ logger.Info("Enrollment already completed (no cleanup is configured). Agent will exit.");
+ if (consoleMode)
+ Console.WriteLine("Enrollment already completed (no cleanup is configured). Agent will exit.");
+ return true;
+ }
+
+ // Cleanup is configured - attempt it now in case scheduled task failed
+ logger.Info("Enrollment already completed. Attempting cleanup retry (scheduled task may have failed)...");
+ if (consoleMode)
+ Console.WriteLine("Enrollment already completed. Attempting cleanup retry...");
+
+ try
+ {
+ using (var service = new MonitoringService(config, logger))
+ {
+ // Trigger cleanup directly without running enrollment
+ if (config.SelfDestructOnComplete)
+ {
+ logger.Info("Executing self-destruct cleanup...");
+ // Call the internal cleanup method via reflection or expose it
+ // For now, we'll create a minimal service and let it clean up on dispose
+ service.TriggerCleanup();
+ }
+ else if (config.CleanupOnExit)
+ {
+ logger.Info("Executing standard cleanup...");
+ service.TriggerCleanup();
+ }
+ }
+
+ logger.Info("Cleanup retry completed. Agent will exit.");
+ if (consoleMode)
+ Console.WriteLine("Cleanup retry completed. Agent will exit.");
+ }
+ catch (Exception ex)
+ {
+ logger.Warning($"Cleanup retry failed: {ex.Message}");
+ if (consoleMode)
+ Console.WriteLine($"WARNING: Cleanup retry failed: {ex.Message}");
+ }
+
+ return true;
+ }
}
}