Over time, I’ve tested many different setups for VMware Horizon environments — some more successful than others. But after countless rebuilds, troubleshooting sessions, and user feedback, I finally landed on what I consider my ideal configuration: a Horizon environment powered by FSLogix profiles and Entra Hybrid Joined virtual machines.
In this post, I’ll walk through how I’ve structured the configuration, step by step:
- Building the Horizon Master Image – the base OS, optimizations, and agents I include.
- Joining to Entra Hybrid – the configuration details that ensure smooth sign-in and device registration.
- Configuring the Desktop Pools – settings that keep login performance and flexibility balanced.
- Setting Up FSLogix Profiles – how I handle profile containers, exclusions, and storage.
- Final Tweaks and Recommendations – small but impactful adjustments that make the whole environment stable and fast.
- Sources
⚠️ Disclaimer:
I’m not a certified VMware professional. Everything described in this post is based on my own hands-on experience and the configuration that has proven reliable in my environment.
These recommendations come from extensive testing, trial and error, and real-world practice — not from official VMware training or certification materials.
Most of the configurations and procedures were developed by following tutorials, community discussions, and official guides or knowledge base articles from VMware, Microsoft, and other trusted sources.
All referenced sources can be found at the end of this post.
1. Building the Horizon Master Image
For the base image, we start with a clean, patched Windows 11 Enterprise image. After joining it to the domain, we install the following key components:
- Omnissa Horizon Agent (selecting only the features needed for my pools)
- FSLogix Apps
- Latest Windows Updates
- VMware Tools
💡 Tip:
Since the switch to Omnissa Horizon licensing, the Helpdesk Plugin is now included in the standard license package. That means you can enable it directly during the setup process without needing any additional licenses. Make sure to activate it in the Omnissa Horizin Agent configuration — it’s a powerful tool for monitoring user sessions and troubleshooting performance issues.
At the time of writing this post, our environment is running:
VMware Tools: 12.5.3 (Build 24819442)
Omnissa Horizon Agent: Version 2506
In previous years, we used the VMware OS Optimization Tool (OSOT) to remove unnecessary services and scheduled tasks from the master image. However, after experiencing a few stability issues with certain applications, we decided to move away from OSOT. Instead, we created a small custom cleanup script that we run right before shutting down the master image and taking a snapshot.
Our current script contains the following tasks:
- Delete temporary files
- Delete downloads of the currently logged-in user
- Empty Recycle Bin
- Start Disk Cleanup
- Reset network settings
- Optimize storage (optional, may take a while)
start cleanup.ps1
@echo off
:: Enable UTF-8 console output
chcp 65001 >nul
:: Start cleanup.ps1 with PowerShell in bypass mode
set SCRIPT=%~dp0cleanup.ps1
powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT%"
pause
cleanup.ps1
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$OutputEncoding = [System.Text.Encoding]::UTF8
Write-Host "Starting cleanup..." -ForegroundColor Cyan
# Delete temporary files
Write-Host "Deleting temporary files..." -ForegroundColor Yellow
Get-ChildItem -Path "C:\Windows\Temp" -Recurse -Force -ErrorAction SilentlyContinue | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
# Delete downloads of the currently logged-in user
Write-Host "Deleting Downloads folder of the logged-in user..." -ForegroundColor Yellow
$downloadPath = Join-Path $env:USERPROFILE "Downloads"
if (Test-Path $downloadPath) {
Get-ChildItem -Path $downloadPath -Recurse -Force -ErrorAction SilentlyContinue | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
Write-Host "Downloads from $downloadPath have been cleaned." -ForegroundColor Green
} else {
Write-Host "No Downloads folder found." -ForegroundColor Red
}
# Empty Recycle Bin
Write-Host "Emptying Recycle Bin..." -ForegroundColor Yellow
Clear-RecycleBin -Force -ErrorAction SilentlyContinue
# Start Disk Cleanup
Write-Host "Starting automatic Disk Cleanup..." -ForegroundColor Yellow
Start-Process -FilePath "cleanmgr.exe" -ArgumentList "/sagerun:1" -Wait
# Reset network settings
Write-Host "Network: Releasing IP addresses and flushing DNS cache..." -ForegroundColor Yellow
ipconfig /release
ipconfig /flushdns
# Optimize storage (optional, may take a while)
Write-Host "Performing storage optimization..." -ForegroundColor Yellow
compact.exe /compactos:always
Write-Host "Cleanup completed!" -ForegroundColor Green
2. Preparing the Horizon Master Image for Entra Hybrid Join
Before finalizing the hybrid join, we also onboarded our Horizon master image to Microsoft Defender for Endpoint to ensure every virtual desktop instance reports correctly to the security portal once it’s deployed.
We followed Microsoft’s official steps for onboarding non-persistent VDI environments, as described in their documentation:
🔗 Configure endpoints for VDI in Microsoft Defender for Endpoint
In our case we followed the “Single Entry Path”. Here’s a summary of the process we implemented:
Download the VDI onboarding package
From the Microsoft Defender portal, navigate to
Settings → Endpoints → Device Management → Onboarding.
Choose the correct operating system, select VDI onboarding scripts for non-persistent endpoints, and then download the .zip package.
Extract and copy the scripts
Unzip the package and copy its contents to:
C:\Windows\System32\GroupPolicy\Machine\Scripts\Startup
If the folder isn’t visible, make sure hidden files and folders are enabled in File Explorer.
Copy the extracted files
Copy both Onboard-NonPersistentMachine and WindowsDefenderATPOnboardingScript.
Register the Startup Script via Group Policy
Open the Group Policy Editor (gpedit.msc) and navigate to:Computer Configuration → Windows Settings → Scripts → Startup
Then: Use the PowerShell Scripts tab and add Onboard-NonPersistentMachine.ps1
(There’s no need to specify the other file, as it’s triggered automatically.)
⚙️ Important:
This Group Policy should be applied only to the Organizational Unit (OU) that contains your Instant Clone desktops — not to the master image or any template VM.
The master image must remain clean and unregistered so that every new clone performs its own onboarding during first boot.
Applying the policy to the master image could cause duplicate device entries or stale registrations in Microsoft Defender for Endpoint and Microsoft Entra ID.
3. Configuring the Desktop Pools
For our deployment, we decided to use Instant Clones to ensure fast provisioning, easy updates, and minimal administrative overhead.
Below is a summary of the key pool settings we applied:
| Setting | Value / Configuration | Description / Purpose |
|---|---|---|
| Guest Customization | ClonePrep | Automates domain join and machine identity generation for each clone. |
| View Storage Accelerator | Enabled | Improves read performance and reduces I/O load on the datastore. |
| Allow reuse of existing computer accounts | Enabled | Prevents unnecessary AD object recreation and supports stable hybrid join behavior. |
| Snapshot Update Cycle | Every 7 days | Ensures regular image refresh and deployment of updates. |
| Cluster / Host | vSAN-backed cluster | Dedicated to Horizon VDI workloads for optimized performance. |
| Pool Type | Instant Clone Desktop Pool | Provides fast, space-efficient, and easily managed VMs. |
| vTPM | Enabled | Required for Windows 11 compatibility and enhanced security. |
| Action at Logoff | Delete and recreate VM | Guarantees a clean non-persistent desktop for every session. |
| Allow users to restart their desktops | No | Prevents manual restarts that could interrupt provisioning cycles. |
| Session Type | Desktop | Standard virtual desktop deployment type. |
| Display Protocol | Horizon Blast (default) | Offers modern, high-performance graphics streaming. |
| Session Collaboration | Disabled | Restricts users from sharing desktop sessions. |
| Allow multiple client sessions per user | No | Each user receives a unique, isolated desktop. |
| Allow PCoIP Ultra | No | Environment standardized on Horizon Blast only. |
| 3D Renderer | Managed via vSphere Client | Configured depending on workload and GPU availability. |
| Remote Power Policy | Always On | Keeps desktops ready for quick connection. |
| Disconnect Session Timeout | 480 minutes | Controls session persistence for idle users. |
| Computer Naming Pattern | W11-vPC-xxx{n:fixed=3} | Ensures consistent and predictable computer naming. |
| VM Access Policy | Block direct vSphere access | Prevents administrators from connecting directly to the VM console, improving security. |
4. Setting Up FSLogix Profiles
To handle user profile roaming in our Horizon environment, we use FSLogix Profile Containers. This provides a fast, reliable, and seamless profile experience for users logging into non-persistent Instant Clone desktops.
The FSLogix profile containers are stored on a Windows file server located on the same vSAN storage as the Horizon virtual machines.
This ensures minimal latency between desktop sessions and the profile store, improving both login times and session responsiveness.
Each user profile is mounted dynamically at logon as a VHDX container, giving the user a persistent experience while keeping the underlying desktop non-persistent.
All FSLogix settings are applied through Group Policy Objects (GPOs) to guarantee consistent configuration across all virtual desktops.
Below is a summary of our applied policy configuration, based on the settings shown in the screenshot:
General FSLogix Policies
GPO Path:Computer Configuration → Administrative Templates → FSLogix
| Policy Name | Setting | Description |
|---|---|---|
| Cleanup Invalid Sessions | Enabled | Ensures stale FSLogix sessions are cleaned up automatically. |
| Roam Recycle Bin | Disabled | Keeps the Recycle Bin local to reduce container size. |
| VHD Compact Disk | Enabled | Compacts profile containers automatically to reclaim disk space. |
FSLogix / ODFC Containers
GPO Path:Computer Configuration → Administrative Templates → FSLogix → ODFC Containers
| Policy Name | Setting | Description |
|---|---|---|
| Include Office Activation | Disabled | Uses standard Microsoft 365 activation instead of FSLogix ODFC. |
FSLogix / Profile Containers
GPO Path:Computer Configuration → Administrative Templates → FSLogix → Profile Containers
| Policy Name | Setting | Description |
|---|---|---|
| Enabled | Enabled | Activates FSLogix Profile Container functionality. |
| Initial AppX Packages | Enabled | Ensures modern Windows apps initialize correctly. |
| Is Dynamic (VHD) | Enabled | Creates containers with dynamic disk sizing. |
| Keep Local Directory (after logoff) | Disabled | Deletes local profile remnants after logoff. |
| Outlook Cached Mode | Enabled | Keeps Outlook in Cached Exchange Mode within the profile container. |
| Redirect Type | Enabled | Standard FSLogix redirection behavior. |
| Remove Orphaned OST Files on Logoff | Enabled | Deletes orphaned OST files to prevent corruption. |
| Roam Identity | Enabled | Roams user identity (SID consistency). |
| Roam Search | Disabled | Keeps Windows Search index local for better performance. |
| Set Temp Folders to Local Path | Enabled | Redirects TEMP, TMP, and INetCache folders to the local drive. |
| Size in MBs | Enabled → 50,000 MB | Limits container size to 50 GB. |
| VHD Locations | Enabled → \\<fileserver>\dfs\FSLogix | Defines the path where FSLogix VHD/VHDX containers are stored. |
| Redirection XML Source Folder | Enabled → \\<fileserver>\software$\FSLogix | Location of the XML redirection configuration. |
FSLogix / Profile Containers / Container and Directory Naming
GPO Path:Computer Configuration → Administrative Templates → FSLogix → Profile Containers -> Container and Directory Naming
| Policy Name | Setting | Description |
|---|---|---|
| Volume Type (VHD or VHDX) | Enabled → VHDX | Uses VHDX for better performance and resilience. |
To further refine our FSLogix configuration, we created a custom redirections.xml file that we linked through Group Policy (configured underComputer Configuration → Administrative Templates → FSLogix → Profile Containers → Redirection XML Source Folder).
This XML file fine-tunes what parts of the user profile are excluded or included in the FSLogix container. The goal is to reduce container size, speed up logons, and avoid storing volatile authentication and cache data that should remain local.
We built the XML based on Microsoft’s official recommendations (Device identity and desktop virtualization) and several practical adjustments for hybrid-joined Horizon environments.
Below is the version we currently use:
<?xml version="1.0" encoding="UTF-8"?>
<FrxProfileFolderRedirection ExcludeCommonFolders="0">
<Excludes>
<!-- Browser Cache -->
<Exclude Copy="0">AppData\Local\Microsoft\Edge\User Data\Default\Cache</Exclude>
<!-- Microsoft Identity + Token Broker (AAD + M365 Sign-in) -->
<Exclude Copy="0">AppData\Local\Microsoft\OneAuth</Exclude>
<Exclude Copy="0">AppData\Local\Microsoft\TokenBroker</Exclude>
<Exclude Copy="0">AppData\Local\Microsoft\IdentityCache</Exclude>
<Exclude Copy="0">AppData\Local\Microsoft\Office\16.0\Identity</Exclude>
<Exclude Copy="0">AppData\Local\Packages\Microsoft.AAD.BrokerPlugin_cw5n1h2txyewy</Exclude>
<Exclude Copy="0">AppData\Local\Packages\Microsoft.Windows.CloudExperienceHost_cw5n1h2txyewy</Exclude>
<!-- Office Authentication Caches -->
<Exclude Copy="0">AppData\Local\Microsoft\CredentialManager</Exclude>
<Exclude Copy="0">AppData\Roaming\Microsoft\Credentials</Exclude>
<!-- Registry Keys -->
<Exclude Copy="0">HKEY_CURRENT_USER\SOFTWARE\Microsoft\IdentityCRL</Exclude>
<Exclude Copy="0">HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\AAD</Exclude>
<Exclude Copy="0">HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WorkplaceJoin</Exclude>
<Exclude Copy="0">HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows NT\CurrentVersion\TokenBroker</Exclude>
</Excludes>
<Includes>
<!-- Oracle/Sun Java -->
<Include Copy="3">AppData\LocalLow\Sun\Java\Deployment\security</Include>
<!-- Microsoft DPAPI Keys (important for password and M365 login persistence) -->
<Include Copy="3">AppData\Roaming\Microsoft\Protect</Include>
<!-- Edge User Data: keeps login data and local state -->
<Include Copy="3">AppData\Local\Microsoft\Edge\User Data</Include>
</Includes>
</FrxProfileFolderRedirection>
After setting up FSLogix and completing the hybrid join preparation, we added a few final optimizations to make the entire setup more stable and reliable in production.
5. Final Tweaks and Recommendations
To ensure that each Instant Clone correctly completes the Entra Hybrid Join process during its first startup, we created a dedicated GPO for the Horizon Agent.
This policy enforces that the Horizon Agent waits for the hybrid join process to finish before fully initializing the desktop session.
Without this setting, some machines could register inconsistently or fail to appear correctly in Microsoft Entra ID, especially in fast-provisioning environments like Instant Clones.
GPO Path:
Computer Configuration → Administrative Templates → Omnissa Horizon Agent Configuration / Agent Configuration
| Policy Name | Setting | Description |
|---|---|---|
| Configure Wait for Hybrid Join | Enabled | Horizon Agent delays login until Entra Hybrid Join is successfull |
By enabling this option, the Horizon Agent delays the login readiness state until the Entra Hybrid Join has completed successfully. This ensures that:
- The machine is properly registered in Entra ID before user login.
- Conditional Access and Defender onboarding policies are applied consistently.
- FSLogix profile mounting occurs in a stable identity state.
As described in the official Omnissa documentation (Support for Azure Active Directory),
Microsoft Entra Connect performs synchronization tasks on an hourly schedule.
Because of this, a freshly provisioned desktop can remain in a “Pending” state for some time before it becomes fully available in Entra ID.
In practice, this can result in longer startup times for new or refreshed desktops.
In our environment, the delay typically ranges between 2 and 10 minutes, depending on synchronization timing and infrastructure load.
However, once the join is completed, the machine remains stable and consistently recognized across all connected Microsoft services.
Sources
Blogs:
- Stephen Wagner: Hybrid Azure AD Join with Azure AD Connect for Non-Persistent VDI with VMware Horizon
- Borns IT- und Windows-Blog: Outlook Error [58tm1]
Microsoft:
- Onboard non-persistent virtual desktop infrastructure (VDI) devices in Microsoft Defender XDR
- Device identity and desktop virtualization
- Install FSLogix applications
Omnissa:

Leave a Reply