diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Security/TenantIdResolver.cs b/src/Agent/AutopilotMonitor.Agent.Core/Security/TenantIdResolver.cs
index 36c5551a..c9d92651 100644
--- a/src/Agent/AutopilotMonitor.Agent.Core/Security/TenantIdResolver.cs
+++ b/src/Agent/AutopilotMonitor.Agent.Core/Security/TenantIdResolver.cs
@@ -7,7 +7,15 @@ namespace AutopilotMonitor.Agent.Core.Security
{
///
/// Resolves the device's AAD TenantId from one of several Windows registry signals.
- /// V1 backport of the V2 resolver — same probe order, same diagnostic logging.
+ ///
+ /// All registry reads go through the explicit
+ /// view, never . Reason: SDK-style net48 EXEs
+ /// may resolve AnyCPU to 32-bit at runtime depending on MSBuild defaults, in which
+ /// case HKLM\SOFTWARE\Microsoft\Enrollments silently redirects to the
+ /// WOW6432Node mirror, a different (and on most devices stale/empty) hive
+ /// that never carries the active MDM enrollment. Forcing Registry64 makes the
+ /// resolver bitness-independent regardless of how AnyCPU resolves at runtime.
+ ///
///
/// Probe order, first non-empty hit wins:
///
@@ -29,9 +37,9 @@ namespace AutopilotMonitor.Agent.Core.Security
///
///
/// Returns null when no source carries a TenantId. Never throws. On miss the
- /// resolver dumps a diagnostic summary of which keys/values it saw so the bootstrap
- /// log carries enough evidence to classify "device truly not enrolled" vs. "agent
- /// fired before enrollment finished writing".
+ /// resolver dumps a compact diagnostic summary so the agent log carries enough
+ /// evidence to classify "device truly not enrolled" vs. "agent fired before
+ /// enrollment finished writing".
///
///
public static class TenantIdResolver
@@ -79,11 +87,15 @@ namespace AutopilotMonitor.Agent.Core.Security
return null;
}
+ private static RegistryKey OpenLocalMachine64() =>
+ RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
+
private static string TryEnrollmentsRegistry(ResolverDiagnostics diag)
{
try
{
- using (var enrollmentsKey = Registry.LocalMachine.OpenSubKey(EnrollmentsKeyPath))
+ using (var hklm64 = OpenLocalMachine64())
+ using (var enrollmentsKey = hklm64.OpenSubKey(EnrollmentsKeyPath))
{
if (enrollmentsKey == null)
{
@@ -91,9 +103,9 @@ namespace AutopilotMonitor.Agent.Core.Security
return null;
}
- foreach (var enrollmentGuid in enrollmentsKey.GetSubKeyNames())
+ foreach (var subKeyName in enrollmentsKey.GetSubKeyNames())
{
- using (var enrollmentKey = enrollmentsKey.OpenSubKey(enrollmentGuid))
+ using (var enrollmentKey = enrollmentsKey.OpenSubKey(subKeyName))
{
if (enrollmentKey == null) continue;
@@ -105,19 +117,34 @@ namespace AutopilotMonitor.Agent.Core.Security
catch { /* unparseable — record as null */ }
}
- var aadTenantId = enrollmentKey.GetValue("AADTenantID")?.ToString();
- var enrollmentState = enrollmentKey.GetValue("EnrollmentState")?.ToString();
-
- diag.EnrollmentsSeen.Add(new EnrollmentSnapshot
+ // Sub-keys without EnrollmentType are well-known structural folders
+ // (Context, Ownership, Status, ValidNodePaths). Skip them silently —
+ // they never carry AADTenantID and only add log noise on miss.
+ if (typeValue == null)
{
- Guid = enrollmentGuid,
- EnrollmentType = typeValue,
- EnrollmentState = enrollmentState,
- HasAadTenantId = !string.IsNullOrEmpty(aadTenantId),
- });
+ diag.EnrollmentsStructuralCount++;
+ continue;
+ }
- if (typeValue == IntuneMdmEnrollmentType && !string.IsNullOrEmpty(aadTenantId))
- return aadTenantId;
+ var aadTenantId = enrollmentKey.GetValue("AADTenantID")?.ToString();
+
+ if (typeValue == IntuneMdmEnrollmentType)
+ {
+ var enrollmentState = enrollmentKey.GetValue("EnrollmentState")?.ToString();
+ diag.IntuneMdmEnrollments.Add(new IntuneMdmSnapshot
+ {
+ Guid = subKeyName,
+ EnrollmentState = enrollmentState,
+ HasAadTenantId = !string.IsNullOrEmpty(aadTenantId),
+ });
+
+ if (!string.IsNullOrEmpty(aadTenantId))
+ return aadTenantId;
+ }
+ else
+ {
+ diag.OtherEnrollmentTypeCount++;
+ }
}
}
}
@@ -134,7 +161,8 @@ namespace AutopilotMonitor.Agent.Core.Security
{
try
{
- using (var tenantInfoKey = Registry.LocalMachine.OpenSubKey(CloudDomainJoinTenantInfoPath))
+ using (var hklm64 = OpenLocalMachine64())
+ using (var tenantInfoKey = hklm64.OpenSubKey(CloudDomainJoinTenantInfoPath))
{
if (tenantInfoKey == null)
{
@@ -164,7 +192,8 @@ namespace AutopilotMonitor.Agent.Core.Security
{
try
{
- using (var joinInfoKey = Registry.LocalMachine.OpenSubKey(CloudDomainJoinJoinInfoPath))
+ using (var hklm64 = OpenLocalMachine64())
+ using (var joinInfoKey = hklm64.OpenSubKey(CloudDomainJoinJoinInfoPath))
{
if (joinInfoKey == null)
{
@@ -207,20 +236,33 @@ namespace AutopilotMonitor.Agent.Core.Security
// Enrollments root.
if (diag.EnrollmentsProbeError != null)
+ {
logger.Warning($"TenantIdResolver: Enrollments probe failed: {diag.EnrollmentsProbeError}");
+ }
else if (diag.EnrollmentsRootMissing)
- logger.Warning($"TenantIdResolver: HKLM\\{EnrollmentsKeyPath} not found.");
- else if (diag.EnrollmentsSeen.Count == 0)
- logger.Warning($"TenantIdResolver: HKLM\\{EnrollmentsKeyPath} present but empty.");
+ {
+ logger.Warning($"TenantIdResolver: HKLM\\{EnrollmentsKeyPath} not found (Registry64 view).");
+ }
else
{
- logger.Warning($"TenantIdResolver: HKLM\\{EnrollmentsKeyPath} carries {diag.EnrollmentsSeen.Count} enrollment(s) but none was Intune MDM (Type=6) with AADTenantID:");
- foreach (var e in diag.EnrollmentsSeen)
+ logger.Warning(
+ $"TenantIdResolver: HKLM\\{EnrollmentsKeyPath} (Registry64) — " +
+ $"Intune-MDM (Type=6): {diag.IntuneMdmEnrollments.Count}, " +
+ $"other enrollment types: {diag.OtherEnrollmentTypeCount}, " +
+ $"structural sub-keys: {diag.EnrollmentsStructuralCount}.");
+
+ if (diag.IntuneMdmEnrollments.Count == 0)
{
- logger.Warning(
- $" - {e.Guid}: EnrollmentType={(e.EnrollmentType?.ToString() ?? "")}, " +
- $"EnrollmentState={e.EnrollmentState ?? ""}, " +
- $"AADTenantID={(e.HasAadTenantId ? "" : "")}");
+ logger.Warning("TenantIdResolver: no Type=6 enrollment present yet — MDM enrollment hasn't reached registry write phase.");
+ }
+ else
+ {
+ foreach (var e in diag.IntuneMdmEnrollments)
+ {
+ logger.Warning(
+ $" - Type=6 {e.Guid}: EnrollmentState={e.EnrollmentState ?? ""}, " +
+ $"AADTenantID={(e.HasAadTenantId ? "" : "")}");
+ }
}
}
@@ -253,7 +295,9 @@ namespace AutopilotMonitor.Agent.Core.Security
{
public bool EnrollmentsRootMissing;
public string EnrollmentsProbeError;
- public readonly List EnrollmentsSeen = new List();
+ public readonly List IntuneMdmEnrollments = new List();
+ public int OtherEnrollmentTypeCount;
+ public int EnrollmentsStructuralCount;
public bool TenantInfoRootMissing;
public string TenantInfoProbeError;
@@ -264,10 +308,9 @@ namespace AutopilotMonitor.Agent.Core.Security
public readonly List JoinInfoEntries = new List();
}
- private struct EnrollmentSnapshot
+ private struct IntuneMdmSnapshot
{
public string Guid;
- public int? EnrollmentType;
public string EnrollmentState;
public bool HasAadTenantId;
}
diff --git a/src/Agent/AutopilotMonitor.Agent.V2.Core/Security/TenantIdResolver.cs b/src/Agent/AutopilotMonitor.Agent.V2.Core/Security/TenantIdResolver.cs
index 6741eee7..6c41afee 100644
--- a/src/Agent/AutopilotMonitor.Agent.V2.Core/Security/TenantIdResolver.cs
+++ b/src/Agent/AutopilotMonitor.Agent.V2.Core/Security/TenantIdResolver.cs
@@ -7,7 +7,15 @@ namespace AutopilotMonitor.Agent.V2.Core.Security
{
///
/// Resolves the device's AAD TenantId from one of several Windows registry signals.
- /// Plan §4.x M4.5.b.
+ ///
+ /// All registry reads go through the explicit
+ /// view, never . Reason: SDK-style net48 EXEs
+ /// may resolve AnyCPU to 32-bit at runtime depending on MSBuild defaults, in which
+ /// case HKLM\SOFTWARE\Microsoft\Enrollments silently redirects to the
+ /// WOW6432Node mirror, a different (and on most devices stale/empty) hive
+ /// that never carries the active MDM enrollment. Forcing Registry64 makes the
+ /// resolver bitness-independent regardless of how AnyCPU resolves at runtime.
+ ///
///
/// Probe order, first non-empty hit wins:
///
@@ -29,9 +37,9 @@ namespace AutopilotMonitor.Agent.V2.Core.Security
///
///
/// Returns null when no source carries a TenantId. Never throws. On miss the
- /// resolver dumps a diagnostic summary of which keys/values it saw so the bootstrap
- /// log carries enough evidence to classify "device truly not enrolled" vs. "agent
- /// fired before enrollment finished writing".
+ /// resolver dumps a compact diagnostic summary so the agent log carries enough
+ /// evidence to classify "device truly not enrolled" vs. "agent fired before
+ /// enrollment finished writing".
///
///
public static class TenantIdResolver
@@ -79,11 +87,15 @@ namespace AutopilotMonitor.Agent.V2.Core.Security
return null;
}
+ private static RegistryKey OpenLocalMachine64() =>
+ RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
+
private static string TryEnrollmentsRegistry(ResolverDiagnostics diag)
{
try
{
- using (var enrollmentsKey = Registry.LocalMachine.OpenSubKey(EnrollmentsKeyPath))
+ using (var hklm64 = OpenLocalMachine64())
+ using (var enrollmentsKey = hklm64.OpenSubKey(EnrollmentsKeyPath))
{
if (enrollmentsKey == null)
{
@@ -91,9 +103,9 @@ namespace AutopilotMonitor.Agent.V2.Core.Security
return null;
}
- foreach (var enrollmentGuid in enrollmentsKey.GetSubKeyNames())
+ foreach (var subKeyName in enrollmentsKey.GetSubKeyNames())
{
- using (var enrollmentKey = enrollmentsKey.OpenSubKey(enrollmentGuid))
+ using (var enrollmentKey = enrollmentsKey.OpenSubKey(subKeyName))
{
if (enrollmentKey == null) continue;
@@ -105,19 +117,34 @@ namespace AutopilotMonitor.Agent.V2.Core.Security
catch { /* unparseable — record as null */ }
}
- var aadTenantId = enrollmentKey.GetValue("AADTenantID")?.ToString();
- var enrollmentState = enrollmentKey.GetValue("EnrollmentState")?.ToString();
-
- diag.EnrollmentsSeen.Add(new EnrollmentSnapshot
+ // Sub-keys without EnrollmentType are well-known structural folders
+ // (Context, Ownership, Status, ValidNodePaths). Skip them silently —
+ // they never carry AADTenantID and only add log noise on miss.
+ if (typeValue == null)
{
- Guid = enrollmentGuid,
- EnrollmentType = typeValue,
- EnrollmentState = enrollmentState,
- HasAadTenantId = !string.IsNullOrEmpty(aadTenantId),
- });
+ diag.EnrollmentsStructuralCount++;
+ continue;
+ }
- if (typeValue == IntuneMdmEnrollmentType && !string.IsNullOrEmpty(aadTenantId))
- return aadTenantId;
+ var aadTenantId = enrollmentKey.GetValue("AADTenantID")?.ToString();
+
+ if (typeValue == IntuneMdmEnrollmentType)
+ {
+ var enrollmentState = enrollmentKey.GetValue("EnrollmentState")?.ToString();
+ diag.IntuneMdmEnrollments.Add(new IntuneMdmSnapshot
+ {
+ Guid = subKeyName,
+ EnrollmentState = enrollmentState,
+ HasAadTenantId = !string.IsNullOrEmpty(aadTenantId),
+ });
+
+ if (!string.IsNullOrEmpty(aadTenantId))
+ return aadTenantId;
+ }
+ else
+ {
+ diag.OtherEnrollmentTypeCount++;
+ }
}
}
}
@@ -134,7 +161,8 @@ namespace AutopilotMonitor.Agent.V2.Core.Security
{
try
{
- using (var tenantInfoKey = Registry.LocalMachine.OpenSubKey(CloudDomainJoinTenantInfoPath))
+ using (var hklm64 = OpenLocalMachine64())
+ using (var tenantInfoKey = hklm64.OpenSubKey(CloudDomainJoinTenantInfoPath))
{
if (tenantInfoKey == null)
{
@@ -164,7 +192,8 @@ namespace AutopilotMonitor.Agent.V2.Core.Security
{
try
{
- using (var joinInfoKey = Registry.LocalMachine.OpenSubKey(CloudDomainJoinJoinInfoPath))
+ using (var hklm64 = OpenLocalMachine64())
+ using (var joinInfoKey = hklm64.OpenSubKey(CloudDomainJoinJoinInfoPath))
{
if (joinInfoKey == null)
{
@@ -207,20 +236,33 @@ namespace AutopilotMonitor.Agent.V2.Core.Security
// Enrollments root.
if (diag.EnrollmentsProbeError != null)
+ {
logger.Warning($"TenantIdResolver: Enrollments probe failed: {diag.EnrollmentsProbeError}");
+ }
else if (diag.EnrollmentsRootMissing)
- logger.Warning($"TenantIdResolver: HKLM\\{EnrollmentsKeyPath} not found.");
- else if (diag.EnrollmentsSeen.Count == 0)
- logger.Warning($"TenantIdResolver: HKLM\\{EnrollmentsKeyPath} present but empty.");
+ {
+ logger.Warning($"TenantIdResolver: HKLM\\{EnrollmentsKeyPath} not found (Registry64 view).");
+ }
else
{
- logger.Warning($"TenantIdResolver: HKLM\\{EnrollmentsKeyPath} carries {diag.EnrollmentsSeen.Count} enrollment(s) but none was Intune MDM (Type=6) with AADTenantID:");
- foreach (var e in diag.EnrollmentsSeen)
+ logger.Warning(
+ $"TenantIdResolver: HKLM\\{EnrollmentsKeyPath} (Registry64) — " +
+ $"Intune-MDM (Type=6): {diag.IntuneMdmEnrollments.Count}, " +
+ $"other enrollment types: {diag.OtherEnrollmentTypeCount}, " +
+ $"structural sub-keys: {diag.EnrollmentsStructuralCount}.");
+
+ if (diag.IntuneMdmEnrollments.Count == 0)
{
- logger.Warning(
- $" - {e.Guid}: EnrollmentType={(e.EnrollmentType?.ToString() ?? "")}, " +
- $"EnrollmentState={e.EnrollmentState ?? ""}, " +
- $"AADTenantID={(e.HasAadTenantId ? "" : "")}");
+ logger.Warning("TenantIdResolver: no Type=6 enrollment present yet — MDM enrollment hasn't reached registry write phase.");
+ }
+ else
+ {
+ foreach (var e in diag.IntuneMdmEnrollments)
+ {
+ logger.Warning(
+ $" - Type=6 {e.Guid}: EnrollmentState={e.EnrollmentState ?? ""}, " +
+ $"AADTenantID={(e.HasAadTenantId ? "" : "")}");
+ }
}
}
@@ -253,7 +295,9 @@ namespace AutopilotMonitor.Agent.V2.Core.Security
{
public bool EnrollmentsRootMissing;
public string EnrollmentsProbeError;
- public readonly List EnrollmentsSeen = new List();
+ public readonly List IntuneMdmEnrollments = new List();
+ public int OtherEnrollmentTypeCount;
+ public int EnrollmentsStructuralCount;
public bool TenantInfoRootMissing;
public string TenantInfoProbeError;
@@ -264,10 +308,9 @@ namespace AutopilotMonitor.Agent.V2.Core.Security
public readonly List JoinInfoEntries = new List();
}
- private struct EnrollmentSnapshot
+ private struct IntuneMdmSnapshot
{
public string Guid;
- public int? EnrollmentType;
public string EnrollmentState;
public bool HasAadTenantId;
}
diff --git a/src/Agent/AutopilotMonitor.Agent.V2/buildcounter.txt b/src/Agent/AutopilotMonitor.Agent.V2/buildcounter.txt
index 80e3e6ea..055b6671 100644
--- a/src/Agent/AutopilotMonitor.Agent.V2/buildcounter.txt
+++ b/src/Agent/AutopilotMonitor.Agent.V2/buildcounter.txt
@@ -1 +1 @@
-506
+507
diff --git a/src/Agent/AutopilotMonitor.Agent/buildcounter.txt b/src/Agent/AutopilotMonitor.Agent/buildcounter.txt
index fe1b0608..53d108b9 100644
--- a/src/Agent/AutopilotMonitor.Agent/buildcounter.txt
+++ b/src/Agent/AutopilotMonitor.Agent/buildcounter.txt
@@ -1 +1 @@
-1228
+1231