The diagnostics package's built-in collection is now ONE catalog in Shared (DiagnosticsBuiltInSections.All): the agent iterates it when building the ZIP and the backend serves the same list to the portal (GET /api/diagnostics/paths, MemberRead), so what administrators see is exactly what the agent collects. Agent - New conditional sections: RealmJoin client/package/Chocolatey/per-user logs behind the tenant's RealmJoin Watcher toggle (mirrored onto AgentConfiguration.EnableRealmJoinWatcher by RemoteConfigMerger) and the Device Preparation bootstrapper event log behind the deterministic WDP marker (probe once per build, fail toward Classic). ZIP layout mirrors the disk (RealmJoinLogs/Windows|Choco|User). Every skip is a package-manifest line. - Built-ins bypass DiagnosticsPathGuards by design (reviewed code); caps, reparse-point skip and evtx export still apply. No ConfigVersion bump. Backend - GET diagnostics/paths (MemberRead, JWT-scoped) with the catalog + global paths. - MergeDiagnosticsLogPaths: global ∪ tenant dedup (trimmed, case-insensitive) — duplicates used to produce duplicate ZIP entry names. Web - Collapsed read-only built-in list (RealmJoin on/off in the tenant header, neutral in the Global-Admin view), one-line DiagnosticsPathRow for every entry, add-row above the lists; tenant context no longer fetches /api/global/config. Tests: agent RealmJoin on/off, no user session, bootstrapper evtx, probe failure, merger mirror; backend catalog invariants, payload shape, dedup, policy; web builtInSectionDisplay. Docs: docs/agent/diagnostics-package.md (+index/log). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9.7 KiB
type, title, description, resource, tags, timestamp
| type | title | description | resource | tags | timestamp | |||||
|---|---|---|---|---|---|---|---|---|---|---|
| concept | Diagnostics Package | What the agent's diagnostics ZIP contains and why — the built-in section catalog shared with the portal, the RealmJoin and Device Preparation collection gates, the configured-path guards, caps, and the package manifest. | agent |
|
2026-08-23 |
Schema
The diagnostics package is the agent's last-resort support artifact: a ZIP built in memory
by DiagnosticsPackageService.BuildArchiveBytes and uploaded through a short-lived SAS
URL at the end of a session, at the WhiteGlove seal (Always mode) or on a server-requested
on-demand collection (server actions).
Archive layout
sessioninfo.txt session/tenant/device/outcome, hardware via WMI
<built-in sections> catalog order, see below
AdditionalLogs/<last folder>/ admin-configured paths (global ∪ tenant)
package-manifest.txt every packaging decision (always written)
_TRUNCATED.txt only when a cap skipped something
The built-in catalog is the single source of truth
Everything the package collects before any configured path is data, not code paths:
DiagnosticsBuiltInSections.All in Shared (Models/Diagnostics/DiagnosticsBuiltInSections.cs).
The agent iterates it in order; the backend serves the same list to the portal
(GET /api/diagnostics/paths, MemberRead) so what administrators see is exactly what the
agent does. There is no second copy to drift.
| Id | ZIP folder | Source (unexpanded) | Patterns | Recursive | Condition |
|---|---|---|---|---|---|
| AgentLogs | AgentLogs |
%ProgramData%\AutopilotMonitor\Logs |
log patterns | no | Always |
| ImeLogs | ImeLogs |
%ProgramData%\Microsoft\IntuneManagementExtension\Logs |
log patterns | no | Always |
| ImeBootstrapperEventLog | ImeLogs |
C:\Windows\System32\winevt\Logs |
BootstrapperAgentServiceLogProvider.evtx |
no | DevicePreparation |
| AgentState | AgentState |
%ProgramData%\AutopilotMonitor\State |
log patterns + *.complete, *.marker |
yes | Always |
| AgentSpool | AgentSpool |
%ProgramData%\AutopilotMonitor\Spool |
*.jsonl, *.json |
no | Always |
| AgentMarkers | AgentMarkers |
%ProgramData%\AutopilotMonitor |
*.complete, *.marker |
no | Always |
| RealmJoinWindows | RealmJoinLogs/Windows |
C:\Windows\Logs |
realmjoin*.log |
no | RealmJoinWatcher |
| RealmJoinPackages | RealmJoinLogs/Windows/RealmJoin |
C:\Windows\Logs\RealmJoin |
*.log |
yes | RealmJoinWatcher |
| RealmJoinChoco | RealmJoinLogs/Choco |
%ProgramData%\RealmJoin\choco\logs |
*.log |
yes | RealmJoinWatcher |
| RealmJoinUserTray | RealmJoinLogs/User |
%LOGGED_ON_USER_PROFILE%\AppData\Local\RealmJoin |
tray*.log |
no | RealmJoinWatcher |
| RealmJoinUserLogs | RealmJoinLogs/User/Logs |
%LOGGED_ON_USER_PROFILE%\AppData\Local\RealmJoin\Logs |
*.log |
yes | RealmJoinWatcher |
"Log patterns" = *.log *.txt *.json *.jsonl *.etl *.evtx *.xml *.csv *.cab. The five
Always sections are the historical layout (PR1-B forensics) and are lock-tested in order;
the RealmJoin ZIP layout mirrors the disk (C:\Windows\Logs\{realmjoin*.log, RealmJoin\…} →
RealmJoinLogs/Windows/…, %LOCALAPPDATA%\RealmJoin\{tray*.log, Logs\…} →
RealmJoinLogs/User/…), so a flat section and a tree section never produce the same entry
name. Two sections may share a ZIP folder only when they read different source folders
(ImeLogs + the bootstrapper evtx) — (ZipFolder, SourceFolder) pairs are unique.
Conditions
DiagnosticsSectionCondition is evaluated by the agent (IsSectionActive, pure) against its
effective configuration and the enrollment scenario; unknown values fail closed.
- RealmJoinWatcher — the tenant's RealmJoin Watcher toggle
(
AnalyzerConfiguration.EnableRealmJoinWatcher). The package service only seesAgentConfiguration, soRemoteConfigMergermirrors that one Analyzers knob ontoAgentConfiguration.EnableRealmJoinWatcher(the RealmJoin host keeps reading the remote block). Deliberately NOT additionally gated on the compile-timeRealmJoinTrackingEnabledkill switch: if the live watcher is ever disabled, the logs become more useful, not less. No ConfigVersion bump — no new wire field. - DevicePreparation —
EnrollmentRegistryDetector.IsDeterministicDevicePreparation(), the same deterministicDevicePreparation\BootstrapperAgent\ExecutionContextmarker every other WDP gate keys on (WDP scenario gates); CloudAssigned* fallback rules count as Classic. The probe is a registry read, evaluated once per build (SCENARIO: devicePreparation=… realmJoinWatcher=…in the manifest); a probe failure is treated as Classic and logged — the evtx export is skipped, never the package. The channel is exclusively locked while active, so the existingwevtutil eplexport path (ResolveChannelForEvtxFile, field case 2026-08-17) handles it.
Inactive sections are never silent: BUILT-IN SKIPPED (RealmJoin Watcher disabled): <Id>,
BUILT-IN SKIPPED (not a Device Preparation enrollment): <Id>,
BUILT-IN SKIPPED (no user session for token): <Id> '<source>' (the
%LOGGED_ON_USER_PROFILE% token resolves via UserProfileResolver only once an
interactive user exists — identical to configured paths). Active ones log
BUILT-IN: <Id> -> folder='…' zip='…' recursive=… followed by the per-pattern
ADDED / NO MATCH / FOLDER MISSING lines.
Built-ins bypass the configured-path guards — on purpose
DiagnosticsPathGuards (gather-rule guardrails)
validates admin-typed paths: allowlisted prefixes, wildcard only in the last segment,
C:\Users blocked except the token's AppData\Local|Roaming. The catalog is reviewed code,
exactly like the five sections that never went through the guard before — and
%ProgramData%\RealmJoin is not on the allowlist at all. What does apply to every section:
the reparse-point skip (files and directories), the .evtx channel export, and the caps
(100 MB per file, 500 MB total, 5000 files) tracked globally across sections by
BudgetTracker, with every skip recorded in the manifest and _TRUNCATED.txt.
Configured paths: global ∪ tenant
GetAgentConfigFunction.MergeDiagnosticsLogPaths sends global paths (Global Admin,
AdminConfiguration.DiagnosticsGlobalLogPathsJson) first, then tenant paths
(TenantConfiguration.DiagnosticsLogPathsJson), dropping blank entries and collapsing
duplicates (trimmed, case-insensitive) onto the first occurrence — a path present in both
lists used to produce duplicate ZIP entry names. The agent keys each entry's ZIP folder on
the last directory name (AdditionalLogs/<folder>), so two configured paths ending in a
same-named folder share one ZIP folder.
Portal surfaces
- Global Admin → Settings → Diagnostics Log Paths: the built-in catalog (collapsed, read-only, neutral condition pills) above the editable global list; add-row first.
- Tenant → Settings → Agent → Diagnostics Package: built-in catalog (header carries the
tenant's persisted RealmJoin Watcher state, RealmJoin rows show on/off), the global paths
read-only, then the tenant's own entries; every role with a tenant membership can read the
catalog (the route is MemberRead, JWT-scoped). One line per entry (
DiagnosticsPathRow), full values in tooltips.
Test seams
DiagnosticsPackageServiceinternal ctor: the five historical folder overrides map onto catalog ids;sectionFolderOverridescovers any other section;devicePreparationProbereplaces the registry read (seam callers default to "not WDP", so existing rigs never touch the registry).UserProfileResolver.SetForTesting(path|null)/Reset()drive the token.- Catalog invariants live in the backend test project (
DiagnosticsBuiltInSectionsTests), because Shared has no test project.
Examples
A RealmJoin tenant (watcher on) with a signed-in user: RealmJoinLogs/Windows/realmjoin.log,
RealmJoinLogs/Windows/RealmJoin/Packages/<pkg>/<timestamp>_install.log,
RealmJoinLogs/Choco/<pkg>/<timestamp>_install.log, RealmJoinLogs/User/tray.log,
RealmJoinLogs/User/Logs/RjImeHost.log. The same tenant's package collected before the
desktop: the two User sections appear as BUILT-IN SKIPPED (no user session for token).
A Classic enrollment: BUILT-IN SKIPPED (not a Device Preparation enrollment): ImeBootstrapperEventLog. A tenant that had added
%LOGGED_ON_USER_PROFILE%\AppData\Local\RealmJoin\Logs\*.log manually keeps working — the
files simply appear twice (AdditionalLogs/Logs/… and RealmJoinLogs/User/Logs/…) until the
entry is removed.
Citations
src/Shared/AutopilotMonitor.Shared/Models/Diagnostics/DiagnosticsBuiltInSections.cs— the catalogsrc/Agent/AutopilotMonitor.Agent.V2.Core/Monitoring/Runtime/DiagnosticsPackageService.cs—BuildArchiveBytes,AddBuiltInSection,IsSectionActive, caps, evtx exportsrc/Agent/AutopilotMonitor.Agent.V2.Core/Configuration/RemoteConfigMerger.cs—EnableRealmJoinWatchermirrorsrc/Backend/AutopilotMonitor.Functions/Functions/Diagnostics/GetDiagnosticsPathsFunction.cs—GET diagnostics/pathssrc/Backend/AutopilotMonitor.Functions/Functions/Config/GetAgentConfigFunction.cs—MergeDiagnosticsLogPathssrc/Web/autopilot-monitor-web/components/diagnostics/—DiagnosticsPathRow,BuiltInSectionsList,builtInSectionDisplay- Tests:
DiagnosticsPackageServiceTests(RealmJoin on/off, no user session, bootstrapper evtx, probe failure),DiagnosticsBuiltInSectionsTests,GetDiagnosticsPathsPayloadTests,GetAgentConfigFunctionTests.MergeDiagnosticsLogPaths_*