- Absolute Secure Access requires a SCEP certificate to connect
- Zscaler ZCC needs user authentication to enroll
- Both applications attempt to connect immediately upon installation
- Neither can succeed during Device ESP because:
- No user context exists
- No SCEP certificate has been issued
- No user credentials are available
The result? The Autopilot Enrollment Status Page (ESP) hangs indefinitely.
The Solution
Install both applications during Device ESP but prevent them from attempting to connect. After the user logs in and certificates are available, a one-time script connects both services in the correct sequence.
Step 1: Identify Service Names
Before implementing anything, I need to identify the exact service names, to ascertain these names run this on a test machine where both products are installed:
# Find Absolute/NetMotion services
Get-Service | Where-Object {
$_.DisplayName -like "*Absolute*" -or
$_.DisplayName -like "*NetMotion*" -or
$_.Name -like "nm*" -or
$_.Name -eq "MESSERV"
} | Format-Table Name, DisplayName, Status
# Find Zscaler services
Get-Service | Where-Object {
$_.DisplayName -like "*Zscaler*" -or
$_.Name -like "ZSA*"
} | Format-Table Name, DisplayName, Status
Common service names I've encountered:
- Absolute:
MESSERV,nmproxy,mobilityservice - Zscaler:
ZSATunnel,ZSAService
Step 2: Configure Applications in Intune for ESP
Adding to Enrollment Status Page (ESP)
- Navigate to Devices > Enrollment > Windows enrollment > Enrollment Status Page
- Select your ESP profile (or create one)
- Under Settings, ensure:
- Show app and profile configuration progress: Yes
- Block device use until all apps and profiles are installed: Yes (for Device phase)
Deploy Applications as Required During Device Phase
For each application in Intune:
- Go to Devices > Windows > Windows Apps
- Select your application (MSI or Win32)
- Under Properties > Assignments:
- Add group: Select your Autopilot devices group
- Mode: Required
- End user notifications: Hide all toast notifications
- Availability: As soon as possible
- Installation deadline: As soon as possible
- CRITICAL: Under "Assignment filters", ensure it's set to Device context, not user
This ensures both applications install during the Device ESP phase.
Step 3: Disable Services Post-Installation
Since the installation parameters for every organization can differ, you will need to disable the services immediately after installation.
Deploy this as a PowerShell script that runs in System context during device phase:
Script : Disable-VPNServices.ps1
# Disable-VPNServices.ps1
# Runs in SYSTEM context during device configuration
$logPath = "C:\Windows\Temp\Disable_VPN_Services.log"
Start-Transcript -Path $logPath
Write-Host "Disabling VPN services to prevent connection attempts during ESP"
# Disable Absolute Secure Access
$absoluteServices = @("MESSERV", "nmproxy", "mobilityservice")
foreach ($serviceName in $absoluteServices) {
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
if ($service) {
Write-Host "Found Absolute service: $serviceName"
Set-Service -Name $serviceName -StartupType Manual
Stop-Service -Name $serviceName -Force -ErrorAction SilentlyContinue
Write-Host "Service $serviceName disabled"
}
}
# Disable Zscaler ZCC
# Note: Even with uninstall password protection, services can be managed
$zscalerServices = @("ZSATunnel", "ZSAService")
foreach ($serviceName in $zscalerServices) {
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
if ($service) {
Write-Host "Found Zscaler service: $serviceName"
Set-Service -Name $serviceName -StartupType Manual
Stop-Service -Name $serviceName -Force -ErrorAction SilentlyContinue
Write-Host "Service $serviceName disabled"
}
}
# Additional safeguard: Disable Zscaler network adapter binding
try {
Get-NetAdapterBinding -ComponentID "ZS_ZAPPRD" -ErrorAction Stop |
Disable-NetAdapterBinding -ComponentID "ZS_ZAPPRD"
Write-Host "Disabled Zscaler network adapter binding"
} catch {
Write-Host "Zscaler network adapter binding not found or already disabled"
}
Write-Host "VPN services disabled successfully"
Stop-Transcript
Deploy this script in Intune:
- Devices > Scripts and remediations > Platform scripts
- Run as: System
- Run script in 64-bit PowerShell: Yes
- Assign to: Autopilot devices group
Step 4: Create the Post-Login Connection Script
This script runs once after the user logs in to establish VPN connections.
Connect-VPNServices-RunOnce.ps1
# Connect-VPNServices-RunOnce.ps1
# Runs ONCE in user context after first login
$logPath = "C:\Windows\Temp\VPN_UserSetup_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
Start-Transcript -Path $logPath
# Check if already run - this makes it run once only
$markerFile = "$env:APPDATA\VPNSetupComplete.flag"
if (Test-Path $markerFile) {
Write-Host "VPN setup already completed for this user"
Stop-Transcript
exit 0
}
Write-Host "Starting one-time VPN connection setup for user: $env:USERNAME"
Write-Host "Time: $(Get-Date)"
# Wait for user profile to stabilize
Write-Host "Waiting 30 seconds for user profile initialization..."
Start-Sleep -Seconds 30
# Step 1: Verify SCEP certificate is present
Write-Host "Checking for user certificate..."
$certFound = $false
$maxAttempts = 12
for ($i = 1; $i -le $maxAttempts; $i++) {
$certs = Get-ChildItem Cert:\CurrentUser\My -ErrorAction SilentlyContinue
# Adjust this based on your certificate template
# Look for certificates with Client Authentication capability
$userCert = $certs | Where-Object {
$_.EnhancedKeyUsageList.FriendlyName -contains "Client Authentication" -and
$_.Subject -match $env:USERNAME
}
if ($userCert) {
Write-Host "Certificate found: $($userCert.Subject)"
$certFound = $true
break
}
Write-Host "Certificate not found, attempt $i of $maxAttempts"
if ($i -lt $maxAttempts) {
Start-Sleep -Seconds 10
}
}
if (-not $certFound) {
Write-Error "User certificate not found after $maxAttempts attempts"
Write-Host "VPN setup will retry on next login"
Stop-Transcript
exit 1
}
# Step 2: Start Absolute Secure Access
Write-Host "`nStarting Absolute Secure Access service..."
$absoluteStarted = $false
$absoluteServices = @("MESSERV", "nmproxy", "mobilityservice")
foreach ($serviceName in $absoluteServices) {
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
if ($service) {
Write-Host "Configuring service: $serviceName"
Set-Service -Name $serviceName -StartupType Automatic
Start-Service -Name $serviceName -ErrorAction SilentlyContinue
# Wait for service to fully start
$timeout = 30
$elapsed = 0
while (($service.Status -ne 'Running') -and ($elapsed -lt $timeout)) {
Start-Sleep -Seconds 2
$elapsed += 2
$service.Refresh()
}
if ($service.Status -eq 'Running') {
Write-Host "Service $serviceName is running"
$absoluteStarted = $true
}
}
}
if ($absoluteStarted) {
Write-Host "Absolute Secure Access started, waiting 20 seconds for connection..."
Start-Sleep -Seconds 20
} else {
Write-Warning "Could not start Absolute Secure Access service"
}
# Step 3: Start Zscaler ZCC
Write-Host "`nStarting Zscaler Client Connector..."
$zscalerServices = @("ZSATunnel", "ZSAService")
foreach ($serviceName in $zscalerServices) {
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
if ($service) {
Write-Host "Configuring service: $serviceName"
Set-Service -Name $serviceName -StartupType Automatic
Start-Service -Name $serviceName -ErrorAction SilentlyContinue
# Wait for service to start
$timeout = 30
$elapsed = 0
while (($service.Status -ne 'Running') -and ($elapsed -lt $timeout)) {
Start-Sleep -Seconds 2
$elapsed += 2
$service.Refresh()
}
if ($service.Status -eq 'Running') {
Write-Host "Service $serviceName is running"
}
}
}
# Re-enable Zscaler network adapter binding if it was disabled
try {
Get-NetAdapterBinding -ComponentID "ZS_ZAPPRD" -ErrorAction Stop |
Enable-NetAdapterBinding -ComponentID "ZS_ZAPPRD"
Write-Host "Re-enabled Zscaler network adapter binding"
} catch {
Write-Host "Zscaler network adapter binding not found or already enabled"
}
# Step 4: Create marker file to indicate completion
Write-Host "`nCreating completion marker..."
New-Item -Path $markerFile -ItemType File -Force | Out-Null
Write-Host "VPN setup completed successfully at $(Get-Date)" | Out-File $markerFile
Write-Host "`nVPN connection sequence completed"
Write-Host "Both services are now set to start automatically on future logins"
Stop-Transcript
# Display notification to user (optional)
$notification = New-Object System.Windows.Forms.NotifyIcon
$notification.Icon = [System.Drawing.SystemIcons]::Information
$notification.BalloonTipTitle = "VPN Setup Complete"
$notification.BalloonTipText = "Your VPN connections have been configured successfully."
$notification.Visible = $true
$notification.ShowBalloonTip(5000)
Start-Sleep -Seconds 5
$notification.Dispose()
exit 0
Step 5: Deploy the Run-Once Script in Intune
To ensure this script runs once for each user after login, you may need to change the name and descriptions to your organisational requirements:
-
Navigate to Devices > Scripts and remediation's > Platform scripts
-
Click Add > Windows 10 and later
-
Basics:
- Name:
VPN Post-Login Setup - Run Once - Description:
Connects VPN services after user first login
- Name:
-
Script settings:
- Script location: Upload
Connect-VPNServices-RunOnce.ps1 - Run this script using the logged on credentials: Yes (Critical - must be user context)
- Enforce script signature check: No
- Run script in 64 bit PowerShell Host: Yes
- Script location: Upload
-
Assignments:
- Assign to: Your Autopilot users group (not devices)
- Filter: None needed
-
Review + add
The script checks for a marker file in the user's AppData folder, ensuring it only runs once per user.
Important Considerations
ZCC Service Password Protection
If you have deployed an uninstall password protection enabled, this should not affect the script as the services can be stopped and the service status updated with Powershell, the password should only prevents unauthorised GUI-based actions (logout, uninstall, disable)
Certificate Detection
The script looks for certificates with Client Authentication capability, you can also adjust the filter based on your specific certificate template:
# Example: If your certs have a specific issuer
$userCert = $certs | Where-Object {
$_.Issuer -match "<certificate-issuer>" -and
$_.EnhancedKeyUsageList.FriendlyName -contains "Client Authentication"
}
Timing
The script includes deliberate waits:
- 30 seconds after login (profile stabilization)
- Up to 2 minutes for certificate arrival
- 20 seconds after Absolute starts (connection establishment)
These can be adjusted based on your environment, just change the code.
Verification
After implementation, check these logs, if you get errors you may need to adapt the script to your individual organisational requirements:
C:\Windows\Temp\Disable_VPN_Services.log- Service disable during ESPC:\Windows\Temp\VPN_UserSetup_*.log- User connection script%APPDATA%\VPNSetupComplete.flag- Completion marker- Event Viewer > Applications and Services > Microsoft > Windows > DeviceManagement-Enterprise-Diagnostics-Provider
Conclusion
This approach solves the Autopilot deadlock by:
- Installing both VPN clients during Device ESP (as required apps)
- Immediately disabling their services to prevent connection attempts
- Running a one-time script after user login to:
- Wait for the SCEP certificate
- Start services in the correct order
- Set them to automatic for future logins
The key insight was recognizing that installation and connection must happen at different deployment phases. The Device ESP completes successfully because no connections are attempted, and the user experience is seamless with automatic VPN establishment after first login.