diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Configuration/AgentConfiguration.cs b/src/Agent/AutopilotMonitor.Agent.Core/Configuration/AgentConfiguration.cs
index a2cac1cb..e3de86ea 100644
--- a/src/Agent/AutopilotMonitor.Agent.Core/Configuration/AgentConfiguration.cs
+++ b/src/Agent/AutopilotMonitor.Agent.Core/Configuration/AgentConfiguration.cs
@@ -92,6 +92,12 @@ namespace AutopilotMonitor.Agent.Core.Configuration
///
public bool RebootOnComplete { get; set; } = false;
+ ///
+ /// When true, the log directory is preserved after self-destruct/cleanup.
+ /// All other files (binaries, config, spool, scheduled task) are still removed.
+ ///
+ public bool KeepLogFile { get; set; } = false;
+
///
/// Whether to enable geo-location detection (queries external IP services)
///
diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Core/MonitoringService.cs b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Core/MonitoringService.cs
index 53a9968b..33f5d5e0 100644
--- a/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Core/MonitoringService.cs
+++ b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Core/MonitoringService.cs
@@ -26,6 +26,7 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
private readonly Timer _uploadTimer;
private readonly Timer _debounceTimer;
private readonly object _timerLock = new object();
+ private readonly ManualResetEventSlim _completionEvent = new(false);
private long _eventSequence = 0;
private EnrollmentPhase? _lastPhase = null;
@@ -496,6 +497,18 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
UploadEventsAsync().Wait(TimeSpan.FromSeconds(10));
_logger.Info("Monitoring service stopped");
+
+ // Unblock WaitForCompletion() in background/SYSTEM mode
+ _completionEvent.Set();
+ }
+
+ ///
+ /// Blocks the calling thread until the service stops (used when running as Scheduled Task
+ /// under SYSTEM where there is no interactive Console.ReadLine() to keep the process alive).
+ ///
+ public void WaitForCompletion()
+ {
+ _completionEvent.Wait();
}
///
@@ -778,37 +791,54 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
"AutopilotMonitor"
));
+ var logDir = Path.GetFullPath(Path.Combine(agentBasePath, "Logs"));
+ var keepLogs = _configuration.KeepLogFile;
+
// Create a self-deleting PowerShell script
var cleanupScript = $@"
+$scriptPath = $MyInvocation.MyCommand.Path
+
# Wait for agent process to exit
-Start-Sleep -Seconds 3
+Start-Sleep -Seconds 2
Wait-Process -Id {currentProcessId} -ErrorAction SilentlyContinue -Timeout 30
+Start-Sleep -Seconds 1
# Remove Scheduled Task
-Write-Host 'Removing Scheduled Task: {_configuration.ScheduledTaskName}'
try {{
Stop-ScheduledTask -TaskName '{_configuration.ScheduledTaskName}' -ErrorAction SilentlyContinue
Unregister-ScheduledTask -TaskName '{_configuration.ScheduledTaskName}' -Confirm:$false -ErrorAction SilentlyContinue
- Write-Host 'Scheduled Task removed successfully'
-}} catch {{
- Write-Host ""Failed to remove Scheduled Task: $_""
-}}
+}} catch {{ }}
-# Delete AutopilotMonitor folder
-Write-Host 'Deleting folder: {agentBasePath}'
-try {{
- Remove-Item -Path '{agentBasePath}' -Recurse -Force -ErrorAction Stop
- Write-Host 'Folder deleted successfully'
-}} catch {{
- Write-Host ""Failed to delete folder: $_""
+{(keepLogs ? $@"
+# Delete everything except Logs directory
+Get-ChildItem -Path '{agentBasePath}' -Exclude 'Logs' | ForEach-Object {{
+ $dest = $_.FullName + '.del'
+ try {{ Rename-Item -Path $_.FullName -NewName $dest -Force -ErrorAction SilentlyContinue }} catch {{ }}
+ Remove-Item -Path $dest -Recurse -Force -ErrorAction SilentlyContinue
}}
-
-Write-Host 'AutopilotMonitor Agent self-destruct complete'
+" : $@"
+# Rename folder then delete. Retry a few times to let the OS release handles.
+$renamedPath = '{agentBasePath}.del'
+$renamed = $false
+for ($i = 1; $i -le 10; $i++) {{
+ try {{
+ Rename-Item -Path '{agentBasePath}' -NewName $renamedPath -Force -ErrorAction Stop
+ $renamed = $true
+ break
+ }} catch {{
+ Start-Sleep -Seconds 2
+ }}
+}}
+if ($renamed) {{
+ Remove-Item -Path $renamedPath -Recurse -Force -ErrorAction SilentlyContinue
+}} else {{
+ Remove-Item -Path '{agentBasePath}' -Recurse -Force -ErrorAction SilentlyContinue
+}}
+")}
{(_configuration.RebootOnComplete ? @"
-# Reboot the device
-Write-Host 'Initiating reboot...'
-Restart-Computer -Force
+Restart-Computer -Force -Delay 10 -Comment 'Autopilot enrollment completed - Autopilot Monitor is rebooting'
" : "")}
+Remove-Item -Path $scriptPath -Force -ErrorAction SilentlyContinue
";
// Write cleanup script to temp location (outside of agent folder)
@@ -816,13 +846,21 @@ Restart-Computer -Force
File.WriteAllText(tempScriptPath, cleanupScript);
_logger.Info($"Cleanup script written to {tempScriptPath}");
- // Launch cleanup script in background (detached process)
+ // Change CWD to temp so this process no longer holds a reference into
+ // the AutopilotMonitor folder tree - Windows won't allow renaming a
+ // directory that any process has as its current working directory.
+ try { Directory.SetCurrentDirectory(Path.GetTempPath()); } catch { }
+
+ // Launch via cmd /c start so the powershell process is created outside the
+ // current Job Object (Scheduled Task job). cmd's 'start' command always
+ // creates a new process group that breaks job inheritance, even under SYSTEM.
var psi = new ProcessStartInfo
{
- FileName = "powershell.exe",
- Arguments = $"-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File \"{tempScriptPath}\"",
+ FileName = "cmd.exe",
+ Arguments = $"/c start \"\" /b powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File \"{tempScriptPath}\"",
UseShellExecute = false,
- CreateNoWindow = true
+ CreateNoWindow = true,
+ WorkingDirectory = Path.GetTempPath()
};
Process.Start(psi);
@@ -849,27 +887,38 @@ Restart-Computer -Force
"AutopilotMonitor"
));
- // Create a cleanup PowerShell script
+ var logDir = Path.GetFullPath(Path.Combine(agentBasePath, "Logs"));
+ var keepLogs = _configuration.KeepLogFile;
+
+ // Create a self-deleting cleanup PowerShell script
var cleanupScript = $@"
-# Wait for agent process to exit
-Start-Sleep -Seconds 3
+$scriptPath = $MyInvocation.MyCommand.Path
+
+# Wait for agent process to exit. Wait-Process may fail if the process is already gone - that is fine.
+Start-Sleep -Seconds 2
Wait-Process -Id {currentProcessId} -ErrorAction SilentlyContinue -Timeout 30
+Start-Sleep -Seconds 1
-# Delete AutopilotMonitor folder
-Write-Host 'Deleting folder: {agentBasePath}'
-try {{
- Remove-Item -Path '{agentBasePath}' -Recurse -Force -ErrorAction Stop
- Write-Host 'Folder deleted successfully'
-}} catch {{
- Write-Host ""Failed to delete folder: $_""
+{(keepLogs ? $@"
+# Delete everything except Logs directory.
+# Rename subdirs/files first so locked EXE bytes are released, then remove.
+Get-ChildItem -Path '{agentBasePath}' -Exclude 'Logs' | ForEach-Object {{
+ $dest = $_.FullName + '.del'
+ try {{ Rename-Item -Path $_.FullName -NewName $dest -Force -ErrorAction SilentlyContinue }} catch {{ }}
+ Remove-Item -Path $dest -Recurse -Force -ErrorAction SilentlyContinue
}}
-
-Write-Host 'AutopilotMonitor Agent cleanup complete'
+" : $@"
+# Rename the folder first (works even while EXE bytes are still mapped),
+# then delete the renamed copy - by then all handles are closed.
+$renamedPath = '{agentBasePath}.del'
+try {{ Rename-Item -Path '{agentBasePath}' -NewName $renamedPath -Force -ErrorAction Stop }} catch {{ $renamedPath = '{agentBasePath}' }}
+Remove-Item -Path $renamedPath -Recurse -Force -ErrorAction SilentlyContinue
+")}
{(_configuration.RebootOnComplete ? @"
-# Reboot the device
-Write-Host 'Initiating reboot...'
Restart-Computer -Force
" : "")}
+# Delete this script
+Remove-Item -Path $scriptPath -Force -ErrorAction SilentlyContinue
";
// Write cleanup script to temp location
@@ -877,11 +926,13 @@ Restart-Computer -Force
File.WriteAllText(tempScriptPath, cleanupScript);
_logger.Info($"Cleanup script written to {tempScriptPath}");
- // Launch cleanup script
+ // Launch via cmd /c start so the powershell process is created outside the
+ // current Job Object (Scheduled Task job). cmd's 'start' command always
+ // creates a new process group that breaks job inheritance, even under SYSTEM.
var psi = new ProcessStartInfo
{
- FileName = "powershell.exe",
- Arguments = $"-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File \"{tempScriptPath}\"",
+ FileName = "cmd.exe",
+ Arguments = $"/c start \"\" /b powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File \"{tempScriptPath}\"",
UseShellExecute = false,
CreateNoWindow = true
};
@@ -907,6 +958,7 @@ Restart-Computer -Force
_enrollmentTracker?.Dispose();
_gatherRuleExecutor?.Dispose();
_remoteConfigService?.Dispose();
+ _completionEvent.Dispose();
}
}
}
diff --git a/src/Agent/AutopilotMonitor.Agent/AutopilotMonitor.Agent.csproj b/src/Agent/AutopilotMonitor.Agent/AutopilotMonitor.Agent.csproj
index 965c87a8..b9a4c5cd 100644
--- a/src/Agent/AutopilotMonitor.Agent/AutopilotMonitor.Agent.csproj
+++ b/src/Agent/AutopilotMonitor.Agent/AutopilotMonitor.Agent.csproj
@@ -13,12 +13,7 @@
-
-
-
-
-
diff --git a/src/Agent/AutopilotMonitor.Agent/AutopilotMonitorService.cs b/src/Agent/AutopilotMonitor.Agent/AutopilotMonitorService.cs
deleted file mode 100644
index 58f21115..00000000
--- a/src/Agent/AutopilotMonitor.Agent/AutopilotMonitorService.cs
+++ /dev/null
@@ -1,83 +0,0 @@
-using System;
-using System.ServiceProcess;
-using AutopilotMonitor.Agent.Core.Configuration;
-using AutopilotMonitor.Agent.Core.Logging;
-using AutopilotMonitor.Agent.Core.Monitoring.Core;
-
-namespace AutopilotMonitor.Agent
-{
- ///
- /// Windows Service wrapper for the monitoring agent
- ///
- public class AutopilotMonitorService : ServiceBase
- {
- private MonitoringService _monitoringService;
- private AgentLogger _logger;
-
- public AutopilotMonitorService()
- {
- ServiceName = "AutopilotMonitor";
- CanStop = true;
- CanPauseAndContinue = false;
- AutoLog = true;
- }
-
- protected override void OnStart(string[] args)
- {
- try
- {
- var config = LoadConfiguration();
- var logDir = Environment.ExpandEnvironmentVariables(config.LogDirectory);
- _logger = new AgentLogger(logDir, config.EnableDebugLogging);
-
- _logger.Info("Windows Service starting");
-
- _monitoringService = new MonitoringService(config, _logger);
- _monitoringService.Start();
-
- _logger.Info("Windows Service started");
- }
- catch (Exception ex)
- {
- _logger?.Error("Failed to start service", ex);
- throw;
- }
- }
-
- protected override void OnStop()
- {
- try
- {
- _logger?.Info("Windows Service stopping");
- _monitoringService?.Stop();
- _monitoringService?.Dispose();
- _logger?.Info("Windows Service stopped");
- }
- catch (Exception ex)
- {
- _logger?.Error("Error stopping service", ex);
- }
- }
-
- private AgentConfiguration LoadConfiguration()
- {
- // Load or create persisted session ID
- var dataDirectory = Environment.ExpandEnvironmentVariables(@"%ProgramData%\AutopilotMonitor");
- var sessionPersistence = new SessionPersistence(dataDirectory);
- var sessionId = sessionPersistence.LoadOrCreateSessionId();
-
- return new AgentConfiguration
- {
- //ApiBaseUrl = Environment.GetEnvironmentVariable("AUTOPILOT_MONITOR_API") ?? "http://localhost:7071",
- ApiBaseUrl = Environment.GetEnvironmentVariable("AUTOPILOT_MONITOR_API") ?? "https://autopilotmonitor-func.azurewebsites.net",
- SessionId = sessionId,
- TenantId = "default-tenant",
- SpoolDirectory = Environment.ExpandEnvironmentVariables(@"%ProgramData%\AutopilotMonitor\Spool"),
- LogDirectory = Environment.ExpandEnvironmentVariables(@"%ProgramData%\AutopilotMonitor\Logs"),
- UploadIntervalSeconds = 30,
- MaxBatchSize = 100,
- EnableDebugLogging = false
- };
- }
- }
-}
diff --git a/src/Agent/AutopilotMonitor.Agent/Program.cs b/src/Agent/AutopilotMonitor.Agent/Program.cs
index d1d1b96e..ffb8acd1 100644
--- a/src/Agent/AutopilotMonitor.Agent/Program.cs
+++ b/src/Agent/AutopilotMonitor.Agent/Program.cs
@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.ServiceProcess;
using AutopilotMonitor.Agent.Core.Configuration;
using AutopilotMonitor.Agent.Core.Logging;
using AutopilotMonitor.Agent.Core.Monitoring.Core;
@@ -22,85 +21,97 @@ namespace AutopilotMonitor.Agent
return;
}
- // Check if running in console mode (for testing)
- if (args.Contains("--console") || Environment.UserInteractive)
- {
- RunConsole(args);
- }
- else
- {
- RunService();
- }
+ // Always run directly - no ServiceBase.Run.
+ // The agent is started by the Scheduled Task (SYSTEM) or manually with --console.
+ // Both paths land here and run identically; --console just enables console output.
+ RunAgent(args);
}
- static void RunConsole(string[] args)
+ static void RunAgent(string[] args)
{
- Console.WriteLine("Autopilot Monitor Agent - Console Mode");
- Console.WriteLine("======================================");
- Console.WriteLine();
+ var consoleMode = args.Contains("--console") || Environment.UserInteractive;
+
+ if (consoleMode)
+ {
+ Console.WriteLine("Autopilot Monitor Agent");
+ Console.WriteLine("=======================");
+ Console.WriteLine();
+ }
try
{
var config = LoadConfiguration(args);
var logDir = Environment.ExpandEnvironmentVariables(config.LogDirectory);
- var logger = new AgentLogger(logDir, enableDebug: true);
+ var logger = new AgentLogger(logDir, enableDebug: config.EnableDebugLogging);
logger.Info("");
logger.Info("============================================================================================================");
- logger.Info("====================================== Agent starting in console mode ======================================");
+ logger.Info($"============================== Agent starting ({(consoleMode ? "console" : "background/SYSTEM")}) ==============================");
logger.Info("============================================================================================================");
- Console.WriteLine($"Session ID: {config.SessionId}");
- Console.WriteLine($"Tenant ID: {config.TenantId}");
- Console.WriteLine($"API URL: {config.ApiBaseUrl}");
- Console.WriteLine($"Log Directory: {logDir}");
- if (config.EnableSimulator)
+ if (consoleMode)
{
- Console.WriteLine($"Simulator Mode: ENABLED");
- Console.WriteLine($"Simulate Failure: {(config.SimulateFailure ? "YES" : "NO")}");
- if (!string.IsNullOrEmpty(config.SimulationLogDirectory))
+ Console.WriteLine($"Session ID: {config.SessionId}");
+ Console.WriteLine($"Tenant ID: {config.TenantId}");
+ Console.WriteLine($"API URL: {config.ApiBaseUrl}");
+ Console.WriteLine($"Log Dir: {logDir}");
+ Console.WriteLine($"Keep Logs: {config.KeepLogFile}");
+ if (config.EnableSimulator)
{
- Console.WriteLine($"Simulation Log Dir: {config.SimulationLogDirectory}");
- Console.WriteLine($"Simulation Speed: {config.SimulationSpeedFactor}x");
+ Console.WriteLine($"Simulator: ENABLED (Failure={config.SimulateFailure})");
+ if (!string.IsNullOrEmpty(config.SimulationLogDirectory))
+ Console.WriteLine($"Sim Log Dir: {config.SimulationLogDirectory} ({config.SimulationSpeedFactor}x)");
}
- }
-
- if (config.RebootOnComplete)
- {
- Console.WriteLine($"Reboot on Complete: ENABLED");
- }
-
- if (config.EnableGeoLocation)
- {
- Console.WriteLine($"GeoLocation Detection: ENABLED");
- }
-
- if (config.UseClientCertAuth)
- {
- Console.WriteLine($"Client Certificate Auth: ENABLED");
- if (!string.IsNullOrEmpty(config.ClientCertThumbprint))
+ if (config.RebootOnComplete) Console.WriteLine("Reboot: ON COMPLETE");
+ if (config.UseClientCertAuth)
{
- Console.WriteLine($"Certificate Thumbprint: {config.ClientCertThumbprint}");
+ Console.WriteLine($"Cert Auth: ENABLED");
+ if (!string.IsNullOrEmpty(config.ClientCertThumbprint))
+ Console.WriteLine($"Thumbprint: {config.ClientCertThumbprint}");
}
+ Console.WriteLine();
}
- Console.WriteLine();
-
using (var service = new MonitoringService(config, logger))
{
service.Start();
- Console.WriteLine("Agent is running. Press Enter to stop...");
- Console.ReadLine();
- service.Stop();
+
+ if (consoleMode)
+ {
+ Console.WriteLine("Agent is running. Press Enter to stop...");
+ Console.ReadLine();
+ service.Stop();
+ Console.WriteLine("Agent stopped.");
+ }
+ else
+ {
+ // Running as Scheduled Task under SYSTEM - block until the monitoring
+ // service signals completion via self-destruct / process exit.
+ service.WaitForCompletion();
+ }
}
logger.Info("Agent stopped");
- Console.WriteLine("Agent stopped.");
}
catch (Exception ex)
{
- Console.WriteLine($"ERROR: {ex.Message}");
- Console.WriteLine(ex.StackTrace);
+ // Last-resort: write to a crash file next to the log directory since we
+ // cannot use the Event Log (no traces policy).
+ try
+ {
+ var crashPath = Path.Combine(
+ Environment.ExpandEnvironmentVariables(@"%ProgramData%\AutopilotMonitor\Logs"),
+ $"crash_{DateTime.UtcNow:yyyyMMdd_HHmmss}.log");
+ Directory.CreateDirectory(Path.GetDirectoryName(crashPath));
+ File.WriteAllText(crashPath, $"[{DateTime.UtcNow:u}] FATAL: {ex}");
+ }
+ catch { /* nowhere left to log */ }
+
+ if (consoleMode)
+ {
+ Console.WriteLine($"ERROR: {ex.Message}");
+ Console.WriteLine(ex.StackTrace);
+ }
}
}
@@ -121,7 +132,7 @@ namespace AutopilotMonitor.Agent
var deviceConfigs = new[]
{
new { TenantId = "b54dc1af-5320-4f60-b5d4-821e0cf2a359", SimulateFailure = false, DeviceName = "Device-1" },
- new { TenantId = "b54dc1af-5320-4f60-b5d4-821e0cf2a359", SimulateFailure = true, DeviceName = "Device-2" }, // This one will fail
+ new { TenantId = "b54dc1af-5320-4f60-b5d4-821e0cf2a359", SimulateFailure = true, DeviceName = "Device-2" },
new { TenantId = "b54dc1af-5320-4f60-b5d4-821e0cf2a359", SimulateFailure = false, DeviceName = "Device-3" },
new { TenantId = "a53834b7-42bc-46a3-b004-369735c3acf9", SimulateFailure = false, DeviceName = "Device-4" },
new { TenantId = "deadbeef-dead-beef-dead-beefdeadbeef", SimulateFailure = false, DeviceName = "Device-5" }
@@ -130,7 +141,6 @@ namespace AutopilotMonitor.Agent
var tasks = new List();
var services = new List();
- // Start all 5 instances in parallel
for (int i = 0; i < deviceConfigs.Length; i++)
{
var deviceConfig = deviceConfigs[i];
@@ -150,15 +160,13 @@ namespace AutopilotMonitor.Agent
SimulateFailure = deviceConfig.SimulateFailure,
UseClientCertAuth = baseConfig.UseClientCertAuth,
ClientCertThumbprint = baseConfig.ClientCertThumbprint,
- CleanupOnExit = false, // Don't cleanup in mass rollout mode for easier inspection
- SelfDestructOnComplete = false, // Don't self-destruct in mass rollout mode
- RebootOnComplete = false, // Don't reboot in mass rollout mode
- EnableGeoLocation = false // Disable geo-location to reduce noise
+ CleanupOnExit = false,
+ SelfDestructOnComplete = false,
+ RebootOnComplete = false,
+ EnableGeoLocation = false
};
var logDir = Environment.ExpandEnvironmentVariables(config.LogDirectory);
-
- // Ensure directories exist
Directory.CreateDirectory(config.SpoolDirectory);
Directory.CreateDirectory(config.LogDirectory);
@@ -173,19 +181,13 @@ namespace AutopilotMonitor.Agent
var service = new MonitoringService(config, logger);
services.Add(service);
- // Start each service in a separate task
var task = System.Threading.Tasks.Task.Run(() =>
{
try
{
service.Start();
-
- // Keep the service running until enrollment completes
- // The simulator will automatically complete and emit enrollment_complete or enrollment_failed
while (true)
- {
System.Threading.Thread.Sleep(1000);
- }
}
catch (Exception ex)
{
@@ -201,36 +203,17 @@ namespace AutopilotMonitor.Agent
Console.WriteLine("Press Enter to stop all instances...");
Console.ReadLine();
- // Stop all services
Console.WriteLine();
Console.WriteLine("Stopping all instances...");
foreach (var service in services)
{
- try
- {
- service.Stop();
- service.Dispose();
- }
- catch (Exception ex)
- {
- Console.WriteLine($"Error stopping service: {ex.Message}");
- }
+ try { service.Stop(); service.Dispose(); }
+ catch (Exception ex) { Console.WriteLine($"Error stopping service: {ex.Message}"); }
}
Console.WriteLine("All instances stopped.");
}
- static void RunService()
- {
- var service = new AutopilotMonitorService();
- ServiceBase.Run(service);
- }
-
- ///
- /// Reads the Azure AD Tenant ID from the Windows Registry.
- /// Looks for enrollments with EnrollmentType = 6 (AAD Join).
- ///
- /// The AAD Tenant ID if found, otherwise null.
static string GetTenantIdFromRegistry()
{
try
@@ -240,113 +223,81 @@ namespace AutopilotMonitor.Agent
using (var enrollmentsKey = Registry.LocalMachine.OpenSubKey(enrollmentsKeyPath))
{
if (enrollmentsKey == null)
- {
- Console.WriteLine("Registry key not found: HKLM\\" + enrollmentsKeyPath);
return null;
- }
- // Iterate through all enrollment GUIDs
foreach (var enrollmentGuid in enrollmentsKey.GetSubKeyNames())
{
using (var enrollmentKey = enrollmentsKey.OpenSubKey(enrollmentGuid))
{
- if (enrollmentKey == null)
- continue;
+ if (enrollmentKey == null) continue;
- // Check if this is an AAD Join enrollment (EnrollmentType = 6)
var enrollmentType = enrollmentKey.GetValue("EnrollmentType");
if (enrollmentType != null && Convert.ToInt32(enrollmentType) == 6)
{
- // Try to get AADTenantID
var tenantId = enrollmentKey.GetValue("AADTenantID");
if (tenantId != null)
- {
- var tenantIdString = tenantId.ToString();
- Console.WriteLine($"Found AAD Tenant ID in registry: {tenantIdString} (Enrollment: {enrollmentGuid})");
- return tenantIdString;
- }
+ return tenantId.ToString();
}
}
}
-
- Console.WriteLine("No AAD Join enrollment (EnrollmentType=6) with AADTenantID found in registry.");
}
}
- catch (Exception ex)
- {
- Console.WriteLine($"Error reading Tenant ID from registry: {ex.Message}");
- }
+ catch { }
return null;
}
static AgentConfiguration LoadConfiguration(string[] args = null)
{
- // Parse command-line arguments
- var enableSimulator = args?.Contains("--simulator") ?? false;
- var simulateFailure = args?.Contains("--simulate-failure") ?? false;
- var noAuth = args?.Contains("--no-auth") ?? false;
- var noCleanup = args?.Contains("--no-cleanup") ?? false;
- var rebootOnComplete = args?.Contains("--reboot-on-complete") ?? false;
- var disableGeoLocation = args?.Contains("--disable-geolocation") ?? false;
- var newSession = args?.Contains("--new-session") ?? false;
+ var enableSimulator = args?.Contains("--simulator") ?? false;
+ var simulateFailure = args?.Contains("--simulate-failure") ?? false;
+ var noAuth = args?.Contains("--no-auth") ?? false;
+ var noCleanup = args?.Contains("--no-cleanup") ?? false;
+ var rebootOnComplete = args?.Contains("--reboot-on-complete") ?? false;
+ var disableGeoLoc = args?.Contains("--disable-geolocation") ?? false;
+ var newSession = args?.Contains("--new-session") ?? false;
+ var keepLogFile = args?.Contains("--keep-logfile") ?? false;
- // Parse certificate thumbprint if provided
- string certThumbprint = null;
- string tenantIdOverride = null;
- string imeLogPathOverride = null;
- string simulationLogDirectory = null;
+ string certThumbprint = null;
+ string tenantIdOverride = null;
+ string imeLogPathOverride = null;
+ string simulationLogDir = null;
double simulationSpeedFactor = 50;
+
if (args != null)
{
var thumbprintIndex = Array.IndexOf(args, "--cert-thumbprint");
if (thumbprintIndex >= 0 && thumbprintIndex + 1 < args.Length)
- {
certThumbprint = args[thumbprintIndex + 1];
- }
var tenantIdIndex = Array.IndexOf(args, "--tenant-id");
if (tenantIdIndex >= 0 && tenantIdIndex + 1 < args.Length)
- {
tenantIdOverride = args[tenantIdIndex + 1];
- }
var imeLogPathIndex = Array.IndexOf(args, "--ime-log-path");
if (imeLogPathIndex >= 0 && imeLogPathIndex + 1 < args.Length)
- {
imeLogPathOverride = args[imeLogPathIndex + 1];
- Console.WriteLine($"Using custom IME log path: {imeLogPathOverride}");
- }
var simLogDirIndex = Array.IndexOf(args, "--simulation-log-dir");
if (simLogDirIndex >= 0 && simLogDirIndex + 1 < args.Length)
- {
- simulationLogDirectory = args[simLogDirIndex + 1];
- Console.WriteLine($"Using simulation log directory: {simulationLogDirectory}");
- }
+ simulationLogDir = args[simLogDirIndex + 1];
var simSpeedIndex = Array.IndexOf(args, "--simulation-speed-factor");
if (simSpeedIndex >= 0 && simSpeedIndex + 1 < args.Length)
- {
if (double.TryParse(args[simSpeedIndex + 1], out var speed))
- {
simulationSpeedFactor = speed;
- Console.WriteLine($"Using simulation speed factor: {simulationSpeedFactor}x");
- }
- }
}
- // Defaults (fallback values if no config file exists)
- //string apiBaseUrl = "http://localhost:7071";
- string apiBaseUrl = "https://autopilotmonitor-func.azurewebsites.net";
- int uploadIntervalSeconds = 30;
- bool cleanupOnExit = true;
- bool selfDestructOnComplete = true;
- bool rebootOnCompleteConfig = false;
- bool enableGeoLocationConfig = true;
- bool useClientCertAuthConfig = true;
+ // Defaults
+ string apiBaseUrl = "https://autopilotmonitor-func.azurewebsites.net";
+ int uploadIntervalSeconds = 30;
+ bool cleanupOnExit = true;
+ bool selfDestructOnComplete = true;
+ bool rebootOnCompleteConfig = false;
+ bool enableGeoLocationConfig = true;
+ bool useClientCertAuthConfig = true;
- // Try to load configuration from JSON file (written by PowerShell bootstrap script)
+ // Load configuration JSON written by PowerShell bootstrap script
var configFilePath = Environment.ExpandEnvironmentVariables(@"%ProgramData%\AutopilotMonitor\Config\agent-config.json");
if (File.Exists(configFilePath))
{
@@ -378,91 +329,65 @@ namespace AutopilotMonitor.Agent
if (configDict.ContainsKey("useClientCertAuth") && configDict["useClientCertAuth"] != null)
useClientCertAuthConfig = Convert.ToBoolean(configDict["useClientCertAuth"]);
- Console.WriteLine($"Loaded configuration from {configFilePath}");
+ if (configDict.ContainsKey("keepLogFile") && configDict["keepLogFile"] != null)
+ keepLogFile = keepLogFile || Convert.ToBoolean(configDict["keepLogFile"]);
}
}
- catch (Exception ex)
- {
- Console.WriteLine($"Warning: Failed to load configuration file: {ex.Message}");
- Console.WriteLine("Using default configuration");
- }
- }
- else
- {
- Console.WriteLine($"Configuration file not found at {configFilePath}");
- Console.WriteLine("Using default configuration");
+ catch { /* use defaults */ }
}
// Environment variable overrides config file
apiBaseUrl = Environment.GetEnvironmentVariable("AUTOPILOT_MONITOR_API") ?? apiBaseUrl;
- // Command-line arguments override everything (for --no-cleanup)
if (noCleanup)
{
cleanupOnExit = false;
selfDestructOnComplete = false;
}
- // Determine TenantId: Command-line arg > Registry > Fallback
+ // Determine TenantId: command-line arg > registry > fallback
string tenantId;
if (!string.IsNullOrEmpty(tenantIdOverride))
{
- // Command-line argument takes precedence
tenantId = tenantIdOverride;
- Console.WriteLine($"Using Tenant ID from command-line argument: {tenantId}");
}
else
{
- // Try to read from registry
tenantId = GetTenantIdFromRegistry();
-
if (string.IsNullOrEmpty(tenantId))
- {
- // Fallback to default (for testing/development)
- tenantId = "b54dc1af-5320-4f60-b5d4-821e0cf2a359";
- Console.WriteLine($"Using fallback Tenant ID: {tenantId}");
- }
+ tenantId = "b54dc1af-5320-4f60-b5d4-821e0cf2a359"; // fallback for dev/testing
}
- // Load or create persisted session ID
- var dataDirectory = Environment.ExpandEnvironmentVariables(@"%ProgramData%\AutopilotMonitor");
- var sessionPersistence = new SessionPersistence(dataDirectory);
+ var dataDirectory = Environment.ExpandEnvironmentVariables(@"%ProgramData%\AutopilotMonitor");
+ var sessionPersist = new SessionPersistence(dataDirectory);
- // Force new session if --new-session flag is set
if (newSession)
- {
- Console.WriteLine("--new-session flag detected, deleting existing session...");
- sessionPersistence.DeleteSession();
- }
+ sessionPersist.DeleteSession();
- var sessionId = sessionPersistence.LoadOrCreateSessionId();
-
- var sessionStatus = sessionPersistence.SessionExists() && File.GetCreationTime(Path.Combine(dataDirectory, "session.id")) < DateTime.Now.AddMinutes(-1)
- ? "Restored from previous session"
- : "New session created";
- Console.WriteLine($"Session ID: {sessionId} ({sessionStatus})");
+ var sessionId = sessionPersist.LoadOrCreateSessionId();
return new AgentConfiguration
{
- ApiBaseUrl = apiBaseUrl,
- SessionId = sessionId,
- TenantId = tenantId,
- SpoolDirectory = Environment.ExpandEnvironmentVariables(@"%ProgramData%\AutopilotMonitor\Spool"),
- LogDirectory = Environment.ExpandEnvironmentVariables(@"%ProgramData%\AutopilotMonitor\Logs"),
+ ApiBaseUrl = apiBaseUrl,
+ SessionId = sessionId,
+ TenantId = tenantId,
+ SpoolDirectory = Environment.ExpandEnvironmentVariables(@"%ProgramData%\AutopilotMonitor\Spool"),
+ LogDirectory = Environment.ExpandEnvironmentVariables(@"%ProgramData%\AutopilotMonitor\Logs"),
UploadIntervalSeconds = uploadIntervalSeconds,
- MaxBatchSize = 100,
- EnableDebugLogging = true,
- EnableSimulator = enableSimulator,
- SimulateFailure = simulateFailure,
- UseClientCertAuth = !noAuth && useClientCertAuthConfig,
- ClientCertThumbprint = certThumbprint,
- CleanupOnExit = cleanupOnExit,
+ MaxBatchSize = 100,
+ EnableDebugLogging = true,
+ EnableSimulator = enableSimulator,
+ SimulateFailure = simulateFailure,
+ UseClientCertAuth = !noAuth && useClientCertAuthConfig,
+ ClientCertThumbprint = certThumbprint,
+ CleanupOnExit = cleanupOnExit,
SelfDestructOnComplete = selfDestructOnComplete,
- RebootOnComplete = rebootOnComplete || rebootOnCompleteConfig,
- EnableGeoLocation = !disableGeoLocation && enableGeoLocationConfig,
- ImeLogPathOverride = imeLogPathOverride,
- SimulationLogDirectory = simulationLogDirectory,
- SimulationSpeedFactor = simulationSpeedFactor
+ RebootOnComplete = rebootOnComplete || rebootOnCompleteConfig,
+ EnableGeoLocation = !disableGeoLoc && enableGeoLocationConfig,
+ ImeLogPathOverride = imeLogPathOverride,
+ SimulationLogDirectory = simulationLogDir,
+ SimulationSpeedFactor = simulationSpeedFactor,
+ KeepLogFile = keepLogFile
};
}
}