When the Microsoft Intune Management Extension (IME) gets stuck in the Enrollment Status Page (ESP), pinpointing the exact cause can feel like looking for a needle in a haystack. But with the right tools, like PowerShell, and this Advanced IME and ESP Troubleshooting guide, you can quickly get to the root of the issue.
This post will walk you through the IME’s ESP detection flow step-by-step. We’ll show you how each phase works and provide scripts to help you determine the ESP phase yourself.
Introduction and a small recap
Our last blog explored how devices can get stuck on the Enrollment Status Page (ESP), particularly when the Intune Management Extension (IME) can’t progress beyond the Account Setup phase. The IME relies on specific registry keys and status checks to determine each phase of the ESP flow. By taking a look at the Intune Management Extension logs, you could see it for yourself, but here’s a quick recap of how it works:
Check FirstSync: IME first verifies if the device has completed initial syncing with Intune by checking the FirstSyncSID registry key. If the key is missing or IsSyncDone is not set to 1, IME halts at Account Setup, assuming the sync isn’t complete.
Verify Sidecar Policy Provider Installation: After confirming sync, IME checks the InstallationState of the Sidecar Policy Provider. If not set to “Completed,” it indicates that essential provisioning policies aren’t fully deployed yet.
Confirm Provisioning Completion: IME then verifies the HasProvisioningCompleted status to ensure that the device provisioning process is done. A true value here means the device is ready to continue beyond provisioning.
Track Sidecar Policy Creation: Once provisioning completes, IME verifies that tracking policies are in place by checking the TrackingPoliciesCreated flag. If missing, it pauses until these policies are properly applied.
Device and Account Setup Checks: Finally, IME checks the installation state of all required apps in both the device and user phases. Any app that’s not fully installed, or in progress, will delay IME from marking the ESP as complete.
Diving into the GetESPPhase step by step
Now, let’s dive into each ESP phase, adding PowerShell scripts that let you check these settings directly and troubleshoot where the Microsoft Intune Management Extension (IME) might be encountering issues.
Step 1: CheckESPPhase and the FirstSync Key
After opening the Intune Management Extension log files with the CMTrace tool, we will notice that the FirstSyncSID key indicates whether a device’s initial sync was completed successfully. If it’s present, ESP should set the NotInEsp status.
If that key below (SID) doesn’t exist and with it, the IsSyncDone is not set to 1, the IME will stay in the Account Setup Phase.
You can check it yourself by running this PowerShell Script:
# Define the base registry path
$basePath = "HKLM:\SOFTWARE\Microsoft\Enrollments"
# Get all enrollment GUIDs under the base path
$enrollments = Get-ChildItem -Path $basePath
foreach ($enrollment in $enrollments) {
# Check if the ProviderID is set to "MS DM Server"
$providerID = (Get-ItemProperty -Path $enrollment.PSPath -Name "ProviderID" -ErrorAction SilentlyContinue).ProviderID
if ($providerID -eq "MS DM Server") {
# Check if there is a "FirstSync" key under the current enrollment for device-level sync status
$firstSyncPathDevice = Join-Path -Path $enrollment.PSPath -ChildPath "FirstSync"
if (Test-Path -Path $firstSyncPathDevice) {
# Check device-level "IsSyncDone" status
try {
$isSyncDoneDevice = (Get-ItemProperty -Path $firstSyncPathDevice -Name "IsSyncDone" -ErrorAction Stop).IsSyncDone
if ($isSyncDoneDevice -eq 1) {
Write-Host "Device-level IsSyncDone: Completed" -ForegroundColor Green
} elseif ($isSyncDoneDevice -eq 0) {
Write-Host "Device-level IsSyncDone: Not Completed" -ForegroundColor Red
} else {
Write-Host "Device-level IsSyncDone: Unexpected Value ($isSyncDoneDevice)" -ForegroundColor Yellow
}
} catch {
Write-Host "Device-level IsSyncDone: Missing" -ForegroundColor Red
}
}
# Check if there is a "FirstSync" key under the current enrollment for user-level sync status
if (Test-Path -Path $firstSyncPathDevice) {
# Get all SIDs under the "FirstSync" path
$sids = Get-ChildItem -Path $firstSyncPathDevice -ErrorAction SilentlyContinue
if ($sids.Count -eq 0) {
# If FirstSync is found but no SID subkeys exist, output an error message
Write-Host "User-level IsSyncDone: No SIDs Found" -ForegroundColor Red
} else {
# Loop through each SID if present
foreach ($sid in $sids) {
try {
# Attempt to retrieve the "IsSyncDone" value for each SID
$isSyncDoneValue = (Get-ItemProperty -Path $sid.PSPath -Name "IsSyncDone" -ErrorAction Stop).IsSyncDone
# Output based on the IsSyncDone value
if ($isSyncDoneValue -ne $null) {
if ($isSyncDoneValue -eq 1) {
Write-Host "User-level IsSyncDone: Completed" -ForegroundColor Green
} elseif ($isSyncDoneValue -eq 0) {
Write-Host "User-level IsSyncDone: Not Completed" -ForegroundColor Red
} else {
Write-Host "User-level IsSyncDone: Unexpected Value ($isSyncDoneValue)" -ForegroundColor Yellow
}
}
} catch {
# Handle cases where IsSyncDone does not exist, but only if the SID exists
Write-Host "User-level IsSyncDone: Missing" -ForegroundColor Red
}
}
}
}
}
}
So, for example if the FirstSync key is created but the user-level Issyncdone is missing, this script will show you the outcome:
Step 2: Verify InstallationState of the Sidecar PolicyProvider
After trying to find the IsSyncDone for the logged in use, the IME now starts checking the SideCar InstallationState. .
This code will check the InstallationState and if it is set to complete, not installed, or in progress
The PowerShell Script Example:
function Get-PolicyProviderInstallationState {
param (
[string]$InstanceID = "Sidecar"
)
Write-Output "[Win32App] Checking InstallationState for PolicyProvider with InstanceID: $InstanceID"
try {
# Query the WMI class for the specific PolicyProvider installation state
$providerQuery = Get-WmiObject -Namespace "root\cimv2\mdm\dmmap" -Query "SELECT InstanceID, InstallationState FROM MDM_EnrollmentStatusTracking_PolicyProviders02_01"
foreach ($provider in $providerQuery) {
if ($provider.InstanceID -eq $InstanceID) {
Write-Output "[Win32App] Found InstanceID: $($provider.InstanceID) with InstallationState: $($provider.InstallationState)"
# Map the InstallationState integer value to a readable status (if needed)
switch ($provider.InstallationState) {
0 { $state = "Unknown" }
1 { $state = "NotInstalled" }
2 { $state = "NotRequired" }
3 { $state = "Completed" }
default { $state = "Unknown" }
}
Write-Output "[Win32App] InstallationState for PolicyProvider '$InstanceID' is $state"
return $state
}
}
Write-Output "[Win32App] PolicyProvider with InstanceID '$InstanceID' not found."
return "NotFound"
}
catch {
Write-Output "[Win32App] Failed to check InstallationState with exception: $_"
return "Error"
}
}
Step 3: Verify HasProvisioningCompleted
After checking the Sidecar Intune Agent, the IME verifies whether the device provisioning is marked complete.
This code above, checks if the hasprovisioningcompleted has been set.
We can spot the same in the appworkload.log (Intune log)
Use this to check its status:
function Get-HasProvisioningCompleted {
Write-Output "[Win32App] Checking HasProvisioningCompleted status for device"
try {
# Query the WMI class for HasProvisioningCompleted status
$provisioningQuery = Get-WmiObject -Namespace "root\cimv2\mdm\dmmap" -Query "SELECT HasProvisioningCompleted FROM MDM_EnrollmentStatusTracking_Setup01"
foreach ($result in $provisioningQuery) {
if ($result.HasProvisioningCompleted -ne $null) {
Write-Output "[Win32App] Found HasProvisioningCompleted: $($result.HasProvisioningCompleted)"
return [bool]$result.HasProvisioningCompleted
}
}
Write-Output "[Win32App] HasProvisioningCompleted property not found."
return $false
}
catch {
Write-Output "[Win32App] Failed to check HasProvisioningCompleted with exception: $_"
return $false
}
}
Step 4: Check Sidecar Tracking Policies Created
Once provisioning completes, IME checks if tracking policies are set.
Run this script to check Sidecar tracking policies:
function Get-SidecarTrackingPoliciesCreated {
param (
[string]$UserSID = $null
)
Write-Output "[Win32App] Checking Sidecar Tracking Policies Created status"
$context = New-Object System.Management.ManagementNamedValueCollection
if ($UserSID) {
$context.Add("PolicyPlatformContext_PrincipalContext_Type", "PolicyPlatform_UserContext")
$context.Add("PolicyPlatformContext_PrincipalContext_Id", $UserSID)
}
# Query WMI to get tracking policies created status
try {
$wmiQuery = Get-WmiObject -Namespace "root\cimv2\mdm\dmmap" -Query "SELECT TrackingPoliciesCreated FROM MDM_EnrollmentStatusTracking_PolicyProviders03_01"
return $wmiQuery.TrackingPoliciesCreated
} catch {
Write-Output "[Win32App] Failed to retrieve Sidecar Tracking Policies Created status"
return $false
}
}
Step 5: Device and Account Setup
IME continues with checking all tracked apps in the device and user/account phases to confirm they’re complete. It does so by kicking off the CheckDeviceAndAccountSetupStateWithWmi IME function.
We can do the same for the device-level checks, using this script:
# Step 3: Check app tracking status for the device phase (via WMI)
function Get-DeviceAppTrackingState {
$deviceApps = Get-CimInstance -Namespace "root\cimv2\mdm\dmmap" -ClassName "MDM_EnrollmentStatusTracking_Tracking03_02" -ErrorAction SilentlyContinue
$rebootRequired = $false
$incompleteApps = $false
foreach ($app in $deviceApps) {
$instanceID = $app.InstanceID
$installationState = $app.InstallationState
$status = ""
# Determine app status based on InstallationState values
if ($installationState -eq 1) {
$status = "Not Installed"
$incompleteApps = $true
} elseif ($installationState -eq 2) {
$status = "In Progress"
$incompleteApps = $true
} elseif ($installationState -eq 3) {
$status = "Reboot Required"
$rebootRequired = $true
} else {
$status = "Installed"
}
# Output each app's status
Write-Host "App InstanceID: $instanceID - Installation State: $installationState - Status: $status"
}
# Summary check for reboot or incomplete apps
if ($rebootRequired -or $incompleteApps) {
if ($rebootRequired) {
Write-Host "`nA reboot is required - Device setup is incomplete."
}
if ($incompleteApps) {
Write-Host "`nSome apps are not installed or in progress - Device setup is incomplete."
}
return $true
} else {
Write-Host "All apps are installed and no reboot is required - Device setup is complete."
return $false
}
}
This script will check the installation state of the tracked applications
Wrapping Up
These PowerShell scripts offer a direct way to monitor and troubleshoot the ESP phases. By running these checks, you can pinpoint where IME might be getting stuck in the Account Setup phase, helping you resolve ESP issues without extensive guesswork
Here’s a thought: you can even combine these scripts to build your own requirement rule for Win32 apps. Doing so allows you to make them applicable only if the device is in provisioning mode. Or should we do that for you? Let us know!