In enterprise environments, we have to deal with a lot of requirements when it comes to app management. One of the common challenges is to control the installation moment during enrollment. We already have some basic controls in place. If the Enrollment Status Page (ESP) is configured during the enrollment all device targeted apps are installed in the “Device Setup” phase and all the user targeted apps are installed in the “Account Setup” phase. These are simple moments in time already. But what if you would like to schedule the installation after ESP when the Desktop is fully available and displayed?
The goal is to let a required Win32 app deployed via Intune install only if the ESP is not running anymore. Why? Maybe because we want to display a dialog for user interaction and this is only possible when the Desktop is fully loaded, otherwise the dialog will be hidden behind the ESP and can’t be interacted with. Resulting in an ESP waiting infinitely for an app which is hidden in the background :-(.
In fact, I needed this for my BitLocker PIN solution (How to enable Pre-Boot BitLocker startup PIN on Windows with Intune) already in the past but didn’t come up with a good idea how to solve it in a reliable way.

Now I gave it another try and solved it by doing the following.
UPDATE 10/20/202:
The following approach worked well in my test environment, but not during the final implementation! The initial idea is used somewhere else where it is working successfully with a different precondition, but in the Autopilot requirement script, there is one caveat I found after releasing the blog. The following paragraphs still describe the way I approached the problem and finally also the issue discovered later with it. If you want to jump to the final solution, click here.
Similar to the Win32 App Requirement Script for Primary User Detection (Deploy an Intune application with user device affinity) I wrote a Win32 App Requirement Script to detect the running ESP. The script is looking for the WWAHost process which is responsible for UWP apps to run JavaScript code. The ESP is based on a UWP app implementation using JavaScript. To accomplish this task, the WWAHost has to load the CloudExperienceHost Module for this to actually display the ESP to the user. So, if the WWAHost is running and has the CloudExperienceHost module loaded, Windows will assign a MainWindowHanldle to the process as soon as the process displays a Window. This is what we can query from any context (user context or system context) by enumerating the MainWindowHandle from the process:
$(Get-Process -Name WWAHost).MainWindowHandle
If this gives us a value other than 0 the process is showing a Window currently.
The Requirement Script finally looks like this:

To demonstrate the whole story, here are a few screenshots of how this all works.
First, I checked if the theory actually works, so execution of the code in system context during the ESP “Account Setup” phase. We see the result of active:

I used my BitLocker PIN solution and then added the new requirement script to it and voila, it did what we expected from theory. The execution was prevented during ESP and first allowed after ESP wasn’t detected anymore (you can see the full Desktop in the background).

To get this result I simply added the Requirement Script to the BitLocker PIN solution like this:

Adding the Detect-ActiveESP.ps1 and do a string compare to ESP-not-active.
As l already demonstrated the BitLocker script was first shown when the full Desktop was loaded:

UPDATE 10/20/202 – changed approach:
The issue with this solution is that my test approach was altering the environment. By utilizing the [Shift] + [F10] hotkey to get a system command prompt I changed the preconditions! As soon as you enter the [Shift] + [F10] hotkey, the explorer.exe is loaded and this is a different precondition for my requirement script. I verified the script by utilizing this approach and it worked perfectly by executing the code in the command prompt. You may ask now: What is the issue? As soon as I started to use the code in the requirement script without the [Shift] + [F10] I was using during verification, it wasn’t working anymore. After digging deeper, I found this:
MS Learn Diagnostics.MainWindowHandle
A process has a main window associated with it only if the process has a graphical interface. If the associated process does not have a main window, the MainWindowHandle value is zero. The value is also zero for processes that have been hidden, that is, processes that are not visible in the taskbar. This can be the case for processes that appear as icons in the notification area, at the far right of the taskbar.
This explains why my command prompt tests were successful as explorer.exe was loaded.
So, I gave it another try to find a different indicator when the desktop is loaded. It is not an elegant version (imho) and has some downsides, but it ist working for now. I simply take the “Windows Security notification icon” as an indicator for now. This icon is first started when the explorer fully loads in the User Desktop session:

The downside is, that the security icon should not be hidden by policy:
HKLM\SOFTWARE\Policies\Microsoft\Windows Defender Security Center\Systray\HideSystray = 1
As long as this precondition is given the solutions works fine for me.
You can find the updated simple Requirement Script on my GitHub:
https://github.com/okieselbach/Intune/blob/master/Detect-ActiveESP.ps1
I hope this solves a challenge that is coming up often. At least for me, it is useful and a real-world use case I have shown with the BitLocker PIN solution. I hope also it demonstrates how even your test approach may not always be sufficient. Feel free to leave a comment about what you have used it for or if you found a better solution!
Happy deployment!
Good evening,
Thank you for this post. Unfortunately, the solution
does not work for my client.
I am in the following context:
– Windows 11
– Windows Autopilot White Glove.
– Cloud Trust configured
– User ESP enabled with WH4B configured to activate on exit from user ESP.
I have created a Win32 application with as required, your script.
I do a white glove with reseal. Success.
I start the laptop and do the user registration.
The user ESP is blocked for 30 minutes during which the application installs after 10 minutes.
The log shows ESP-not-active.
I came across another similar post where the person provided 2 solutions per script but I have the same problem.
Do you have any idea?
Thanks for your help.
Hi David,
did you use the updated version of the script or the one with the MainWindowHandle?
best,
Oliver
This is similar to what I have been using when I have apps that I want to get installed right after the User ESP has completed. Sometimes Intune runs re-detection for this app very fast and sometimes it can take like 30-60 minutes. Have you noticed this?
I’ve been using custom detection script for ESP to simply detect if wwahost.exe process is running or not. I assume you have tested this method also but found something that it cannot be used?
Yes the issue is that the wwahost process stays alive with Windows 11 after deployment. 😏
When looking at the IntuneManagementExtension.log you can see that it seems to use some logic to detect if the ESP is running or not. “[Win32App] Not in ESP processing. EspPhase: NotInESP, sender……….” Would be interesting to know if this could be used in custom scripts
Hi Oliver,
thank you for that helpful post.
Is there really no way to deploy a Win32-App with a user interface during the ESP?
I tracked down all active processes before and after I press Shift + F10. My hope was to find a process that I can then start manually to get the session of defaultuser0 interactive, but so far I had no success.
Best regards,
Daniel
After you hit Shift + F10 you could technically get a foreground window, but without this you can’t steal a foreground Windows property of the ESP process. The OS will not allow the stealing of the TopMost property.
best,
Oliver
Hi,
i see in the settings for the requirements the “detectbitlockerstatus.ps1” as a setting. I can´t find anything how to set it up in your documentation 😦
It is just a requirement script to detect if BitLocker status is on or not. Can be used in whatever situation you like. Not specifically mention for anything. It you want something postponed until encryption is finished you can use it.
Compare output for BitLockerProtection-On or BitLockerProtection-Off.
Hey, Oliver.
How about instead reading the ESP status directly from the registry? Have you tried that?
https://learn.microsoft.com/en-us/troubleshoot/mem/intune/device-enrollment/understand-troubleshoot-esp
More info, samples and techniques:
https://learn.microsoft.com/en-us/windows/client-management/mdm/enrollmentstatustracking-csp
https://www.powershellgallery.com/packages/Get-AutopilotESPStatus/4.1/Content/Get-AutopilotESPStatus.ps1
Cheers.
P.S. Comments are not checked, ping me on LinkedIn instead.
Hi Arsen,
I’ve tried several approaches. And the defaultUser approach was failing in rare circumstances, as the real user gets logged on at a certain point in time. The CSP approach is also not reliable, as the EnrollmentStatusTracking CSP sadly didn’t always reflect the actual status. It never switched to finish on some machines, so it’s not really reliable. The registry key approach is the more robust approach imho, but a slightly more complex solution and bound to parsing of keys which need to fulfill what you are looking for. A change by MS might break the solution. Yes, sure this might be true for a lot of ways if MS changes something. So given all these I chose initially the WindowHandle way of solving this, but as you can read it failed due to the measurement and therefore altering the system in behavior. The last approach is a very simple version which has a good chance of being reliable for a longer time, as long as you do not hide the process. Anyway, there might be several solutions to tackle it. Right now, I’ve chosen this one and it works in my environment. Does not mean it will work for everyone.
best,
Oliver
We’re seeing very unreliable results with the popup, you mentioned it appears as soon as the desktop launches but we are seeing it taking hours to appear due to the initial run failing the requirements check while the ESP is up. While this isn’t an issue as the device is still encrypted, just without a PIN, is there a way to make it more reliable with regards to the timeframe?
Hey Dan,
actually, it should not take more than max 1h. as IME syncs with the service every 1h and evaluates the apps. So, max 1h later another sync after ESP should occur (in general it would be something around 30min.) and then bring the popup. If not, you should investigate the logs a litte deeper to understand why. Everything in the logs regarding these evaluations should be logged.
best,
Oliver
Thanks Oliver for the helpful post. I just wanted to clarify is that based on my understanding this is now fixed and now the barrier between blocking and required vs required but not blocking is now respected 🙂. Meaning we should not need to follow the steps in this article. Would you please confirm?
Hi,
the upcoming change “Windows Autopilot pre-provisioning process for app installs” (https://techcommunity.microsoft.com/t5/intune-customer-success/update-to-windows-autopilot-pre-provisioning-process-for-app/ba-p/3752516) is for the technician phase, meaning pre-provisioning aka WhiteGlove. So not totally covered if you do user-enrollments.
best,
Oliver
https://techcommunity.microsoft.com/t5/intune-customer-success/selecting-required-apps-for-your-enrollment-status-page/ba-p/2200381
Does this mean if I have an app like AutoCAD set as required but not listed in the blocking apps during ESP, there is still a chance for it to be installed during the ESP as a blocking app ? Sorry I’m a little confused as to why would an app block device use during ESP despite not being listed as a blocking app.
Yes, that was exactly the case all the time, as the apps got installed in random order and the ESP is told by the setting wait for these x apps. So, if the total amount of apps is provided in random order you may install several apps in the beginning which are not in the list of the blocking apps. But with the new v3appProcessor this behavior changed and they are now sending down the app list of the blocking apps first. See here for the changed behavior of the ESP: https://techcommunity.microsoft.com/t5/intune-customer-success/upcoming-improvements-to-win32-app-supersedence/ba-p/3713026
Hi Oliver
Did you try to start the same application during OOBE?
For me the process starts, but it isn’t in the foreground. The WWAHost “hides” it.
As soon as, I press “shift+f10” this process shows up and the OOBE goes in the “debug” mode.
Do you have any experience with it?
I am curious what happens when “shift+f10” getting pressed.
Gruess, Ákos 😉
You won’t get applications to foreground as long as another application has exclusive foreground already occupied. Shift+F10 releases teh exclusive foreground from the ESP and other windows can grab foreground then. But as long as the ESP has the foreground permission you can’t circumvent it. That’s the window handling in Windows. So, Shift+F10 releases this exclusive foreground permission. That’s all I know about it.