There is a deployment pattern that looks completely reasonable on a whiteboard. Script A creates a folder and drops a configuration file into it. Script B reads that file and applies the settings. Two small platform scripts, one clean dependency, done before lunch.
It works on every pilot device. Then you scale to four thousand machines and a slice of them arrive at the desktop half-configured. Not all of them. Not the same ones twice. That is the signature of an ordering assumption, and Intune platform scripts are one of the easiest places in the endpoint stack to make one by accident.
Microsoft documents that platform scripts run before Win32 apps, and documents the phase order inside an Autopilot device preparation deployment. Microsoft documents no execution order at all between one platform script and another. That absence is the whole point: if your provisioning depends on script A finishing before script B starts, nothing in the product guarantees it, and the failure will be intermittent rather than obvious. Make every script self-contained, or move the ordering into Win32 app dependencies where Intune actually enforces a relationship.
The problem: script chains that only break at scale
Intune platform scripts are the PowerShell scripts feature you find under Devices in the admin center. They are delivered and executed by the Intune Management Extension, which Microsoft abbreviates as the IME. For the rest of this post I will use both names, because the portal says "platform scripts" and every log file on the device says IME.
The dependency chain is the most common way admins get hurt here. It usually looks like one of these:
- Script A writes a certificate or a licence file to disk. Script B installs a product that needs it.
- Script A sets a registry value that gates a feature. Script B configures that feature.
- Script A creates a local group or a service account. Script B adds something to it.
- Script A maps a network location. Script B copies content from it.
Every one of those pairs runs fine when the device is quiet. The pilot device has nothing else competing for the IME, so the scripts effectively run one after another and the chain holds. During real provisioning the device is doing a dozen other things at once. The order shifts, and the second script runs against a prerequisite that does not exist yet.
Here is the shape of the failure, as it appears in the second script.
Notice the second failure mode. A missing prerequisite that throws is annoying but visible. A missing prerequisite that produces an empty value and a zero exit code is much worse, because Intune records the script as successful. Your reporting says the fleet is configured. It is not.
Why it happens: what Microsoft documents, and what it does not
The honest answer to "what order do platform scripts run in" is that Microsoft does not say. Before you treat that as a gap in my research, look at how much Microsoft does document about script execution. The specificity everywhere else is exactly what makes the silence on ordering meaningful.
What is documented
| Behaviour | What Microsoft documents | Source doc |
|---|---|---|
| Run frequency | "Once the script executes, it doesn't execute again unless there's a change in the script or policy." | Platform scripts |
| Retry on failure | Retried three times, on the next three consecutive IME check-ins. After that, no further attempts unless the script changes. | Platform scripts |
| Timeout | Scripts time out after 30 minutes. | Platform scripts |
| Size limit | The script must be less than 200 KB (ASCII). | Platform scripts |
| Check-in cadence | The IME checks for new or updated installations every 8 hours, independently of the MDM check-in. It also checks after every reboot. | IME overview |
| Execution context | "Run this script using the logged on credentials" defaults to Yes. Choose No for system context. | Platform scripts |
| Host architecture | "Run script in 64-bit PowerShell host" defaults to No, which means a 32-bit host. | Platform scripts |
| Per-user re-run | Scripts assigned to the device run for every new user that signs in, except on multi-session SKUs where user check-in is disabled. | Platform scripts |
| Scripts versus Win32 apps | "PowerShell scripts are executed before Win32 apps run. In other words, PowerShell scripts execute first. Then, Win32 apps execute." | Platform scripts |
| ESP tracking | "During ESP, SideCar tracks only Win32 apps (no PowerShell scripts)." | ESP troubleshooting |
That last row is the one most people get wrong, so it is worth stating slowly. SideCar is Microsoft's internal name for the IME acting as an Enrollment Status Page policy provider. During a classic Autopilot deployment with the Enrollment Status Page shown, the page counts and blocks on Win32 apps. It does not count or block on platform scripts.
Autopilot device preparation is different, and it is documented
Autopilot device preparation, sometimes called Autopilot v2, does not use the Enrollment Status Page at all. Microsoft states that plainly: "Windows Autopilot device preparation doesn't use the Enrollment Status Page (ESP)." Instead it runs its own tracked sequence, and that sequence is published. Microsoft documents device setup continuing in this order.
- The device joins Microsoft Entra ID and enrols in Intune.
- The Intune Management Extension installs.
- The user is added to, or removed from, the local Administrators group according to the policy.
- The deployment syncs with Intune and checks for selected line-of-business and Microsoft 365 apps. All MDM policy is synced at this point too, but "application of the policy isn't tracked during the deployment."
- Selected line-of-business and Microsoft 365 apps install. A failure here fails the deployment.
- Selected PowerShell scripts run. A failure here fails the deployment.
- Selected Win32, Microsoft Store and Enterprise App Catalog apps install. A failure here fails the deployment.
- The "Required setup complete" page is shown, the user is signed in, and a second sync delivers everything else.
Read step 6 carefully. It says the selected scripts run, and that a failure fails the deployment. It does not say in what order the selected scripts run relative to each other. That is the gap, and it is the same gap as in classic Autopilot.
The absence of an ordering guarantee is the finding
Microsoft is not shy about saying when order is undefined. In the Win32 app documentation, describing dependencies, the wording is explicit: "evaluation and installation of dependencies doesn't follow a specific order at a dependency level." That is a product team choosing to tell you an ordering guarantee does not exist.
No equivalent statement exists for platform scripts, in either direction. There is no documented order between scripts, and there is no documented promise of one. The correct engineering response to an undocumented ordering is to assume there is none. An undocumented behaviour that happens to hold today is free to change in the next IME release, and the IME updates itself.
The registry surface, documented and otherwise
Three registry areas matter here. Two are documented by Microsoft. One is not, and I am labelling it clearly rather than presenting it as official. They all sit below this parent key.
| Subkey below that parent | What it holds | Documented? |
|---|---|---|
| Windows\Autopilot\EnrollmentStatusTracking | Root of Enrollment Status Page tracking state. Contains Device, ESPTrackingInfo, and a subkey named for the signed-in user's SID. | Yes |
| Windows\Autopilot\EnrollmentStatusTracking\Device\DevicePreparation | Install state of the IME as a page policy provider, plus the resource types that provider tracks. States are 1 NotInstalled, 2 NotRequired, 3 Completed, 4 Error. | Yes |
| Windows\Autopilot\EnrollmentStatusTracking\Device\Setup\Apps\PolicyProviders\Sidecar | TrackingPoliciesCreated, showing whether the IME built its tracking policies for the device setup phase. | Yes |
| Windows\Autopilot\EnrollmentStatusTracking\Device\Setup\Apps\Tracking\Sidecar\Win32App_{AppID} | InstallationState per tracked Win32 app: 1 NotInstalled, 2 InProgress, 3 Completed, 4 Error. An Error stops the page installing further apps. | Yes |
| Enrollments\{EnrollmentGUID}\FirstSync | The page settings the device received, including SkipDeviceStatusPage and SkipUserStatusPage, which are set to 0xffffffff when a phase is skipped. | Yes |
| IntuneManagementExtension\Policies\{Owner}\{ScriptID} | Per-script state the IME records: a Result string, an ErrorCode, a DownloadCount, and typically ResultDetails carrying the script's output. Owner is the all-zero GUID for device context, or a user object ID for user context. | No. Community-known, stable in practice, subject to change without notice. |
Notice what is missing from the documented rows. There is a tracked, numbered, per-app state for Win32 apps and nothing equivalent for scripts. The product tracks app ordering because app ordering is a feature. It does not track script ordering because script ordering is not one.
How to verify: read the IME's own record on the device
You cannot verify an ordering guarantee that does not exist. What you can do is observe what actually happened on a device, which is enough to prove or disprove a suspected ordering bug. Start in the portal, then go to the device.
Step 1: check the recorded outcome in the admin center
Every platform script has its own monitoring blade. This tells you whether the IME thinks the script succeeded, not whether it did its job.
Here is the full click path to create or inspect a platform script, with the settings that matter for provisioning.
- Sign in to the Microsoft Intune admin center.
- Select Devices, then Scripts and remediations, then Platform scripts.
- Select Add, then Windows 10 and later.
- In Basics, enter a name and a description, then select Next.
- In Script settings, browse to the script file. It must be under 200 KB of ASCII.
- Set Run this script using the logged on credentials to No for anything that runs during provisioning, because no user is signed in during the out-of-box experience.
- Set Enforce script signature check according to your signing policy. The default is Yes.
- Set Run script in 64-bit PowerShell host to Yes if the script touches 64-bit-only paths or registry views. The default is No, which means a 32-bit host.
- Select Next through Scope tags, then assign the policy to a device group under Assignments.
- Review the summary and select Add.
For Autopilot device preparation, the selected scripts live in the policy itself.
Inside the policy, the Apps section allows up to 25 managed applications and the Scripts section allows up to 10 PowerShell scripts. Both sets must also be assigned to the device group named in the policy, and both must be configured for System context. The out-of-box experience timeout, Minutes allowed before showing installation error, accepts an integer between 15 and 720 and applies to the whole deployment rather than to any individual app or script.
Step 2: read the per-script state on the device
This is the undocumented key. It is where the IME parks its own record of what it ran and what came back.
A DownloadCount of 4 on a failing script is the documented retry behaviour showing itself. That is the original attempt plus three retries, after which the IME stops trying.
Step 3: build the real timeline from the logs
The log folder is documented as C:\ProgramData\Microsoft\IntuneManagementExtension\Logs. Microsoft documents IntuneManagementExtension.log as the main log, carrying check-ins, policy requests, policy processing and reporting. It documents AgentExecutor.log as the log that tracks PowerShell script executions. Both are CMTrace format, so every line carries a timestamp. That timestamp is the only real evidence of order you will ever have.
Step 4: run the companion script
Reading two sources by hand and correlating GUIDs gets old quickly. The companion script does it for you. It reads the IME policy key, parses the IME logs, and prints both the recorded outcome per script and the chronological order actually observed on that device. It is read-only, it fails loudly rather than reporting a misleading empty result, and it runs on both Windows PowerShell 5.1 and PowerShell 7.
The file is Get-PlatformScriptExecutionOrder.ps1. Optional flags: -IncludeAgentExecutor to add the script-host launches, -MaxEvents 0 to print the full timeline, and -ResolveNames to turn script GUIDs into their Intune display names with a read-only Microsoft Graph call to GET /beta/deviceManagement/deviceManagementScripts.
The fix: designs that do not need an order
There is no setting that makes platform scripts ordered. The fix is a design change, and there are four patterns worth knowing.
Pattern 1: make every script self-contained
This is the default answer and it solves most cases. A platform script should create its own prerequisites, verify its own outcome, and be safe to run twice. It should never assume another script has run.
Pattern 2: move the ordering into Win32 app dependencies
When the work genuinely cannot be made independent, stop using platform scripts for it. Package each step as a Win32 app and express the relationship as a dependency. Intune will not install a Win32 app until its dependent apps are installed, which is an enforced relationship rather than a hopeful one.
- Package each step with the Microsoft Win32 Content Prep Tool into an
.intunewinfile. - In the admin center, go to Apps, then All Apps, then Create, and choose Windows app (Win32).
- Upload each package and set its install and uninstall commands. Set Install behavior to System.
- On the Detection rules page, define a rule that proves the step completed. A registry value or a file the step creates works well. All configured rules must be satisfied for the app to count as installed.
- Select Create to add the app, then reopen it and go to the Dependencies page. Dependencies can only be added after the app exists in Intune.
- Add the earlier step as a dependency of the later step, and leave Automatically install set to Yes.
- Assign only the final app as Required. Intune targets and installs the dependencies for you.
Two documented limits shape this design. There is a maximum of 100 apps in a dependency graph, counting sub-dependencies and the parent app itself. And critically, "evaluation and installation of dependencies doesn't follow a specific order at a dependency level." So build a linear chain, where C depends on B and B depends on A, rather than making both A and B dependencies of C at the same level.
Each dependency also inherits the documented Win32 retry logic, which is three attempts five minutes apart, plus the global reevaluation schedule that follows a 24-hour cadence. Supersedence is the sibling feature for replacing or updating an app, capped at 10 nodes in a supersedence graph.
Pattern 3: bounded waiting, never unbounded
Sometimes you cannot avoid waiting on something the script does not control, such as a service reaching a state. Wait with a bound, and let the platform retry rather than sitting there.
Remember the documented 30-minute script timeout. Remember too that during Autopilot device preparation the out-of-box experience timeout covers the entire deployment, not one script. A script that blocks for 25 minutes waiting for a dependency has burned most of a provisioning budget that everything else shares. Wait for a minute or two, then exit non-zero and let the IME retry on the next check-in.
Pattern 4: keep provisioning-critical work small and explicit
In Autopilot device preparation, only the apps and scripts you explicitly select in the policy are part of the tracked deployment. Everything else assigned to that device group arrives in the second sync, after the user has a desktop. So decide what genuinely must be true before first sign-in, select exactly that, and let the rest land afterwards. Fewer selected items means fewer chances for the undefined order between them to matter.
There is no Group Policy equivalent, and that matters here
Platform scripts are an Intune-only feature, delivered by the IME. There is no Group Policy path that configures them, and no configuration service provider that reorders them.
Proof it worked: the observed order on a real device
The companion script was run on a live Intune-managed Windows 11 device. The output below is genuine, with tenant and script identifiers replaced by Microsoft-style placeholders and redaction markers. Treat the specific GUIDs and timestamps as illustrative.
What to take from a run like this. The registry section tells you the outcome the IME recorded, which is what the portal reports. The timeline section tells you when things actually happened, which is the only way to test an ordering theory. If the two scripts you care about show overlapping windows in the timeline, you have your answer. They were not sequential, and any code that assumed they were is running on luck.
The Event Viewer question
There is no Event ID catalog in this post, and that is deliberate rather than an omission. Microsoft does not document an event log channel or event IDs for platform script execution. The diagnostic surface for the IME is its CMTrace-format log files, which Microsoft suggests reading with CMTrace.exe. The related Autopilot and mobile device management event channels, such as the DeviceManagement-Enterprise-Diagnostics-Provider admin channel, carry enrolment and reboot diagnostics rather than per-script results. Inventing plausible-looking event IDs here would be worse than saying there are none.
References
Microsoft official documentation, all fetched and confirmed while writing this post.
- Add PowerShell scripts to Windows devices in Microsoft Intune — run-once behaviour, the three-retry scenario, the 30-minute timeout, the 200 KB limit, the context and 64-bit host settings, and the statement that scripts execute before Win32 apps.
- Understand Microsoft Intune Management Extension — the 8-hour check-in, the documented log folder and the per-file log table, the minimum IME version, and the removal conditions.
- Troubleshoot the Enrollment Status Page (ESP) — the EnrollmentStatusTracking registry layout, the SideCar provider, the installation state values, and "During ESP, SideCar tracks only Win32 apps (no PowerShell scripts)."
- EnrollmentStatusTracking CSP — the node tree behind that registry state, the device preparation timeout default of 15 minutes, and the tracked resource types.
- Windows Autopilot device preparation user-driven Microsoft Entra join overview — the documented step-by-step device setup order, including where scripts sit relative to apps.
- Create a Windows Autopilot device preparation policy — the Apps and Scripts limits, the System context requirement, and the 15 to 720 minute deployment timeout.
- Windows Autopilot device preparation troubleshooting FAQ — "Windows Autopilot device preparation doesn't use the Enrollment Status Page (ESP)."
- Add and assign Win32 apps to Microsoft Intune — dependencies, the 100-app graph limit, supersedence, the retry logic, and "evaluation and installation of dependencies doesn't follow a specific order at a dependency level."
- List deviceManagementScripts, Microsoft Graph beta — the endpoint and the read-only scope the companion script uses to resolve script names.
Community deep-dives, each fetched and confirmed on topic.
| Author | Post | Why it is worth reading |
|---|---|---|
| Rudy Ooms | Autopilot and pre-provisioning's infinite waiting list | Walks a real case where a platform script blocks during pre-provisioning and the Enrollment Status Page sits on "Identifying apps", precisely because scripts are not tracked. |
| Peter van der Woude | Working with Win32 app dependencies | The practical walkthrough of building an ordered chain with dependencies, including the dependency viewer and its limits. |
The short conclusion. Platform scripts are excellent at doing one independent thing well. They are a poor substitute for a dependency graph. If your provisioning correctness rests on which of two scripts wins a race, redesign it before it redesigns your week.
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.