HomeNewsletterCommunityMVP FeedToolsArchiveBlogToday's NewsAboutServicesQuick Links Subscribe free
← Back to Blog
Autopilot AutopilotESPIntuneRegistryTroubleshooting

Where ESP Progress Actually Lives: The Category-Status JSON in the Registry

IA
Imran Awan
16 August 2026

An Enrollment Status Page that sits on "Preparing your device for mobile management" for forty minutes tells the user nothing and tells you almost as little. The usual next step is collecting MDM diagnostics and reading several megabytes of log. There is a much faster path: the ESP writes its own progress into the registry as three JSON documents, one per phase, each containing every subcategory with its state and the exact status text the user saw. Read those and you know precisely where it stopped.

The short version

The ESP records its progress in three REG_SZ values under HKLM:\SOFTWARE\Microsoft\Provisioning\AutopilotSettings: DevicePreparationCategory.Status, DeviceSetupCategory.Status and AccountSetupCategory.Status. Each is a JSON blob with a categoryState plus one entry per subcategory - TPM attestation, Entra join, MDM enrollment, security policies, certificates, network, apps - each with its own subcategoryState and the literal subcategoryStatusText shown on screen, including counts like "Apps (3 of 3 installed)". A category stuck at inProgress with one subcategory also inProgress is your hang point, named exactly.

Note: the ESP has three phases and they are not interchangeable. Device Preparation secures and joins the device (TPM attestation, Entra join, MDM enrollment). Device Setup applies device-targeted policies, certificates, network profiles and apps. Account Setup repeats that set in the signed-in user's context. Knowing which phase hung usually narrows the cause before you look at anything else.

The problem: a progress bar with no diagnostics

The ESP is deliberately opaque to the end user - that is the point, it is a "please wait" screen. But when it hangs or fails, admins inherit the same opacity. The visible symptoms are all variations of:

In the last case especially, the logs may already have rolled. The registry record has not.

Why it happens: the ESP tracks phases and subcategories

Internally the ESP is a state machine over categories and subcategories. It needs to survive reboots mid-provisioning, so it persists that state rather than holding it in memory - which is precisely why it is still readable long afterwards. Each of the three phase values holds a JSON object with:

Those subcategory names are the useful part. "MdmEnrollmentSubcategory" hanging is a completely different investigation from "AppsSubcategory" hanging, and the phase heading on screen does not distinguish them.

Gotcha: AccountSetupCategory.Status reading notStarted across the board is usually not a fault. On many devices the user phase is skipped by configuration, or simply has not run for the account you are inspecting. Do not chase it as a failure unless you specifically expected the user phase to execute.

How to verify: read the three JSON blobs

The values are raw JSON strings, so pipe them through ConvertFrom-Json to get something readable:

PowerShell — read one phase (run elevated)
\$k = 'HKLM:\SOFTWARE\Microsoft\Provisioning\AutopilotSettings' \$s = Get-ItemProperty \$k # Device Preparation - TPM attestation, Entra join, MDM enrollment \$s.'DevicePreparationCategory.Status' | ConvertFrom-Json | Format-List

To find the hang point directly, look for anything still inProgress across all three phases:

PowerShell — find the stuck subcategory
foreach (\$name in 'DevicePreparationCategory.Status','DeviceSetupCategory.Status','AccountSetupCategory.Status') { \$obj = \$s.\$name | ConvertFrom-Json \$obj.PSObject.Properties | Where-Object { \$_.Value.subcategoryState -match 'inProgress|fail|error' } | ForEach-Object { "\$name -> \$(\$_.Name) = \$(\$_.Value.subcategoryState)" } }
Tip: run that on a device that is currently stuck on the ESP, from a remote session or after pressing Shift+F10 during OOBE, and you get the answer in seconds instead of collecting and parsing a diagnostics bundle. It is the single fastest ESP triage step available.

The fix: identify the stuck subcategory, then act on it

Context: the registry read is diagnosis, not repair. What you do next depends entirely on which subcategory stopped, which is exactly why identifying it first matters. Mapping the common ones:

Stuck subcategoryWhat it is waiting onWhere to look next
TpmAttestationSubcategoryTPM attestation to Microsoft's attestation serviceTPM health and firmware, plus outbound access to the attestation endpoints - a blocked proxy is a common cause
AadjSubcategoryMicrosoft Entra join to completeNetwork and identity - and for hybrid profiles, the on-premises domain join path and its connector
MdmEnrollmentSubcategoryIntune MDM enrollmentEnrollment restrictions, licensing, and device enrollment limits in Intune
AppsSubcategoryRequired apps to install - the status text carries the countThe blocking apps themselves; a single large or failing app holds the whole phase
SecurityPoliciesSubcategoryDevice configuration profiles to applyThe specific profiles targeted at the device, and any conflicts between them
CertificatesSubcategoryCertificate profiles to deliverSCEP/PKCS infrastructure and the NDES connector, if used

To review or reduce what the ESP actually blocks on, in the Intune admin center:

  1. Sign in to intune.microsoft.com.
  2. Go to Devices › Enrollment › Enrollment Status Page.
  3. Select your profile and review Show app and profile configuration progress, the block/timeout settings, and critically the list of apps selected under Block device use until required apps are installed.
  4. Trim that app list to the genuine minimum - every app there is a chance to hang the phase.
intune.microsoft.comDevices › EnrollmentEnrollment Status Page
Watch out: do not edit these JSON values to force a phase to "succeeded". The ESP reads its own state on resume, so a hand-edited blob can convince it that work it never performed is complete - producing a device that finished provisioning without the policies, certificates or apps you required, and no record that anything was missed. Diagnose from these values; change behaviour in the ESP profile.

Registry reference

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\AutopilotSettings
Value name (type)HoldsRead it for
DevicePreparationCategory.Status (REG_SZ)JSON: TPM attestation, Entra join, MDM enrollment, ESP provider installFailures before the device is even manageable
DeviceSetupCategory.Status (REG_SZ)JSON: security policies, certificates, network connections, apps, reboot coalescingThe most common hang phase - app and policy delivery, with counts in the status text
AccountSetupCategory.Status (REG_SZ)JSON: the same set again in user context, plus MFA preparationUser-phase problems; notStarted here is frequently normal
Global.MDMEnrollmentStatus (REG_SZ)Coarse enrollment stateA quick sanity check before parsing the JSON blobs
Global.ShowContinueAnywayButton (REG_SZ)true when the escape hatch is offeredConfirming whether a user could have bypassed a failing ESP - relevant when a device "provisioned" but is non-compliant
UseRefactoredEsp (REG_SZ)True on builds using the newer ESP implementationExplaining why older guidance and value names may not match what you see

Proof it worked: a full real capture, decoded

A genuine run of Get-AutopilotEspStatus.ps1 from this series against a real Autopilot-provisioned, Entra hybrid joined device. No redaction was needed - none of these fields carry device or tenant identifiers:

PowerShell — Get-AutopilotEspStatus.ps1 (real output)
Autopilot / ESP Category Status -------------------------------------------------------------- Device Preparation (TPM attestation, Entra join, MDM enrollment) categoryState : succeeded statusText : Completed TpmAttestationSubcategory succeeded Securing your hardware (Completed) AadjSubcategory succeeded Joining your organization's network (Completed) MdmEnrollmentSubcategory succeeded Registering your device for mobile management (Completed) EspProviderInstallationSubcategory succeeded Preparing your device for mobile management (Completed) InitiateSyncSessions succeeded SetContinueAnywayButtonVisibility succeeded Device Setup (security policies, certs, network, apps) categoryState : succeeded statusText : Completed SecurityPoliciesSubcategory succeeded Security policies (1 of 1 applied) CertificatesSubcategory succeeded Certificates (No setup needed) NetworkConnectionsSubcategory succeeded Network connections (No setup needed) AppsSubcategory succeeded Apps (3 of 3 installed) RebootCoalescing succeeded SendResultsToMdmServer succeeded SaveWhiteGloveSuccessResult succeeded Account Setup (same set again, in user context) categoryState : notStarted WaitingForAadRegistrationSubcategory notStarted PrepareMultifactorAuth notStarted SecurityPoliciesSubcategory notStarted ...

Read this as a post-mortem and notice how much is recoverable months later. Device Preparation and Device Setup both completed. The exact counts survived - "Security policies (1 of 1 applied)" and "Apps (3 of 3 installed)" - so you can prove not just that the phase succeeded but how much work it did. Certificates and network connections report "No setup needed", which is meaningfully different from "succeeded with nothing to do by accident": the ESP had nothing targeted at it.

Account Setup reads notStarted throughout. On this device that is expected, not a fault - the user phase did not run. This is exactly the case the gotcha above warns about, and it is worth seeing in a real capture so you do not raise it as an incident.

Finally, note SaveWhiteGloveSuccessResult under Device Setup. Its presence indicates the pre-provisioning (white glove) result path was exercised - a useful, non-obvious signal that a device came through a technician flow rather than straight user-driven OOBE.

References

Microsoft MVP community deep-dives

Verified and genuinely on-topic - each URL was fetched and confirmed before being cited here, not copied on trust:

AuthorPostWhat it adds
Rudy Ooms (MVP, call4cloud.nl)Step by Step: How Windows Retrieves the Autopilot ProfileTraces the full eight-step token-and-profile retrieval flow, and independently documents both the AutopilotPolicyCache registry key and the wmansvc on-disk cache this post relies on
PowerShell Scripts — ESP Category Status

Script for this post is in Windows-Autopilot-Scripts.

Get-AutopilotEspStatus.ps1 — read-only: parses all three ESP phase blobs, prints every subcategory with its state and on-screen text, and exits 1 if any failure state is present so it can be used as a detection script
View all scripts on GitHub
Was this post helpful?
React below — no account needed
Share this post
LinkedIn X / Twitter Reddit Bluesky

More from EndpointWeekly

Autopilot
Which apps actually block the Enrollment Status Page (and why…
You set the ESP to block until your required apps install, it cleared in four minutes,…
Autopilot
Reading AutopilotConfigurationFile.json: Every Documented Field…
Microsoft documents exactly nine properties for AutopilotConfigurationFile.json, and none…
Autopilot
CloudAssignedOobeConfig Decoded: Which OOBE Screens Your Profile…
One REG_DWORD on every Autopilot device encodes which OOBE screens your deployment…