Using Windows Server 2025 in a Horizon Brownfield Setup (Yes, You Can)

.

Attention, I am experiencing some anomalies after activating the hybrid environment… I advise you to wait for news before proceeding.

LDAP replication fix: The April 8, 2025 update (KB5055523) introduced an LDAP replication issue when Windows Server 2025 is mixed with older Windows versions. The November 11, 2025 patch (kb5068861) resolves this issue

Hey, good news!

As of early August 2025, Horizon now supports Windows Server 2025 in brownfield environments. If you’re already running Horizon and thinking about tossing in a Server 2025 box, here’s the breakdown: what you need, what to tweak, and some handy commands to make it happen.

All information is taken from the official Omnissa KB (Windows 2025 support for Horizon brownfield installations (6000960) )

A jump into the past

Before… when trying to install the Connection Server Replica role on Windows Server 2025 and trying to insert it into a pod containing Windows Server 2016, 2019, and 2022, you would get this error:

A screenshot of a computer error AI-generated content may be incorrect.

.

Requirements

• Horizon version: Must be Horizon 8 v2503 or later. This is what enables full support for Windows Server 2025 in brownfield setups
• Windows Server Update

Ensure all Connection Server machines within the pod are updated with the Microsoft Windows 2025 February update or later release (for more details, check the official Omnissa KB)

All Windows 2025 machines hosting the Connection Server must have the Microsoft November 11, 2025 patch (or later) installed. This is required for:

  1. AD LDS functional level update: Without the required patch, updating the AD LDS functional level will cause the AD LDS instance to fail. Refer to Microsoft’s documentation: AD LDS service startup fails if updated without required patches.
  2. LDAP replication fix: The April 8, 2025 update (KB5055523) introduced an LDAP replication issue when Windows Server 2025 is mixed with older Windows versions. The November 11, 2025 patch (kb5068861) resolves this issue
• AD LDS Functional Level: This is the kicker:
◦ Windows Server 2025 uses AD LDS functional level 7 by default
◦ Older WS versions (2016–2022) often default their AD LDS to level 2, which is incompatible with WS 2025 replicas

Fresh installs on WS 2025 with Horizon v2503 will default to FL-7 for AD LDS. But if you’re joining WS 2025 to older Horizon Connection Servers that haven’t had their FL bumped, you’ll hit a replica error (“minimum level required by this version… is 7”)

Omnissa has released a script to upgrade FL to version 7 (Supported from Windows Server 2016 onwards)

.

.

Step-by-Step: What to Do

1. Verify you’re on Horizon 8 v2503+:
◦ Go to your Connection Server > About > Confirm version.
2. Check the current AD LDS functional level and Windows Server patch
◦ Download the PowerShell script (UpdateFunctionalLevel-v1.ps1) attached to the Omnissa KB and run it on all Connection Servers
◦ The script can be checked:
1. Windows Operating System and Patch Level of the Connection Server machines
2 . Current Functional Level of local and Global AD LDS
3. Upgrade the FL to 7 (Remember to create a VM snapshot and do a backup….)
◦ The same Omnissa script can upgrade the FL from 2 to 7:
1. Update the Functional Level of the current Pod
2 .Update the Functional Level of CPA
A screen shot of a computer AI-generated content may be incorrect.

.

All check and upgrade steps in my YouTube video:

https://www.youtube.com/watch?v=aR2qvvlY9gc

.

.

.

What You’re Looking At

Component

Requirement

Horizon version

v2503 or later

AD LDS Functional Level

Must be 7 (WS 2025 default), not 2

Action Required

Check and update FL before pod join

.

A Quick Tech Tip

“Horizon 2503 or later will set functional level to 7 for fresh install of the connection server on Windows 2025 and to 2 for anything before Windows 2025.”
— Omnissa internal forum 

So yeah—if you’ve done a fresh install on WS 2025 after Horizon 2503, you’re golden. But mixing in older AD LDS configs? That’s when things break… but this post is the answer.

.

Using Windows Server 2025 in a Horizon Brownfield Setup (Yes, You Can)

Logs Don’t Lie: Why You Need Syslog Enabled on Omnissa Access SaaS

A diagram of a server AI-generated content may be incorrect.

If you’re running Omnissa Access SaaS and you haven’t enabled syslog yet, here’s your gentle-but-firm nudge: do it now. No, seriously. Your SIEM is hungry, and syslog is the buffet.

Let’s dig into why syslog matters, and how you can set it up in less time than it takes to reboot a stubborn printer.

.

Why Should I Enable Syslog?

You might think, “Access logs are already there in the console. Isn’t that enough?”
Short answer:
Nope.

Longer answer:

• Centralized Security Monitoring: Syslog lets you push logs to a SIEM (like Splunk or SYSLOG, I suppose that Omnissa increases the supported SIEM), helping you detect anomalies like brute force attacks, unusual login patterns, or rogue authentication attempts.
• Compliance & Auditing: GDPR, ISO 27001, HIPAA — they all love detailed, timestamped logs.
• Operational Insight: Know exactly who did what, where, and when — across all your users and apps.
• Forensics & Troubleshooting: Ever tried to investigate a login issue without logs? Exactly.

.

What Events Can I Capture?

Omnissa Access can emit audit events, system events, user authentication, and admin actions. Think:

• User logins (successful & failed)
• Policy evaluations
• App launches
• Admin config changes (Policies, Rule etc.)

All this, neatly packaged as syslog messages you can parse, alert on, or just hoard like a proper security engineer.

.

How to Enable Syslog in Omnissa Access SaaS

Requirement

–TLS connection
–Expose to internet our syslog or SIEM. (For now it is only possible configuration, OK this are a point of attention for the Security… but you can manage with firewall rule and other configuration to increase the security)

Setting up syslog in Omnissa Access SaaS is surprisingly painless.

1.Login to the Omnissa Access SaaS Admin Console
Navigate to the
Integrations section in the Omnissa Access SaaS Admin UI.
2.Go to SIEM
You’ll find the syslog settings under
SIEM

A screenshot of a computer AI-generated content may be incorrect.

3.Enable Syslog Forwarding
Toggle it
on, and enter your syslog destination (IP or FQDN), port, and protocol (TCP or UDP).

A screenshot of a computer AI-generated content may be incorrect.

Where

◦ Appname

A tag appends to the syslog raw

◦ Chose Facility
◦ Choose the Severity
Select which log levels
◦ Hostname
Currently the syslog server needs to be published on the internet (this might cause some headaches) in order for the Access SaaS solution to be able to send logs.
◦ TCP Port
◦ Client Certificate
◦ Client Private Key
◦ Syslog Certificate
4.Save & Monitor
Save your settings and check your syslog server for incoming logs. A quick
tcpdump or tail on your syslog endpoint can help confirm delivery.

.

Bonus Tip: Test It!

Try logging in as a test user, change a policy, or simulate a failed login — and watch the logs roll in. If your SIEM lights up, congrats — you’ve just levelled up your visibility game.

My syslog, in this case for the test, is a normal RSYSLOG installed on UBUNTU OS.

I can see a lot of information:

A screen shot of a computer screen AI-generated content may be incorrect.

Where can we see this action:

–LOGIN (Failed or Successful) -> Administrator account or user account and the type of login (MFA/Local Password …)
–LAUNCH -> Application launched and the name of the application/VDI.
–LOGOFF

And many other information like configuration changes, deleted or viewed objects (such as policies).

.

Pro Tips

• TCP with TLS over UDP for reliable, encrypted delivery. (it is a requirement)
• Use log tagging or filtering on the SIEM side to categorise Omnissa logs separately.
• Integrate with alerting tools like PagerDuty or Slack for real-time reactions.

.

🏁 Final Thoughts

Syslog isn’t just for compliance checkboxes — it’s your window into what’s happening inside Omnissa Access. Whether you’re defending against intrusions or just troubleshooting a login issue on a Friday at 5 PM, you’ll thank yourself for turning it on.

So go ahead — feed your SIEM. You know it’s hungry. 🍽️

.

Logs Don’t Lie: Why You Need Syslog Enabled on Omnissa Access SaaS

Securing Horizon Event DB to SQL Server with TLS

.

Because Who Likes Sniffers Anyway?

So you’ve installed Horizon (2503 is last version) and got your Event Database humming along on a shiny SQL Server. But hold on a sec — did you remember to lock down that traffic with TLS encryption? Or are you letting your event logs float around in plain text like it’s still 1999?

Let’s fix that.
Here’s how to set up
SSL/TLS encryption between Horizon and your SQL Server, with a proper certificate from your Microsoft CA, and make sure your event data isn’t the low-hanging fruit on your network.

.

 Why bother?

Because:

• Anyone with Wireshark can eavesdrop on your events and see user logins, VM power actions, etc.
• Your security team will buy you more coffee if you’re nice to them.

.

The Plan

1.Request & issue a certificate from your Microsoft CA infrastructure.
2.Install the certificate on your SQL Server.
3.Configure SQL Server to force encryption using that cert.
4.Enable Horizon to use SSL for SQL connection
5.Test it and sleep better.

.

1. Request a certificate from your Microsoft CA

On your SQL Server, create a certificate request:

Use certreq or just the MMC GUI.
Here’s the quick-and-dirty via MMC:

1.Open mmc.exe ➔ Add the Certificates snap-in for Computer account.
2.Right-click Personal ➔ Certificates ➔ All Tasks ➔ Request New Certificate.
3.Select your Active Directory Enrollment Policy.
4.Choose a template that includes “Server Authentication” EKU (typically Web Server template).
5.Fill in the common name (CN) with your SQL Server’s FQDN (must match exactly what clients connect to).

(Example: sql01.contoso.local)

6.Enable that private Key is exportable
7.Finish and you’re done. Your cert should show up in Personal ➔ Certificates.

.

2. Install, verify and assign right permission to the cert

Technically it’s already installed, but verify:

• It’s under Computer -> Personal ➔ Certificates on your SQL Server .
• It has Server Authentication (1.3.6.1.5.5.7.3.1) EKU.
• The private key is present (little key icon when you look at the cert).

.

Now we need to assign to the user that starts the SQL server service the permissions to read the private key.

A screenshot of a computer AI-generated content may be incorrect.

.

A screenshot of a computer screen AI-generated content may be incorrect.

.

3. Tell SQL Server to use it

Configure SQL Server

1.Open SQL Server Configuration Manager.
2.Go to SQL Server Network Configuration ➔ Protocols for MSSQLSERVER ➔ Properties ➔ Certificate tab.

A screenshot of a computer AI-generated content may be incorrect.

3.Select your cert from the dropdown.

.

A screenshot of a computer AI-generated content may be incorrect.

If it doesn’t show up:

• Check that CN matches the machine’s FQDN.
• Make sure it has Server Authentication EKU.
• Ensure it’s in LocalMachine\My (Personal store).

Force encryption (optional but recommended)

Still in Protocols for MSSQLSERVER ➔ Flags tab ➔ Set Force Encryption = Yes.

A screenshot of a computer AI-generated content may be incorrect.

.

Restart SQL Service

You knew this was coming:

Restart-Service MSSQLSERVER

A screenshot of a computer AI-generated content may be incorrect.

.

Testing time

Use SQL Server Management Studio (SSMS) to connect, then run:

SELECT session_id, encrypt_option

FROM sys.dm_exec_connections

WHERE session_id = @@SPID;

If encrypt_option says TRUE, congrats! 🎉

.

What about Horizon?

Enable SSL with modification in the ADAM DB pae-enableDbSSL and set it to 1

1. Start ADSI Edit

A screenshot of a computer AI-generated content may be incorrect.

2.Connect to the ADAM DB

A screenshot of a computer AI-generated content may be incorrect.

A screenshot of a computer AI-generated content may be incorrect.

Remember that the Distinguished Name is different if you use the OLD ADAM Schema or the new Schema (the DN indicated in the image is the new schema with the rebranding Omnissa)

3.Go to OU=Properties > OU=Global > CN=Common and set the pae-enableDbSSL flag to 1 .

A screenshot of a computer AI-generated content may be incorrect.

4. Restart the omnissa Horizon Connection Server Service

A screenshot of a computer AI-generated content may be incorrect.

.

When you configure your Event Database settings in Horizon Administrator, it’ll negotiate TLS automatically if your SQL Server is set up for it.

Just make sure:

• Horizon connects via the FQDN matching the cert CN.
• The client OS trusts your CA (install the CA root cert if needed).

.

Done! Enjoy encrypted peace of mind.

Now your Horizon events are zipped up nice and secure in transit.
No more plain-text passwords, no more nosey packet sniffers. You
can go brag to your security team and earn those extra donuts.

.

.

Bonus topic!

Now I check the traffic from Horizon Connection Server and SQL Server

With Wireshark, we can check if the traffic is encrypted:

1. Install Wireshark and Npcap on the Windows server of one of your connection servers
2.Enable filter ((ip.src == <IP CS> && ip.dst == <IP SQL SERVER>)) || ((ip.src == <IP SQL SERVER> && ip.dst == <IP CS>))
3. Start the capture
4. Log in to the connection server and create an Event filter
5 . Check

A screenshot of a computer AI-generated content may be incorrect.

.

Whitout encryption

A screenshot of a computer AI-generated content may be incorrect.

With Encrypted

 

A screenshot of a computer AI-generated content may be incorrect.

Securing Horizon Event DB to SQL Server with TLS

Add icon to App Pool

 

# --- Step 1: Get admin credentials securely ---
$cred = Get-Credential
$domain = "yourdomain"

# --- Step 2: Build login payload ---
$loginBody = @{
    username = $cred.UserName
    password = $cred.GetNetworkCredential().Password
    domain   = $domain
} | ConvertTo-Json

# --- Step 3: API base URL and cert bypass for testing ---
$restApiBaseUrl = "https://horizon.domain.com/rest"
Add-Type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate,
                                          WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

# --- Step 4: Authenticate and get token ---
$tokenResponse = Invoke-RestMethod -Method POST -Uri "$restApiBaseUrl/login" -Body $loginBody -ContentType "application/json"
$token = $tokenResponse.access_token
$headers = @{ "Authorization" = "Bearer $token" }

# --- Step 5: Load and encode icon file ---
$iconFilePath = "C:\path\file.png"
$iconBytes = [System.IO.File]::ReadAllBytes($iconFilePath)
$base64Icon = [System.Convert]::ToBase64String($iconBytes)

# --- Step 6: Upload the icon ---
$iconBody = @{
    data = $base64Icon
    height = 256
    width = 256
} | ConvertTo-Json -Depth 2

$response = Invoke-RestMethod -Method POST -Uri "$restApiBaseUrl/inventory/v1/application-icons" -Headers $headers -Body $iconBody -ContentType "application/json"

# --- Step 7: Retrieve the icon ID from the uploaded base64 data ---
$iconId = ((Invoke-RestMethod -Method GET -Uri "$restApiBaseUrl/inventory/v1/application-icons/custom-icons" -Headers $headers -ContentType "application/json") | Select-Object data,id | Where-Object {$_.data -eq $base64Icon}).id

# --- Step 8: Get application ID by name (e.g., Notepad) ---
$appFilterJSON = @{
    type = "Equals"
    name = "name"
    value = "Notepad"
}
$appFilterURLEncoded = [System.Web.HttpUtility]::UrlEncode(($appFilterJSON | ConvertTo-Json -Depth 2 -Compress))
$appId = (Invoke-RestMethod -Method GET -Uri "$restApiBaseUrl/inventory/v4/application-pools?filter=$appFilterURLEncoded" -Headers $headers).id

# --- Step 9: Associate the custom icon with the application ---
$appIconAssocBody = @{
    application_pool_ids = @("$appId")
    icon_id = "$iconId"
} | ConvertTo-Json -Depth 2

Invoke-RestMethod -Method POST -Uri "$restApiBaseUrl/inventory/v1/application-pools/action/associate" -Headers $headers -Body $appIconAssocBody -ContentType "application/json"

 

Add icon to App Pool

Script to export and import Application Pools e i loro entitlements

 

# Script per esportare e importare Application Pools e le loro entitlements da un HCS ad un altro.
# Si basa sulle API REST di Omnissa (HCS) e richiede le credenziali di un utente con privilegi di amministratore.
# Il file JSON esportato contiene i pool e le entitlements associate, che possono essere importati in un altro HCS.
# Le API utilizzate sono documentate nella sezione "API Reference" della documentazione di Omnissa. 
# https://developer.omnissa.com/horizon-apis/
# https://retouw.nl/2021/10/02/horizon-rest-api-powershell-7-paging-and-filtering-with-samples/

# === LOGIN & TOKEN ===
function Get-HRToken {
    param([string]$Server, [string]$Domain, [string]$User, [string]$Password)
    $body = @{ domain=$Domain; username=$User; password=$Password } | ConvertTo-Json
    $uri  = "https://$Server/rest/login"   # POST /rest/login :contentReference[oaicite:0]{index=0}
    (Invoke-RestMethod -Method Post -Uri $uri -Body $body -ContentType 'application/json' `
                       -SkipCertificateCheck).access_token
}

# === UTILITY: rimuove campi read-only non clonabili ===
function Sanitize-Pool {
    param($Pool)
    $Pool | Select-Object * -ExcludeProperty id, avm_shortcut_id, global_application_entitlement_id
}


function Export-AppPoolsWithEntitlements {
    param(
        [string]$SrcServer,  [string]$Domain,
        [string]$User,       [string]$Password,
        [string]$OutFile 
    )

    $token = Get-HRToken $SrcServer $Domain $User $Password

    # Lista completa dei pool (max 1000) – GET /inventory/v4/application-pools :contentReference[oaicite:1]{index=1}
    $pools = Invoke-RestMethod -Method Get `
              -Uri "https://$SrcServer/rest/inventory/v3/application-pools?size=1000" `
              -Headers @{Authorization="Bearer $token"} -SkipCertificateCheck

    $export = foreach ($p in $pools) {
        # Entitlement del singolo pool – GET /entitlements/v1/application-pools/{id} :contentReference[oaicite:2]{index=2}
        $ents = Invoke-RestMethod -Method Get `
                -Uri "https://$SrcServer/rest/entitlements/v1/application-pools/$($p.id)" `
                -Headers @{Authorization="Bearer $token"} -SkipCertificateCheck

        [PSCustomObject]@{
            pool         = Sanitize-Pool $p
            entitlements = $ents.ad_user_or_group_ids
        }
    }

    $export | ConvertTo-Json -Depth 15 | Out-File $OutFile -Encoding UTF8
    Write-Host "✓ Esportati $($export.Count) pool in $OutFile"
}




function Import-AppPoolsWithEntitlements {
    param(
        [string]$DstServer,  [string]$Domain,
        [string]$User,       [string]$Password,
        [string]$JsonFile 
    )

    $token = Get-HRToken $DstServer $Domain $User $Password
    $data  = Get-Content $JsonFile | ConvertFrom-Json

    foreach ($item in $data) {
    Write-Host "Singolo item $item"
    $item.pool
    Read-Host -Prompt "Press Enter to continue"
        # 4.1  Crea il nuovo Application Pool – POST /inventory/v1/application-pools :contentReference[oaicite:3]{index=3}
        $bodyPool = $item.pool | ConvertTo-Json -Depth 15
        $newPool  = Invoke-RestMethod -Method Post `
                     -Uri "https://$DstServer/rest/inventory/v1/application-pools" `
                     -Headers @{Authorization="Bearer $token"} `
                     -ContentType 'application/json' -Body $bodyPool -SkipCertificateCheck 
        Write-Host "Pool creato $newPool"
        $nomepool = $($item.pool.name)
        Write-Host "Nome del application $nomepool"
        $poolid = Invoke-RestMethod -Method Get `
                     -Uri "https://hcs01.pollaio.lan/rest/inventory/v1/application-pools?filter=%7B%0A%09%22type%22%3A%20%22Equals%22%2C%0A%09%22name%22%3A%20%22name%22%2C%0A%09%22value%22%3A%20%22$nomepool%22%0A%7D" `
                     -Headers @{Authorization="Bearer $token"} `
                     -ContentType 'application/json' -SkipCertificateCheck
        $poolid
        $poolid.id

    Read-Host -Prompt "Press Enter to continue"
        Write-Host "✓ Creato pool '$($item.pool.name)' (nuovo id $($poolid.id))"

        # 4.2  Ripristina entitlement (se presenti) – POST /entitlements/v1/application-pools (bulk) :contentReference[oaicite:4]{index=4}
        if ($item.entitlements.Count) {
            $Psobj=New-Object -Type psobject
            $Psobj | Add-Member -MemberType NoteProperty -Name "id" -Value $poolid.id -Force
            $Psobj | Add-Member -MemberType NoteProperty -Name "ad_user_or_group_ids" -Value $item.entitlements -Force
            $entSpec ="["
            $entSpec += $Psobj | ConvertTo-Json
            $entSpec += "]"
        
            Invoke-RestMethod -Method Post `
                -Uri "https://$DstServer/rest/entitlements/v1/application-pools" `
                -Headers @{Authorization="Bearer $token"} `
                -ContentType 'application/json' -Body $entSpec -SkipCertificateCheck

            Write-Host "  └─► Entitlement ripristinati: $($item.entitlements.Count) SID - APP ID $($newPool.id)"
        }
    }
}
function DestListFarmsID {
    param(
        [string]$DstServer,  [string]$Domain,
        [string]$User,       [string]$Password,
        [string]$FarmDest
    )
    
    $token = Get-HRToken $DstServer $Domain $User $Password
    $FARMDETAILDST = Invoke-RestMethod -Method Get `
                -Uri "https://$DstServer/rest/inventory/v7/farms?filter=%7B%0A%09%22type%22%3A%20%22Equals%22%2C%0A%09%22name%22%3A%20%22name%22%2C%0A%09%22value%22%3A%20%22$FarmDest%22%0A%7D" `
                -Headers @{Authorization="Bearer $token"} `
                -ContentType 'application/json' 
    Write-host $FARMDETAILDST.id
}

function SourceListFarmsID {
    param(
        [string]$SrcServer,  [string]$Domain,
        [string]$User,       [string]$Password,
        [string]$FarmSrc
    )
$filterhashtable = [ordered]@{}
$filterhashtable.filters = @()
$userfilter= [ordered]@{}
$userfilter.add('type','Equals')
$userfilter.add('name','name')
$userfilter.add('value',$FarmSrc)
$filterhashtable.filters+=$userfilter
$filterflat = $filterhashtable | ConvertTo-Json -Compress
    $token = Get-HRToken $SrcServer $Domain $User $Password
    $FARMDETAILSRC = Invoke-RestMethod -Method Get `
               -Uri "https://$SrcServer/rest/inventory/v3/farms?$filterflat" `
                -Headers @{Authorization="Bearer $token"} `
                -ContentType 'application/json' -skipCertificateCheck
    Write-host $FARMDETAILSRC.id
}
######MAIN PROGRAM###
# Insert Source HCS
$SrcServer = Read-Host -Prompt "Insert Source HCS Server Name"
#$SrcServer = "hcs2111.pollaio.lan"
# Insert Dest HCS
$DstServer = Read-Host -Prompt "Insert Destination HCS Server Name"
#$DstServer = "hcs01.pollaio.lan"
#Insert Domain
$Domain = Read-Host -Prompt "Insert Domain Name (e.g. POLLAIO)"
#$Domain = "pollaio"
# Insert Path to JSON file
$JsonFile = Read-Host -Prompt "Insert Path to JSON file (e.g. c:\attimo\AppPoolsWithEntitlements.json)"
# Insert Credentials 
$Credentials = Get-Credential -Message "Insert Domain Credentials for $SrcServer and $DstServer"
$Username = $Credentials.UserName
$Pass = $Credentials.GetNetworkCredential().Password

# --- EXPORT ---
Export-AppPoolsWithEntitlements `
    -SrcServer $SrcServer -Domain $Domain `
    -User $Username -Password $Pass -OutFile $JsonFile

#
$FarmSrc = Read-Host -Prompt "Insert Source Farm Name"
$SFARM=SourceListFarmsID `
     -SrcServer $SrcServer -Domain $Domain `
    -User $Username -Password $Pass -FarmSrc $FarmSrc 6>&1

$SFARM


$FarmDest = Read-Host -Prompt "Insert Destination Farm Name"
$DFARM=DestListFarmsID `
     -DstServer $DstServer -Domain $Domain `
    -User $Username -Password $Pass -FarmDest $FarmDest 6>&1

$DFARM
(Get-Content -Path $JsonFile) -replace "$($SFARM)", "$($DFARM)" | Set-Content -Path $JsonFile

Read-Host -Prompt "WARNING!! If you import the application in the same HCS change the Name and diplay name in the Json file.  Press Enter to continue"

# --- IMPORT ---
Import-AppPoolsWithEntitlements `
    -DstServer $DstServer -Domain $Domain `
    -User $Username -Password $Pass -JsonFile $JsonFile

 

Script to export and import Application Pools e i loro entitlements

Simplifying App Volumes Management with Recovery Admin in Version 2503

Introduction
With the release of App Volumes 2503, Omnissa has introduced a powerful new feature: Recovery Admin. Designed with IT administrators in mind, this addition simplifies troubleshooting and provides a safer, more streamlined way to handle App Volumes environments during critical issues.

What is Recovery Admin?
Recovery Admin is a dedicated role introduced to improve operational resilience. It grants restricted access to App Volumes when standard administrators are locked out—typically in cases where directory services like Active Directory are down or misconfigured.

Think of it as a “break-glass” account: always available, highly secure, and essential when traditional access paths fail.

Key Capabilities

  • Read-Only Access: Recovery Admin can inspect configurations, assignment states, and errors—helpful for diagnostics without risking accidental changes.This account is strictly limited to managing domain configuration and administrator roles and is not associated with any domain.

  • No Directory Dependency: This role doesn’t rely on external identity providers. Credentials are defined during the initial setup, so access is always guaranteed.

  • Audit-Ready: All Recovery Admin activity is logged separately to maintain accountability.

How to Enable Recovery Admin

  1. Log in to the App Volumes Manager UI.

  2. Go to Configuration > Recovery Admin Settings.

  3. Define a strong password and keep it secure.

Once configured, the Recovery Admin account is ready for use when needed—no domain authentication required.

Use Case Scenario
Imagine your domain controller is unreachable and no administrators can log in. With Recovery Admin, you still have access to read logs, inspect app assignments, and validate system health—all without needing to restore full AD services immediately. It’s a crucial safety net.

We can login with Recovery Admin from UI

Add ?recovery_admin=true at the App Volumes Manager URL

and we check if we are in recovery mode with the yellow banner on the top of the UI.

Best Practices

  • Use Recovery Admin strictly for emergencies.

  • Limit access to trusted personnel.

  • Rotate credentials periodically.

  • Monitor audit logs for unusual activity.

Conclusion
Recovery Admin in App Volumes 2503 is a small but significant feature that enhances platform reliability and administrator confidence. Whether you’re running a single site or managing a distributed environment, having a backdoor to recover and investigate without risking configurations is a game-changer.

Image

Enhancing VDI Security and User Experience with FIDO2/WebAuthn and Omnissa Horizon

In today’s hybrid work environments, Virtual Desktop Infrastructure (VDI) solutions like Omnissa Horizon are critical to ensuring secure and flexible access to corporate resources. Yet, as we continue to shift towards distributed workforces, traditional authentication methods such as passwords and OTP tokens increasingly show their limitations — in both security and user experience.

This is where FIDO2/WebAuthn comes into play. By integrating FIDO2/WebAuthn into your Horizon deployment, you can deliver passwordless, phishing-resistant authentication that simplifies end-user access while dramatically improving security posture.

What is FIDO2/WebAuthn?

FIDO2 is an open standard developed by the FIDO Alliance and W3C. WebAuthn (Web Authentication) is the core API that enables browsers and web applications to leverage strong authenticators such as security keys, biometric devices (like fingerprint readers), or built-in platform authenticators (like Windows Hello or Apple Face ID).

In simple terms: it replaces passwords with secure, device-bound credentials that can’t be phished or reused.

Why combine FIDO2/WebAuthn with VMware Horizon?

Here are the key benefits for Horizon administrators and end users:

1. Stronger Security

Traditional passwords are susceptible to phishing, credential stuffing, and breaches. FIDO2/WebAuthn credentials are cryptographically unique and never leave the user’s device. Even if attackers obtain user data, they cannot reuse it to gain access.

2. Seamless User Experience

Users authenticate with something they have (a device) and something they are (biometrics) or something they know (PIN). The process is fast, intuitive, and eliminates password fatigue. No more forgotten passwords or frequent resets.

3. Simplified Endpoint Management

In distributed VDI environments, especially with BYOD policies, managing traditional credentials across devices is a challenge. FIDO2/WebAuthn allows users to authenticate securely from any compliant device, reducing helpdesk workload and administrative complexity.

4. Phishing Resistance

Unlike OTP or SMS-based 2FA, FIDO2/WebAuthn authentication is bound to the specific origin (domain). Credentials cannot be used on malicious lookalike websites, significantly reducing phishing attack vectors.

5. Future-Proof Compliance

Many modern security frameworks and regulatory guidelines encourage or require phishing-resistant MFA. Deploying FIDO2/WebAuthn puts your organization ahead of compliance mandates.

How does it work with Horizon?

With Horizon’s support for modern authentication protocols (including SAML and integration with identity providers that support FIDO2/WebAuthn), you can deploy passwordless authentication workflows seamlessly:

  • Users access Horizon Client or Workspace ONE with their FIDO2 device (security key, biometrics, or platform authenticator).
  • The identity provider (IdP) validates the FIDO2/WebAuthn credentials.
  • Once authenticated, users are securely provisioned into their Horizon VDI sessions.

No passwords exchanged. No phishable credentials. Just fast, secure, and frictionless access.

Ready to try it?

Integrating FIDO2/WebAuthn with VMware Horizon is a clear step toward a more secure, modern, and user-friendly VDI environment. Whether you’re looking to reduce helpdesk tickets, strengthen security, or improve user experience, this technology is worth exploring.

Now is the time to test and adopt FIDO2/WebAuthn.

(If you want to use FIDO2 for login to VDI, see my post VMware Workspace One Access, VMware Horizon, and FIDO2 device – BIOLNX)

Environment:

  • Horizon 2503
  • Yubikey
  • Windows 11 24H2 (Guest OS for VDI)
  • Firefox and Edge
  • Horizon GPO
  • Use https://webauthn.io for authentication test

0 – Configure an account on WebAuthn.io

Insert the Yubikey on a physical PC

Go to https://webauthn.io

Insert the username and set the preferred authentication method

Select a security key (Yubikey)

Enter the security key assigned to Yubikey

A finger pressing a usb drive into a computer AI-generated content may be incorrect.

1 – Check  Active Directory GPO

The default configuration for “Allow FIDO2 authenticator access” (under Agent Configuration) is Not Configured, and with this configuration, the FIDO2 WebAuthn is enabled.

2 – Test authentication 

Now we can connect to VDI and access with edge to https://webauthn.io

After selecting, authenticate in the next window, select the security key. (This window is up from your client (PC), not from VDI)

A finger pressing a usb drive into a computer AI-generated content may be incorrect.

Spoiler 1

If you want to disable this function, you need to set the Allow FIDO2 authenticator access GPO with the value “Disabled”

And when you try to connect

Spoiler 2

We can use the “FIDO2 allow list” GPO to select which browser to use with the WebAuthN and disable another browser.

For example, enable only Firefox application

In this video, I show you how FIDO2 WebAuthN works and how to exclude certain browsers

https://youtu.be/cWKBxt8BVXU

 

Enhancing VDI Security and User Experience with FIDO2/WebAuthn and Omnissa Horizon

How to Test an NVIDIA Video Card with Omnissa VDI: A How-To Guide

Have you just set up a VDI and want to see if your cool NVIDIA video card is doing its job? Don’t worry, I understand you. There’s nothing more frustrating than spending a fortune on hardware and then finding that the GPU sleeps while the processor does all of it. In this article, I explain in a simple (and fast) way how to test the Nvidia video card within a VDI.

Tested environment

NVIDIA Tesla M10 (not the latest model, but still supported by vSphere and NVIDIA and limited cost for my homelab)

vSphere 8.x (thanks to licenses as vExpert)

Omnissa Horizon 2503 (Thanks to licenses like Omnissa Tech Insider and Omnissa community)

Supermicro as HW

Guest OS Windows 11 2412

NVIDIA drivers installed on ESXi and Windows 11 guest VDI

Configuring the NVIDIA Card: Configuring the Card as Direct Shared

Virtual machine HW: Add the desired cut of the NVIDIA card size

In this link, there are more details on the NVIDIA cards’ size

Virtual GPU Software

First question: But can it be done?

Yes, and it must be done. With modern VDI – we are talking about environments such as those managed with Omnissa (formerly VMware EUC) – it is possible to assign GPUs to virtual machines using technologies such as vGPUs. The problem? It is not always clear if the GPU is really used.

Step 1: Verify that the GPU is mapped

First of all, log in to your VDI (Windows or Linux, little changes) and open the Task Manager or a terminal. In Windows, go to GPU > Performance. If you see “NVIDIA” somewhere, you’re on the right track.

Or use nvidia-smi (yes, it also works in VDI if the drivers are in good shape). It will tell you everything: from the memory used to the temperature, passing through the active processes. It’s like the GPU use.

Step 2: Make the video card work

At this point, test it seriously. What?

  • Open an app that uses graphics acceleration (e.g., a CAD, a 4K video, or even just YouTube at full quality).

A screenshot of a computer AI-generated content may be incorrect.

A screenshot of a computer AI-generated content may be incorrect.

  • On Linux or more closed environments, you can use command-line tools or scripts to make test renders.

During these tests, keep an eye on nvidia-smi or the Task Manager. If the GPU stays at 0%, there’s something wrong (spoiler: it’s often a driver or vGPU assignment issue).

Step 3: Monitor WHIT style

For continuous monitoring, you can install the NVIDIA System Management Interface or use third-party tools built into the Omnissa environment, such as those included in Horizon (Horizon Performance Tracker) or management plug-ins.

Bonus: Don’t forget the logs

Check the host machine and VM logs. Often, there you will find clues to understand if the GPU passage was successful or if there is something blocking everything.

Ultimately?

Testing an Nvidia GPU on a VDI is not complicated, but it takes a method. With the right tools and a keen eye, you can make sure that your graphics assets are really being used, and that the end user (or yourself) doesn’t have to put up with unnecessary lag or jerking.

Want help scripting an automated test? Write. Or… Launch nvidia-smi (with the -l parameter, it goes into automatic refresh) and see if the vGPU wakes up.

 

Some suggestions

 

  • To perform top benchmarks, you have to remove the cap present by default on vSphere for FPS (I would recommend keeping FPS equal to or lower than the Hz value of your monitor, otherwise, we may have a non-optimal fluidity of the images). The cap is deactivated by putting this value in the Advanced settings of the VM’s pciPassthru0.cfg.frame_rate_limiter=0

How to Test an NVIDIA Video Card with Omnissa VDI: A How-To Guide

Upgrade to 2503 and migrate ADAM Partition

The new version of Horizon (version 2503) continues the renaming started by Omnissa in version 2412 to remove references to the VMware name.

Let’s see in this guide (with a simple environment consisting of a single connection server) the update to version 2503 (from 2412) and the AD LDS Application Partition Migration (to remove references to VMware as well).

It is important to follow the instructions in this KB for partition migration:

Omnissa Horizon ADLDS Migration (6000797)

Upgrade to the 2503

Upgrade to Horizon 2503 from 2412 (I recommend if you are coming from previous versions, to a new installation, whether the target version is 2412 or 2503)

  • Backup ADAM DB
  • Snapshot Connection Servers

A screenshot of a computer AI-generated content may be incorrect.

A screenshot of a computer AI-generated content may be incorrect.

A screenshot of a computer AI-generated content may be incorrect.

A screenshot of a computer program AI-generated content may be incorrect.

A screenshot of a computer error AI-generated content may be incorrect.

A screenshot of a computer AI-generated content may be incorrect.

A screenshot of a computer AI-generated content may be incorrect.

AD LDS Application Partition Migration

Migration of the AD LDS partition (WARNING: at this time, if you have Omnissa Access in your infrastructure, you must wait for the new version of Access before proceeding with the partition migration)

We connect to the LDS AD using the classic references dc=vdi,dc=vmware,dc=int

A screenshot of a computer AI-generated content may be incorrect.

We use the script (OmnissaHorizonPartitionMigration-v1.ps1) attached to the KB previously mentioned (WARNING: you must have a maintenance window)

Run as admin

A black and white text on a black background AI-generated content may be incorrect.

We test script execution at the policy level (must be RemoteSigned)

A screen shot of a computer AI-generated content may be incorrect.

Let’s run the script

A screen shot of a computer AI-generated content may be incorrect.

We have the choice whether to migrate or clean the old partition (obviously, we do the cleaning of the old partition only after the migration and only after verifying that everything is ok)

A screenshot of a computer screen AI-generated content may be incorrect.

We select 1

A screen shot of a computer AI-generated content may be incorrect.

A computer screen with white and green text AI-generated content may be incorrect.

We are asked if we have an Omnissa Access configured in our environment, and in this example, we do not have it (in case you need to update it to the latest version before doing this update)

Then check that all the Connection servers are reachable (we have only one, in the case of multiple connection servers in a single POD, it is enough to run the script only once)

A screen shot of a computer AI-generated content may be incorrect.

Backs Up

A computer screen with text on it AI-generated content may be incorrect.

And import the information into the new schema

A screenshot of a computer program AI-generated content may be incorrect.

“Once the Script completes execution, stop the “Omnissa Horizon Connection Server” Windows Service on all Connection Servers in the POD. Only once the services on all servers in the pod are stopped, proceed to the next step. Note that rolling service restarts are NOT supported and will result in instability! 

Start the Connection Server Service one at a time on all Connection Servers in the POD.   You do not need to wait for the Connection server service to fully start up to before finish starting of services on remaining servers in the pod. The first service can wait up to 15 minutes per server in the pod to check back in for replication before allowing the service to start up. “

A screenshot of a computer AI-generated content may be incorrect.

A screenshot of a computer error AI-generated content may be incorrect.

Let’s wait for it to come back up

We connect with the ADSI edit using the new scheme dc=vdi,dc=horizon,dc=internal

A screenshot of a computer AI-generated content may be incorrect.

A screenshot of a computer AI-generated content may be incorrect.

As a test, let’s try to remove a non-existent domain and see which one the change actually applies to (I expect the second)

Let’s change the display name of a pool

A screenshot of a computer AI-generated content may be incorrect.

A screenshot of a computer AI-generated content may be incorrect.

The new has changed

A screenshot of a computer AI-generated content may be incorrect.

The old has not changed

A screenshot of a computer AI-generated content may be incorrect.

Once we finish the tasks and see that everything is stable, we erase the old pattern

A screenshot of a computer program AI-generated content may be incorrect.

A computer screen with white text AI-generated content may be incorrect.

A computer screen with text AI-generated content may be incorrect.

A screen shot of a computer screen AI-generated content may be incorrect.

The old scheme no longer exists

A screenshot of a computer error message AI-generated content may be incorrect.

Upgrade to 2503 and migrate ADAM Partition