A laptop turns up in your fleet report looking almost normal. It is Microsoft Entra joined, it is enrolled in Microsoft Intune, and the user has been working on it for three weeks. What it does not have is the security baseline, the certificate profile, or two of the four required applications. Nobody raised a ticket, because from the user's point of view nothing went wrong. They sat through the Enrollment Status Page (ESP), watched it stall, saw an error message, and clicked the button that said they could keep going. Windows let them, because that is exactly what you told it to do.
The ESP time limit is not a progress bar. It is the moment Windows hands the provisioning decision to your end user, and the buttons they get are drawn from a documented bitmask called BlockInStatusPage. The documented default time limit is 60 minutes with a documented maximum of 1440, and Allow users to use device if installation error occurs sets the bit that renders a Continue Anyway button. Turn that on and a device that failed provisioning can enter production silently. You will only find it afterwards by reading the ESP state the device recorded in its own registry.
The problem: the device reached the desktop, the baseline did not
Most administrators configure the Enrollment Status Page once. They set Show app and profile configuration progress to Yes and pick a time limit that feels generous. Then they tick the boxes that sound helpful and move on. The time limit then gets treated as a patience setting. It is not. It is the trigger for an entire failure path with its own user interface, its own choices, and its own consequences.
Here is what actually happens at the end of the timer. The setting is documented as Show an error when installation takes longer than specified number of minutes. Microsoft describes the behaviour plainly: "A time-out error message is shown after your desired time." The device does not roll back. It does not reset itself. It shows an error message, and then it shows the user whichever remediation buttons you allowed.
Three of those buttons are documented in the DMClient configuration service provider (CSP). They are Reset PC, Try Again and Continue Anyway. The last one is the one that puts half-provisioned hardware into production.
Now think about what "continue" actually means. ESP tracks a specific, documented list of items during the device setup and account setup phases. That list includes Win32 applications, SCEP certificate profiles, VPN and Wi-Fi profiles, and a small set of app-related policies. Microsoft is explicit that ESP does not track security policies such as device restrictions, although those policies do still install in the background.
There is a second, quieter consequence. Compliance policy and Conditional Access evaluate later, on their own schedule. A device that skipped a certificate profile can look fine for hours before anything notices. That is a real exposure window. It exists because a person in an office clicked a green button they were told they were allowed to click.
Why it happens: one timer, three buttons, one bitmask
The Enrollment Status Page is a user interface sitting on top of two configuration service providers. Understanding which one owns what makes the failure path obvious.
The DMClient CSP owns the ESP settings and the final verdict, under its FirstSyncStatus node. The EnrollmentStatusTracking CSP, added in Windows 10 version 1903, owns the per-item progress that fills the page. When people say the ESP "timed out", the timer they are describing lives in DMClient.
| Intune ESP setting | ESP profile property in Microsoft Graph | What it decides at the time limit |
|---|---|---|
| Show app and profile configuration progress | showInstallationProgress | Whether ESP appears at all. Set to No and there is no page, no timer and no gate. |
| Show an error when installation takes longer than specified number of minutes | installProgressTimeoutInMinutes | How long Windows polls before it surfaces an error to the user. Documented default is 60 minutes. |
| Show custom message when time limit or error occur | customErrorMessage | Replaces the default "Setup could not be completed" text. Documented maximum length is 10000 characters. |
| Turn on log collection and diagnostics page for end users | allowLogCollectionOnInstallFailure | Shows the Collect logs button, and enables the Windows Autopilot diagnostics page on Windows 11. |
| Block device use until all apps and profiles are installed | drives BlockInStatusPage | Whether ESP is a gate or a spectator. Set to No and users can leave before Intune has finished. |
| Allow users to reset device if installation error occurs | allowDeviceResetOnInstallFailure | Renders the Reset PC button on failure. |
| Allow users to use device if installation error occurs | allowDeviceUseOnInstallFailure | Renders the Continue Anyway button on failure. This is the bypass. |
| No dedicated toggle in the documented settings list | blockDeviceSetupRetryByUser | Documented as blocking the user from retrying setup on installation failure. This is the Try Again path. |
| Only show page to devices provisioned by out-of-box experience (OOBE) | trackInstallProgressForAutopilotOnly, disableUserStatusTrackingAfterFirstUser | Stops the page reappearing for every later user who signs in for the first time. |
| Block device use until these required apps are installed | selectedMobileAppIds | Filters which required apps count as blocking. It does not decide what gets installed. |
| Only fail selected blocking apps in technician phase | allowNonBlockingAppInstallation | Pre-provisioning only. Ignores failures for non-blocking apps during the Technician Flow. |
The timer, with real numbers
The DMClient node behind the time limit is TimeOutUntilSyncFailure. Its documented characteristics are worth committing to memory, because they are the only published numbers in this whole area.
- Unit of measurement: minutes.
- Documented default value: 60.
- Documented allowed range: 1 to 1440, which is one day.
- Documented purpose: "how long we will poll until we surface an error message to the user".
Read that purpose again. The timer surfaces a message. It is not documented as a cancellation, and installations already in flight are not documented as being aborted by the timer itself. What is documented is different and more brutal. If the InstallationState value for any tracked app reaches 4 (Error), ESP stops installing applications.
There is also a second, separate timer that almost nobody configures. The EnrollmentStatusTracking CSP gives each device preparation policy provider its own Timeout node, in minutes. Microsoft documents that "if no timeout value is specified, ESP selects the default timeout value of 15 minutes". That timer governs the device preparation phase, before device setup even starts.
The bitmask that draws the buttons
This is the part that explains everything else. The remediation buttons are not three independent switches inside Windows. They are three bits in one integer node called BlockInStatusPage, documented with a default value of 0.
| Value | Documented meaning | Practical effect |
|---|---|---|
0x0 | Allow the user to exit the page before provisioning completes. | ESP is informational only. No gate, so no failure path to configure. |
0x1 | Block the user on the page and show the Reset PC button on failure. | The user can restart provisioning from scratch. Slow, but safe. |
0x2 | Block the user on the page and show the Try Again button on failure. | The user can retry the current phase without wiping the device. |
0x4 | Block the user on the page and show the Continue Anyway button on failure. | The user can dismiss the gate and use a device that failed provisioning. |
Because these are bit flags they combine. A device carrying BlockInStatusPage of 7 is blocking and offers all three buttons. A device carrying 1 is blocking and offers only a reset. A device carrying 0 is not blocking at all, which is a different risk with the same outcome.
How to verify: read the settings and the outcome the device recorded
Everything above is theory until you read a real device. Microsoft states that all ESP settings and tracking information are logged in the device registry. That means a device can tell you which failure path it was offered, and how the attempt ended.
Step 1: read the ESP settings the device actually received
The settings live under one parent key, keyed by the enrollment identifier. The enrollment GUID differs per device, so enumerate rather than hardcode it.
| Value name | Documented meaning | What to look for |
|---|---|---|
BlockInStatusPage | Whether the page blocks, and which remediation options are available. | Any value with bit 0x4 set means Continue Anyway was on offer. |
TimeOutUntilSyncFailure | Minutes of polling before an error is surfaced. Default 60, range 1 to 1440. | Very low values on app-heavy builds guarantee a time-out. |
WasDeviceSuccessfullyProvisioned | 0 is failure, 1 is success, 2 is in progress. Once it is 0 or 1 it cannot change again. | A 0 is your strongest single piece of evidence that provisioning failed. |
IsSyncDone | Whether the first syncs are done and the device is fully provisioned. | Read it alongside the verdict value, never instead of it. |
ServerHasFinishedProvisioning | Set by the service to tell the experience it has finished. If this is not true, the experience treats provisioning as a failure. | Absent or false on a device already at the desktop is a red flag. |
AllowCollectLogsButton | Whether the Collect Logs button is displayed. Documented default is false. | If this is off, your failed devices produced no user-initiated logs. |
CustomErrorText | The custom error text shown to the user on failure. | Presence confirms your custom message actually reached the device. |
SkipDeviceStatusPage | Whether the device progress page is skipped. Documented default is true. | Microsoft documents the skip marker as 0xffffffff. |
SkipUserStatusPage | Whether the user progress page is skipped. Documented default is true. | Explains a missing account setup phase with no failure involved. |
Step 2: read the per-phase tracking state
The second key tells you what failed, rather than only that something failed. It is created by the EnrollmentStatusTracking CSP.
Three subkeys matter for the failure path. Device holds device preparation and device setup state. ESPTrackingInfo holds timestamped diagnostics for tracked apps and profiles. A subkey named after the signed-in user's security identifier (SID) holds the account setup state.
That third one is diagnostic gold, precisely because of what Microsoft says about it. The SID subkey "is created in the account setup phase" and "won't be created if the device setup phase fails". So on a device where the user status page was not deliberately skipped, a missing SID subkey is evidence that device setup never completed.
Inside those subkeys, InstallationState carries the documented values 1 (NotInstalled), 2 (InProgress), 3 (Completed) and 4 (Error). For device preparation providers the values are 1 (NotInstalled), 2 (NotRequired), 3 (Completed) and 4 (Error). Those providers also carry a LastError value, which ESP only reads when the state is 4.
Locked value under the Apps subkey is documented as showing "whether the device usage is blocked until this stage is completed". Read it together with BlockInStatusPage. If your intent was a hard gate and either of these says otherwise, your profile assignment is not landing where you think it is.Step 3: check the event channel and the collected diagnostics
Windows Autopilot logs to its own channel. Open Event Viewer and navigate to Applications and Services Logs, then Microsoft, then Windows, then ModernDeployment-Diagnostics-Provider, then Autopilot.
These are the documented identifiers on that channel. Pay attention to what is not in the list.
| Event ID | Type | Documented message |
|---|---|---|
| 100 | Warning | Autopilot policy [name] not found. |
| 101 | Info | AutopilotGetPolicyDwordByName succeeded. |
| 103 | Info | AutopilotGetPolicyStringByName succeeded. |
| 109 | Info | AutopilotGetOobeSettingsOverride succeeded. |
| 111 | Info | AutopilotRetrieveSettings succeeded. |
| 153 | Info | AutopilotManager reported the state changed from [original] to [new]. |
| 160 | Info | AutopilotRetrieveSettings beginning acquisition. |
| 161 | Info | AutopilotManager retrieve settings succeeded. |
| 163 | Info | AutopilotManager determined download isn't required and the device is already provisioned. |
| 164 | Info | AutopilotManager determined Internet is available to attempt policy download. |
| 171 | Error | AutopilotManager failed to set TPM identity confirmed. |
| 172 | Error | AutopilotManager failed to set Autopilot profile as available. |
| 807 | Error | ZtdDeviceIsNotRegistered. |
| 809 | Error | ZtdDeviceHasNoAssignedProfile - Assigned profile does not exist. |
| 815 | Error | ZtdDeviceHasNoAssignedProfile - No profile assigned, and no default profile found. |
| 908 | Error | SerialNumberMismatch or ProductKeyIdMismatch. |
For the full picture, collect diagnostics. In OOBE on a non-S mode device, press Shift+F10 and run the documented command for your scenario.
You also get server-side evidence for free, unless you have turned it off. Microsoft documents that Autopilot diagnostics are automatically captured when devices experience a failure. Each device can capture one set of logs per day. Collections are stored for 28 days, and each device can hold up to 10 collections at a time. That is a genuine audit trail of failed provisioning attempts.
The fix: configure the failure path on purpose
Start by finding the profile the device actually got, not the one you remember editing.
- Sign in to the Microsoft Intune admin center.
- Select Devices, expand Device onboarding, then select Enrollment.
- On the Windows tab, under Windows Autopilot, select Enrollment Status Page.
- Note every profile in the list and its priority order. Priority 1 wins.
- Open the profile that targets your build group, then select Properties.
- Next to Settings, select Edit.
- Confirm Show app and profile configuration progress is set to Yes.
- Set Show an error when installation takes longer than specified number of minutes to a realistic value. Count your blocking apps and be honest about how long they take.
- Set Block device use until all apps and profiles are installed to Yes. Without this, the failure path settings below never appear.
- Set Allow users to use device if installation error occurs to No for any build where the security baseline is not optional.
- Set Allow users to reset device if installation error occurs to Yes if your users can tolerate a rebuild, or No if you would rather they call the service desk.
- Set Turn on log collection and diagnostics page for end users to Yes. Microsoft explicitly recommends this, and it costs you nothing.
- Set Show custom message when time limit or error occur to Yes, then write a message that names your service desk. A user who knows who to call does not go hunting for a bypass.
- Select Review + save, then Save.
- Repeat for every profile that could match your devices, including the default profile.
./Vendor/MSFT/DMClient/Provider/MS DM Server/FirstSyncStatus/SkipUserStatusPage as a Boolean set to True.Now pick a posture per scenario, rather than one setting for the whole fleet.
| Scenario | Allow users to use device on error | Reasoning |
|---|---|---|
| Standard corporate user-driven build | No | The baseline is the point of the build. A device that skipped it should not reach the desktop. |
| Executive or travelling user, remote first boot | No, with a raised time limit and a custom message | Buy time instead of buying a bypass. The documented maximum is 1440 minutes. |
| Kiosk, shared or single-purpose device | No | These devices are defined by their configuration. Half configured is useless. |
| Lab, pilot or developer self-service build | Yes, with log collection on | Speed matters more than completeness here, and you will be watching. |
| Pre-provisioning Technician Flow | No | No end user is present, so a bypass button helps nobody. |
Finally, accept the architectural point. ESP was never a security control, and treating it as one is the root cause of this entire problem. It is a provisioning progress gate. The control that decides whether an incompletely provisioned device reaches your data is compliance policy plus Conditional Access. Configure the ESP failure path so devices rarely slip through, and configure compliance so it matters less when they do.
Proof it worked: a device-level outcome report
The companion script reads only documented state. It enumerates the enrollments and finds the FirstSync key. It decodes BlockInStatusPage into named buttons and reads the EnrollmentStatusTracking phases. Then it prints a single verdict of CLEAN, REVIEW, FAILED or UNKNOWN. It writes nothing to the registry and it calls no cloud service.
Its most important behaviour is the one it refuses to perform. If it cannot read the enrollment root, it aborts with exit code 1 rather than reporting a clean device. A false clean on a compliance check is worse than no check at all.
Run it as a Win32 app detection script, as a Remediations detection script, or as a one-off sweep across a build batch. Add -JsonPath to emit structured findings for an inventory pipeline, and -SkipEventLog when the session is not elevated.
Then close the loop in the console. A device that hit the failure path should have a diagnostics collection sitting against it.
A good outcome looks boring. WasDeviceSuccessfullyProvisioned reads 1 and BlockInStatusPage has no 0x4 bit set. No tracked item sits at InstallationState 4. The per-user SID subkey exists, and Device diagnostics is empty for that device. When that is true across a whole build batch, the ESP time limit has stopped being a decision you quietly delegated to your users.
References
- Set up the Enrollment Status Page — Microsoft Intune. The full documented settings list, the 60 minute default time limit, the profile priority model, ESP tracking scope and known issues.
- DMClient CSP — Microsoft Learn. The
FirstSyncStatusnodes, including theBlockInStatusPagebit flags,TimeOutUntilSyncFailureandWasDeviceSuccessfullyProvisioned. - EnrollmentStatusTracking CSP — Microsoft Learn.
DevicePreparation,Setup, theInstallationStatevalues and the 15 minute provider default. - Troubleshoot the Enrollment Status Page — Microsoft Intune. The registry surface, the log collection commands and the user SID subkey behaviour.
- Windows Autopilot troubleshooting FAQ — Microsoft Learn. The Autopilot event channel, the documented event ID table and the Windows Autopilot diagnostics page.
- Device action: Collect diagnostics — Microsoft Intune. Automatic capture on Autopilot failure, 28 day retention and the 10 collection per device limit.
- windows10EnrollmentCompletionPageConfiguration — Microsoft Graph. The ESP profile properties, including
installProgressTimeoutInMinutes,allowDeviceUseOnInstallFailureandblockDeviceSetupRetryByUser. - Windows Autopilot Enrollment Status Page — Microsoft Learn. Overview of what the ESP tracks and what an ESP profile can configure.
Microsoft MVP community deep-dives
| Author | Article | Why it is relevant |
|---|---|---|
| Jan Ketil Skanke (MSEndpointMgr) | Installing M365 Apps as Win32 App in Intune | Opens on the exact symptom this post is about: Autopilot or the Enrollment Status Page failing or timing out because of the Office CSP. |
| Rudy Ooms (call4cloud) | Autopilot and Pre-Provisioning's Infinite Waiting List | Traces a stalled ESP to a script that timed out, reading the same EnrollmentStatusTracking\Device\Setup keys used above. |
Download it from Imran76Awan/Windows-Autopilot-Scripts — no sign-in required. It is read-only: it reports and never changes a device or anything in Intune. Validate it in your own environment before relying on the output.