With App Volumes we have the possibility to create Writable Volumes that could be mounted on specific VM based on hostname prefix.
Ex. We can ask App Volumes to create a Writable Volumes for users from “WV_Users” group available only from VMs where hostname start by ABCDEF and create another Writable Volumes for VM starting by AZERTY for the same users so they can connect to both pool at the same time.
The question that was raised from a customer is what about FSLogix ? Ok there’s some internal features where you can specify what to do if a container is open at the same time in two different location (see https://learn.microsoft.com/en-us/fslogix/concepts-multi-concurrent-connections) but I’d prefer to get one per pool.
For me there’s at least two possibility :
1 – Use Pool ID name
2 – Use Hostname (well prefix used when deploying the pool)
In order to make it works we need to set a persistant variable available before FSLogix service start, and so the best I found is to use a post-configuration script, run when VM are deployed by Horizon. So I have 2 scripts, one .cmd that’s called by Horizon and a .ps1 one called by the .cmd file after ExecutionPolicy set correctly. Both should be available on the Golden/Master VM.
The first thing I did was to create the .cmd script that :
– Get current PowerShell ExecutionPolicy so I can revert to the same setting once my script is executed
– Run the PowerShell script to get either Pool ID or Hostname
– Revert to initial ExecutionPolicy state
Here the script, let’s call it hzn-post-sync.cmd:
@echo off
REM -- hzn-post-sync.cmd script
REM --- Get current PowerShell ExecutionPolicy
for /f "usebackq delims=" %%P in (powershell -Command "Get-ExecutionPolicy") do (
set "SavedPolicy=%%P"
)
echo Saved ExecutionPolicy: %SavedPolicy%
REM --- Set a temporary ExecutionPolicy (example: RemoteSigned)
powershell -Command "Set-ExecutionPolicy Unrestricted -Force"
echo Temporary ExecutionPolicy set to Unrestricted
REM --- Running PowerShell Script to Get Pool ID
powershell -File c:\Temp\GetPoolID.ps1
REM --- Restore the original ExecutionPolicy
powershell -Command "Set-ExecutionPolicy %SavedPolicy% -Force"
echo Original ExecutionPolicy restored to %SavedPolicy%
And now the PowerShell script called by the .cmd script:
Let’s fist look at for a script Based on Pool ID :
For pool ID, we need to look at in HKLM:\Software\Omnissa\Horizon\Node Manager\Server Pool DN as HKCU\Volatile Environment is available only once a user is logged in.
# --- hzn-post-sync.ps1 script
# --- Read the registry value
$regPath = "HKLM:\Software\Omnissa\Horizon\Node Manager"
$regName = "Server Pool DN"
try {
$fullValue = (Get-ItemProperty -Path $regPath -Name $regName).$regName
} catch {
Write-Error "Unable to read registry key $regPath\$regName"
exit 1
}
# --- Extract PoolID (after cn= and before the first comma)
if ($fullValue -match "cn=([^,]+)") {
$poolID = $matches[1]
} else {
Write-Error "Unable to extract poolID from '$fullValue'"
exit 1
}
# --- Write persistent system environment variable
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" `
-Name "HZNPoolID" -Value $poolID
# --- Notify Windows that environment variables changed
$signature = @"
[DllImport("user32.dll", SetLastError=true)]
public static extern IntPtr SendMessageTimeout(IntPtr hWnd, int Msg, IntPtr wParam, string lParam, uint fuFlags, uint uTimeout, out IntPtr lpdwResult);
"@
$SendMessageTimeout = Add-Type -MemberDefinition $signature -Name "Win32SendMessageTimeout" -Namespace Win32Functions -PassThru
$HWND_BROADCAST = [IntPtr]0xffff
$WM_SETTINGCHANGE = 0x1A
$result = [IntPtr]::Zero
$SendMessageTimeout::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, [IntPtr]::Zero, "Environment", 2, 5000, [ref]$result) | Out-Null
Write-Host "System Variable HZNPoolID defined as '$poolID'"
And this one is if you prefer a script Based on Hostname Prefix :
# Get the hostname
$hostname = $env:COMPUTERNAME
# Keep only the first 4 characters (safe handling if shorter)
if ($hostname.Length -ge 4) {
$shortHostname = $hostname.Substring(0,6)
} else {
$shortHostname = $hostname
}
# Save into permanent System Environment Variable (requires admin rights)
[System.Environment]::SetEnvironmentVariable('ShortHost', $shortHostname, 'Machine')
Write-Output "Shortened hostname saved system-wide as 'ShortHost': $shortHostname"
Restart-Computer -Force
Note : just change $hostname.Substring(0,6) to specify how many character that should be used.
The last step is the configure the FSLogix GPO in order to specify where to create the containers :
Computer Configurations\Policies\Administrative Templates…\FSLogix\Profile Containers | ||
GPO Key | GPO Settings | Details |
VHD Location | \\DFS\SHARE | Specify DFS Share name |
For POOL ID variable | ||
Computer Configurations\Policies\Administrative Templates…\FSLogix\Profile Containers\Container and Directory Naming | ||
GPO Key | GPO Settings | Details |
SID Directory Name Pattern | %HZNPoolID%.%userdomain% | Use POOL ID and Users domain ex : MYPOOL.MYDOMAIN |
SID Directory Name Match | %HZNPoolID%.%userdomain% | Need to be the same as Name Pattern |
VHD Name Pattern | Profile_%username%.%profileversion% | Use Profile_ as prefix and then username and profile version |
VHD Name Match | Profile_%username%.%profileversion%* | Ask to look for any containers with prefix set by pattern |
Volume Type (VHD,VHDX) | VHDX | Speciy to use VHDX container |
For Hostname variable | ||
Computer Configurations\Policies\Administrative Templates…\FSLogix\Profile Containers\Container and Directory Naming | ||
GPO Key | GPO Settings | Details |
SID Directory Name Pattern | %shortHostname%.%userdomain% | Use hostname prefix and Users domain ex : HOSTNA.DOMAIN |
SID Directory Name Match | %shortHostname%.%userdomain% | Need to be the same as Name Pattern |
VHD Name Pattern | Profile_%username%.%profileversion% | Use Profile_ as prefix and then username and profile version |
VHD Name Match | Profile_%username%.%profileversion%* | Ask to look for any containers with prefix set by pattern |
Volume Type (VHD,VHDX) | VHDX | Specify to use VHDX container |
Now, as the variable is created at the time of the deployement of the VM, FSLogix GPO will use it to create/usr the right folder and container.
BONUS :
I’ve been asked if we can manage the FSLogix GPO from Omnissa Dynamic Environment Manager :
Managing FSLogix with DEM means FSLogix Service must start after DEM Service
3 possibility here :
1 ) Set FSLogix service to manual and start it using Task Scheduler (not tested as no guaranty it will start in a reasonable time and after DEM Service)
2 ) Set DEM as a dependency for FSLogix, not working (it will use variable name instead of variable data)
3 ) Set FSLogix services as “Automatic (Delayed Start)” , it works but FSLogix service will start in the 180s after the last “Automatic” service starts. In case a user try to connect before FSLogix is started, it will get a error and won’t have a full Windows desktop
So my recommendation is to configure the FSLogix GPO in Active Directory instead of DEM.
Recent Comments