Olivier Marchetta

Feb 152018
 

My client wanted to redirect the personal documents for the published applications in XenApp to the local OneDrive folder on the user’s devices, and if possible, to create a OneDrive folder shortcut in XenApp looking exactly as the local OneDrive client in the file explorer. Mission accomplished. Discover how I have done it in this document.

Preamble

Current implementation (pre-migration): Microsoft OneDrive has been implemented as the default personal documents storage for the users on the VDI machines and laptops. In the current Citrix XenApp 6.5 farm (published applications), a network drive “O” is mapped on the local client machine using the “OneDrive Mapper” script and is then mapped in the XenApp published application environment via a Citrix policy (file redirection, network drives). The users have been instructed to save their documents in the mapped network drive “O” when using a XenApp published application. The personal folders (Documents, Pictures, etc) in XenApp are not redirected to the network drive and are pointing to the Citrix UPM user profile, which can be confusing when using the published application’s file explorer.

The current OneDrive redirection for the XenApp 6.5 farm is a complex setup involving configuration on all clients (VDI, laptops) with the “OneDrive mapper” script. Several issues have been reported to the service desk regarding the reliability of the “OneDrive mapper” script on laptops, and the disappearance of the redirected network drive “O” in XenApp published applications.

For the new Citrix XenApp 7.15 farm deployment, the OneDrive redirection should be redesigned to remove all pre-requisites on the client side (zero client configuration) in a more reliable approach with less “moving parts” on both servers and clients, and with a more seamless integration in the published applications’ file explorer for a better user experience.

OneDrive folder redirection for XenApp design

The OneDrive folder redirection configuration will use the Citrix client drive mapping and redirection technology to get access to the client device local disk drive and locate the user’s OneDrive folder.

The file explorer hosted in Citrix XenApp for the published application will have folder redirection implemented to seamlessly redirect the user’s personal folders to the local OneDrive client folder located on the user’s device.

In addition, the root OneDrive client folder will be added to the File Explorer as a network shortcut and appearing as the native OneDrive client root folder, even if the client is not actually installed on the Citrix XenApp servers.

Citrix client drive mapping configuration

Microsoft group policies have been used to implement the Citrix XenApp user policies. Note that Citrix does not support mixing Windows settings and Citrix settings in the same group policy. Two policies have been created, one to configure the Citrix client drive mapping policy, and one to configure the OneDrive redirection with PowerShell.

In the first policy, the Citrix File Redirection settings are configured in:

User Configuration / Policies / Citrix Policies / File Redirection

User personal folders redirection configuration

The user personal folders will be redirected into the user’s OneDrive folder as follow:

The user personal folder redirection will be set only if the following conditions are met:

• client drive is reachable (exists) AND the user OneDrive folder is reachable (exists)

If the user OneDrive folder exists but the target folders for the redirection do not, they will be created. For example, if the directory “Videos” is missing in the user’s local OneDrive folder, the directory will be created before configuring the redirection on the XenApp server.

If the conditions are not met, the redirection will not be set to the user’s local OneDrive folder, but to the Citrix user profile (on the XenApp server).

The embedded folder redirection configuration in the Microsoft group policy could not be use in this complex scenario. A power shell login script has been defined on the XenApp server for the OneDrive folder redirection: (see Annex A).

OneDrive network shortcut in file explorer configuration

To achieve the best user experience with the OneDrive folder redirection, a network shortcut pointing to the root of the user’s local OneDrive folder on the user’s device will be added with the native OneDrive icon to the hosted file explorer in XenApp for the published applications.

The shortcut will be created as a symbolic link (directory junction) in the “Network Shortcuts” directory located in:

%username%\AppData\Roaming\Microsoft\Windows\Network Shortcuts

Note that the type of shortcut is a “File folder”, showing that this is not just a regular shortcut (.lnk) but a directory junction:

To get the OneDrive native icon for the shortcut and for this shortcut to appear in the save location window for legacy applications, it is required to copy a “desktop.ini” file at the root of the OneDrive folder. The “desktop.ini” file contains the property of the folder and the icon file location:

[.ShellClassInfo]
CLSID2={0AFACED1-E828-11D1-9187-B532F1E9575D}
Flags=2
IconFile=C:\Local\OneDrive.exe
IconIndex=0

The Shell Class Info is pointing to the registry

“HKEY_CLASS_ROOT\CLSID\{0AFACED1-E828-11D1-9187-B532F1E9575D}”

This is to help the embedded Explorer from published applications to see the directory junction as a directory. The icon file is located on the server itself. The network shortcut is created with a PowerShell script at the user login on the XenApp server.

The script will test the client local drive mapping and the OneDrive folder path on the client device before creating the shortcut. The script will then create the directory junction and create the “desktop.ini” at the root of the OneDrive folder. The script is available at this end of this document (see Annex B).

Annex A: XenApp_OneDrive_Redirection.PS1

# OneDrive redirection for XenApp Script
# Olivier MARCHETTA - IT Contractor - London - 2018
# This script will redirect XenApp folders to the OneDrive folders on the connected client device
# If the client device OneDrive folder is not available, the script will redirect the folders on the local XenApp server
# The script will also create missing folders in OneDrive (fixing a previous old migration from Windows XP profiles)
 
 
# 1. Init variables
 
$remoteonedrive = "\\client\c$\users\" + $env:UserName + "\OneDrive - Company Name"
$testremoteonedrive = Test-Path -Path $remoteonedrive
$usershellfolders = Get-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders"
$restartexplorer = 0 
 
# 2. Redirecting XenApp profile folders to the client OneDrive folders if available
 
If ($testremoteonedrive -eq $true){
 
    #Init OneDrive paths variables
    $mydesktoppath = Join-Path $remoteonedrive -ChildPath "Desktop"
    $mydocumentspath = Join-Path $remoteonedrive -ChildPath "Documents"
    $mypicturespath = Join-Path $remoteonedrive -ChildPath "Pictures"
    $mymusicpath = Join-Path $remoteonedrive -ChildPath "Music"
    $myvideospath = Join-Path $remoteonedrive -ChildPath "Videos"
    $mydownloadspath = Join-Path $remoteonedrive -ChildPath "Downloads"
    $mylinkspath = Join-Path $remoteonedrive -ChildPath "Links"
 
    #Init Test-Path variables
    $mydesktoptest = Test-Path -Path $mydesktoppath
    $mydocumentstest = Test-Path -Path $mydocumentspath
    $mypicturestest = Test-Path -Path $mypicturespath
    $mymusictest = Test-Path -Path $mymusicpath
    $myvideostest = Test-Path -Path $myvideospath
    $mydownloadstest = Test-Path -Path $mydownloadspath
    $mylinkstest = Test-Path -Path $mylinkspath
 
#Testing OneDrive subfolders and creating missing directories
If ($mydesktoptest -eq $false){
   New-Item -Path $remoteonedrive -Name "Desktop" -ItemType "directory" -ErrorAction SilentlyContinue
   Sleep -Milliseconds 200}
If ($mydocumentstest -eq $false){
   New-Item -Path $remoteonedrive -Name "Documents" -ItemType "directory" -ErrorAction SilentlyContinue
   Sleep -Milliseconds 200} 
If ($mypicturestest -eq $false){
   New-Item -Path $remoteonedrive -Name "Pictures" -ItemType "directory" -ErrorAction SilentlyContinue
   Sleep -Milliseconds 200}
If ($mymusictest -eq $false){
   New-Item -Path $remoteonedrive -Name "Videos" -ItemType "directory" -ErrorAction SilentlyContinue
   Sleep -Milliseconds 200}
If ($myvideostest -eq $false){
   New-Item -Path $remoteonedrive -Name "Music" -ItemType "directory" -ErrorAction SilentlyContinue
   Sleep -Milliseconds 200}
    If ($mydownloadstest -eq $false){
   New-Item -Path $remoteonedrive -Name "Downloads" -ItemType "directory" -ErrorAction SilentlyContinue
   Sleep -Milliseconds 200}
If ($mylinkstest -eq $false){
   New-Item -Path $remoteonedrive -Name "Links" -ItemType "directory" -ErrorAction SilentlyContinue
   Sleep -Milliseconds 200}
 
#Comparing and updating User Shell Folders in registry
If ($mydesktoppath -ne $usershellfolders.Desktop){
   Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "Desktop" -value $mydesktoppath -ErrorAction SilentlyContinue
   $restartexplorer = 1}

If ($mydocumentspath -ne $usershellfolders.Personal){
   Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "Personal" -value $mydocumentspath -ErrorAction SilentlyContinue
   $restartexplorer = 1}

If ($mydocumentspath -ne $usershellfolders.'{F42EE2D3-909F-4907-8871-4C22FC0BF756}'){
   Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "{F42EE2D3-909F-4907-8871-4C22FC0BF756}" -value $mydocumentspath -ErrorAction SilentlyContinue
   $restartexplorer = 1}

If ($mypicturespath -ne $usershellfolders."My Pictures"){
   Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "My Pictures" -value $mypicturespath -ErrorAction SilentlyContinue
   $restartexplorer = 1}

If ($mypicturespath -ne $usershellfolders.'{0DDD015D-B06C-45D5-8C4C-F59713854639}'){
   Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "{0DDD015D-B06C-45D5-8C4C-F59713854639}" -value $mypicturespath -ErrorAction SilentlyContinue
   $restartexplorer = 1}

   If ($mymusicpath -ne $usershellfolders."My Music"){
   Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "My Music" -value $mymusicpath -ErrorAction SilentlyContinue
   $restartexplorer = 1}

If ($mymusicpath -ne $usershellfolders.'{A0C69A99-21C8-4671-8703-7934162FCF1D}'){
   Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "{A0C69A99-21C8-4671-8703-7934162FCF1D}" -value $mymusicpath -ErrorAction SilentlyContinue
   $restartexplorer = 1}

If ($myvideospath -ne $usershellfolders."My Video"){
   Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "My Video" -value $myvideospath -ErrorAction SilentlyContinue
   $restartexplorer = 1}

If ($myvideospath -ne $usershellfolders.'{35286A68-3C57-41A1-BBB1-0EAE73D76C95}'){
   Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "{35286A68-3C57-41A1-BBB1-0EAE73D76C95}" -value $myvideospath -ErrorAction SilentlyContinue
   $restartexplorer = 1}

If ($mydownloadspath -ne $usershellfolders.'{374DE290-123F-4565-9164-39C4925E467B}'){
  Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "{374DE290-123F-4565-9164-39C4925E467B}" -value $mydownloadspath -ErrorAction SilentlyContinue
   $restartexplorer = 1}

If ($mylinkspath -ne $usershellfolders.Favorites){    
   Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "Favorites" -value $mylinkspath -ErrorAction SilentlyContinue
   $restartexplorer = 1}      
}

 
# 3. Fallback procedure: Redirecting user folders to the local XenApp profile on the server if the remote OneDrive is not available
# Note: we assume that the local user profile default folders exist and do not need to be tested or created
 
If ($testremoteonedrive -eq $false){
 
    #Init local profile paths variables
    $mydesktoppath = Join-Path $env:userprofile -ChildPath "Desktop"
    $mydocumentspath = Join-Path $env:userprofile -ChildPath "Documents"
    $mypicturespath = Join-Path $env:userprofile -ChildPath "Pictures"
    $mymusicpath = Join-Path $env:userprofile -ChildPath "Music"
    $myvideospath = Join-Path $env:userprofile -ChildPath "Videos"
    $mydownloadspath = Join-Path $env:userprofile -ChildPath "Downloads"
    $mylinkspath = Join-Path $env:userprofile -ChildPath "Links"
 
    #Comparing and updating User Shell Folders in registry
    If ($mydesktoppath -ne $usershellfolders.Desktop){
    Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "Desktop" -value "%USERPROFILE%\Desktop" -ErrorAction SilentlyContinue 
    $restartexplorer = 1}

    If ($mydocumentspath -ne $usershellfolders.Personal){
    Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "Personal" -value "%USERPROFILE%\Documents" -ErrorAction SilentlyContinue 
    $restartexplorer = 1}

    If ($mydocumentspath -ne $usershellfolders.'{F42EE2D3-909F-4907-8871-4C22FC0BF756}'){
    Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "{F42EE2D3-909F-4907-8871-4C22FC0BF756}" -value "%USERPROFILE%\Documents" -ErrorAction Stop    
    $restartexplorer = 1}

    If ($mypicturespath -ne $usershellfolders."My Pictures"){
    Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "My Pictures" -value "%USERPROFILE%\Pictures" -ErrorAction SilentlyContinue
    $restartexplorer = 1}

    If ($mypicturespath -ne $usershellfolders.'{0DDD015D-B06C-45D5-8C4C-F59713854639}'){
    Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "{0DDD015D-B06C-45D5-8C4C-F59713854639}" -value "%USERPROFILE%\Pictures" -ErrorAction SilentlyContinue   
    $restartexplorer = 1}

    If ($mymusicpath -ne $usershellfolders."My Music"){
    Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "My Music" -value "%USERPROFILE%\Music" -ErrorAction SilentlyContinue
    $restartexplorer = 1}

    If ($mymusicpath -ne $usershellfolders.'{A0C69A99-21C8-4671-8703-7934162FCF1D}'){
    Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "{A0C69A99-21C8-4671-8703-7934162FCF1D}" -value "%USERPROFILE%\Music" -ErrorAction SilentlyContinue   
    $restartexplorer = 1}

    If ($myvideospath -ne $usershellfolders."My Video"){
    Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "My Video" -value "%USERPROFILE%\Videos" -ErrorAction SilentlyContinue
    $restartexplorer = 1}

    If ($myvideospath -ne $usershellfolders.'{35286A68-3C57-41A1-BBB1-0EAE73D76C95}'){
    Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "{35286A68-3C57-41A1-BBB1-0EAE73D76C95}" -value "%USERPROFILE%\Videos" -ErrorAction SilentlyContinue
    $restartexplorer = 1}

    If ($mydownloadspath -ne $usershellfolders.'{374DE290-123F-4565-9164-39C4925E467B}'){
    Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "{374DE290-123F-4565-9164-39C4925E467B}" -value "%USERPROFILE%\Downloads" -ErrorAction SilentlyContinue
    $restartexplorer = 1}

    If ($mylinkspath -ne $usershellfolders.Favorites){ 
    Set-ItemProperty "hkcu:\software\microsoft\windows\currentversion\explorer\User Shell Folders" -Name "Favorites" -value "%USERPROFILE%\Favorites" -ErrorAction SilentlyContinue
    $restartexplorer = 1}
} 
 
#Restart explorer
If ($restartexplorer = 1){
$proc = @(Get-CimInstance -Query "Select * from Win32_Process where name = 'explorer.exe'")
for ($i=0; $i -lt $proc.length; $i++){
    $owner = ($proc[$i] | Invoke-CimMethod -MethodName GetOwner).User
    if ($owner -eq $env:UserName){
        $processid = ($proc[$i]).ProcessId
        Stop-Process -Id $processid}}

Annex B: XenApp_OneDrive_Shortcut.PS1

# OneDrive Network Place in File Explorer
# Olivier MARCHETTA - IT Contractor - London - 2018

function Add-NetworkLocation
<#
    Author: 
        
        Tom White, 2015.
    
    Description:
        Creates a network location shortcut using the specified path, name and target.
        Replicates the behaviour of the 'Add Network Location' wizard, creating a special folder as opposed to a simple shortcut.
        Returns $true on success and $false on failure.
        Use -Verbose for extended output.
    Example:
        Add-NetworkLocation -networkLocationPath "$env:APPDATA\Microsoft\Windows\Network Shortcuts" -networkLocationName "Network Location" -networkLocationTarget "\\server\share" -Verbose
#>
{
    [CmdLetBinding()]
    param
    (
        [Parameter(Mandatory=$true)][string]$networkLocationPath,
        [Parameter(Mandatory=$true)][string]$networkLocationName ,
        [Parameter(Mandatory=$true)][string]$networkLocationTarget
    )
    Begin
    {
        Write-Verbose -Message "Network location path: `"$networkLocationPath`"."
        Write-Verbose -Message "Network location name: `"$networkLocationName`"."
        Write-Verbose -Message "Network location target: `"$networkLocationTarget`"."
        Set-Variable -Name desktopIniContent -Option ReadOnly -value ([string]"[.ShellClassInfo]`r`nCLSID2={0AFACED1-E828-11D1-9187-B532F1E9575D}`r`nFlags=2`r`nIconFile=C:\Local\OneDrive.exe`r`nIconIndex=0")
    }
    Process
    {
        Write-Verbose -Message "Checking that `"$networkLocationPath`" is a valid directory..."
        if(Test-Path -Path $networkLocationPath -PathType Container)
        {
            try
            {
                Write-Verbose -Message "Creating `"$networkLocationPath\$networkLocationName`"."
                [void]$(New-Item -Path "$networkLocationPath\$networkLocationName" -ItemType Directory -ErrorAction Stop)
                Write-Verbose -Message "Setting system attribute on `"$networkLocationPath\$networkLocationName`"."
                Set-ItemProperty -Path "$networkLocationPath\$networkLocationName" -Name Attributes -Value ([System.IO.FileAttributes]::System) -ErrorAction Stop
            }
            catch [Exception]
            {
                Write-Error -Message "Cannot create or set attributes on `"$networkLocationPath\$networkLocationName`". Check your access and/or permissions."
                return $false
            }
        }
        else
        {
            Write-Error -Message "`"$networkLocationPath`" is not a valid directory path."
            return $false
        }
        try
        {
            Write-Verbose -Message "Creating `"$networkLocationPath\$networkLocationName\desktop.ini`"."
            [object]$desktopIni = New-Item -Path "$networkLocationPath\$networkLocationName\desktop.ini" -ItemType File
            Write-Verbose -Message "Writing to `"$($desktopIni.FullName)`"."
            Add-Content -Path $desktopIni.FullName -Value $desktopIniContent
        }
        catch [Exception]
        {
            Write-Error -Message "Error while creating or writing to `"$networkLocationPath\$networkLocationName\desktop.ini`". Check your access and/or permissions."
            return $false
        }
        try
        {
            $WshShell = New-Object -ComObject WScript.Shell
            Write-Verbose -Message "Creating shortcut to `"$networkLocationTarget`" at `"$networkLocationPath\$networkLocationName\target.lnk`"."
            $Shortcut = $WshShell.CreateShortcut("$networkLocationPath\$networkLocationName\target.lnk")
            $Shortcut.TargetPath = $networkLocationTarget
            $Shortcut.Description = "Created $(Get-Date -Format s) by $($MyInvocation.MyCommand)."
            $Shortcut.Save()
        }
        catch [Exception]
        {
            Write-Error -Message "Error while creating shortcut @ `"$networkLocationPath\$networkLocationName\target.lnk`". Check your access and permissions."
            return $false
        }
        return $true
    }
}
function Remove-Symlink {
    param (
        [Parameter(Position=0, Mandatory=$true)]
        [string] $Link
          )    
    [System.IO.Directory]::Delete($Link, $true)
}

$remoteonedrive = "\\client\c$\users\" + $env:UserName + "\OneDrive - Company Name"
$testremoteonedrive = Test-Path -Path $remoteonedrive
$testsymlink = Test-Path -Path "$env:APPDATA\Microsoft\Windows\Network Shortcuts\OneDrive - Company Name"
$testdesktopini = Test-Path -Path "$env:APPDATA\Microsoft\Windows\Network Shortcuts\OneDrive - Company Name\desktop.ini"
if ($testsymlink -eq $true){
Remove-Symlink -Link "$env:APPDATA\Microsoft\Windows\Network Shortcuts\OneDrive - Company Name"}
if ($testremoteonedrive -eq $true){
Remove-Item -Path "$remoteonedrive\desktop.ini" -Force
Add-NetworkLocation -networkLocationPath "$env:APPDATA\Microsoft\Windows\Network Shortcuts" -networkLocationName "OneDrive - Company Name" -networkLocationTarget $remoteonedrive -Verbose
Copy-Item -Path "$env:APPDATA\Microsoft\Windows\Network Shortcuts\OneDrive - Company Name\desktop.ini" -Destination $remoteonedrive\desktop.ini -Force}
Apr 052016
 

In our corporate environment I was asked to remove most of the built-in Windows 10 applications from the command line .

It works fine , but the Start Menu layout is then filled with missing applcations as shown in the following screenshot :

And it will be looking like this for each new user logging-in the computer. So I’ve customized the Windows 10 layout to look clean as below :

But to define this new layout as default for everyone in the computer, you have to use some powershell commands to export – import the layout :

Export-StartLayout -Path C:\users\_you_username_\start_menu.xml

Import-StartLayout -LayoutPath C:\users\_your_username\start_menu.xml -MountPath C:\

You might consider using GPOs instead of a command in the distribution image … But the GPO that defines a “layout” located in “Administrative Templates > Start Menu and Taskbar > Start Layout” will apply your exported layout, but also lock the Start Menu customization for the users ! Which we didn’t want to happen…

Finally the Export/Import in PowerShell was a good solution for our needs. 🙂

Mar 052016
 

An interesting script from Technet forums to remove Windows 10 Bulit-in Apps and Windows Store for a corporate build :

$AppsList = "Microsoft.3DBuilder", # 3D Builder
    "Microsoft.MicrosoftOfficeHub", # Get Office
    "Microsoft.SkypeApp", # Get Skype
    "microsoft.windowscommunicationsapps", # Mail & Calendar
    "Microsoft.People", # People
    "Microsoft.CommsPhone", # Phone
    "Microsoft.WindowsPhone", # Phone Companion
    "Microsoft.XboxApp", # Xbox
    "Microsoft.Messaging", # Messaging & Skype
    "Microsoft.MicrosoftSolitaireCollection", # Microsoft Solitaire Collection
    "Microsoft.ConnectivityStore", # Microsoft WiFi
    "Microsoft.Office.Sway", # Microsoft Office Sway
    "Microsoft.Office.OneNote", # Microsoft Office OneNote
    "9E2F88E3.Twitter", # Twitter
    "Microsoft.WindowsStore" # Windows Store

ForEach ($App in $AppsList)
{
 $Packages = Get-AppxPackage | Where-Object {$_.Name -eq $App}
 if ($Packages -ne $null)
 {
  "Removing Appx Package: $App"
  foreach ($Package in $Packages) { Remove-AppxPackage -package $Package.PackageFullName }
 }
 else { "Unable to find package: $App" }

 $ProvisionedPackage = Get-AppxProvisionedPackage -online | Where-Object {$_.displayName -eq $App}
 if ($ProvisionedPackage -ne $null)
 {
  "Removing Appx Provisioned Package: $App"
  remove-AppxProvisionedPackage -online -packagename $ProvisionedPackage.PackageName
 }
 else { "Unable to find provisioned package: $App" }
}
Feb 072016
 

Continuously Available File Server (CAFS) CLUSTER DOCUMENTATION

Contents
1 – Server components and network configuration
2 – Quorum disk configuration
2.1 – Quorum disk mapping on server CAFS1
2.2 – Quorum disk mapping on server CAFS2
3 –Cluster feature deployment
3.1 – Add the Cluster Feature on server CAFS1
3.2 – Add the Cluster Feature on server CAFS2
4 –Cluster Configuration validation
5 – Creation of the cluster


1 – Server components and network configuration

Servers components review:
Server Name CAFS1
Operating System Windows 2012 R2
CPU 2 vCPU
RAM 4 GB
Network interfaces 2 (Production / Private)
Disks 1 (System Drive – 60 GB)

Server Name CAFS2
Operating System Windows 2012 R2
CPU 2 vCPU
RAM 4 GB
Network interfaces 2 (Production / Private)
Disks 1 (System Drive – 60 GB)

Netbios Names :
CAFS = Cluster
CAFS1 = Node 1 name
CAFS2 = Node 2 name


The cluster needs a minimum of 2 separates networks :
– 1 public network interface for clustered services
– 1 private network for the cluster communication (heartbeat)

Public IP Addresses (network name “Production”):
10.5.239.107/24 CAFS
10.5.239.108/24 CAFS
10.5.239.109/24 CAFS

Private IP Addresses for cluster communication (network name “Private”):
172.20.10.1 CAFS
172.20.10.2 CAFS

The network card named “Ethernet” will be joined to the public (prod) network and the network card named “Ethernet2” will be joined to a private network for cluster communication.

2 – Quorum disk configuration

In a VMWare environment, the disk is mapped and shared as a “Raw Device Mapping” storage.
The quorum is a shared virtual SCSI disk on a shared SCSI bus amongst the virtual machines.
The shared disk is small (witness role only, no storage): 1 GB is required in our scenario.
The shared disk has been created in the SAN with the name “cafs-quorum” (storage pool n°3).

2.1 – Quorum disk mapping on CAFS1

NOTE: the virtual machine must be shut down before mapping the quorum shared disk.
In the virtual machine settings click on “Add” on top of window.
 

Select “Hard disk” and click on “Next”.

Select “Raw device mappings” and click on “Next”.

Select the “DGC Fibre Channel disk” pointing to the quorum shared disk with the size of 1 GB then click on “Next”.

Select “Store with the virtual machine” and click on “Next”.

Select “Virtual” for the compatibility mode and click on “Next”.

Set the virtual device node to “SCSI (1:0)” (a new SCSI controller will be created) and click on “Next” then click on “Finish”.

Back to the virtual machine settings you will see a new SCSI bus and a new hard drive with the description “Mapped RAW LUN”.

Click on the new SCSI controller and activate the “SCSI Bus Sharing” to “Physical: virtual disks can be shared between virtual machines on any server”.

Boot on the server CAFS1. In disk management you should now see the quorum disk (Disk 1 – 1.00 GB). Right click on the disk and select Initialize disk.

Select “MBR” for the partition table then click “Ok”.

Back the disk management, right click on the quorum disk and create a new simple volume.

On the welcome screen click Next. Take all the disk size. Click Next.
Select “Do not assign any a driver letter or drive path” then click on “Next”.

Quick format the disk and set the volume label to “QUORUM”. Click “Next” and then click on “Finish”.

Back the disk management the disk is now online, initialized with a NTFS volume.

The quorum disk is now ready for a cluster deployment on CAFS1.
Close the disk management window.

2.2 – Quorum disk mapping on CAFS2

NOTE: the virtual machine must be shut down before mapping the quorum shared disk.
In the virtual machine settings click on “Add” on top of window.

Select “Hard disk” and click on “Next”.

Select “Use an existing virtual disk” and click on “Next”.

Browse to the “CAFS1” virtual machine storage and select the disk “CAFS1.vmdk” with the size of “1 GB” and click “Open”.

Confirm the path to the VMDK file and click “Next”.

Set the virtual device node to “SCSI (1:1)” (a new SCSI controller will be created) and click on “Next” then click on “Finish”.

NOTE: it is very important that the SCSI BUS ID (the 1st number) is set to the same number as defined for other cluster nodes sharing the cluster disk, also it is very important that the Disk ID (the 2nd number) is unique amongst the other nodes sharing the disk. In our scenario, CAFS1 is using the SCSI configuration “1:0”, so for the second node CAFS2 we are using “1:1” (same bus, different disk ID).
 
Back to the virtual machine settings you will see a new SCSI bus and a new hard drive with the description “Mapped RAW LUN”.

Click on the new SCSI controller and activate the “SCSI Bus Sharing” to “Physical: virtual disks can be shared between virtual machines on any server”.


Now you can boot the server on. In disk management you will see the QUORUM disk initialized but “offline”.

This is the normal “standby mode” for the cluster and the disk should not be brought online by the disk management console but only by the Failover Cluster management console.

3 –Cluster feature deployment

The Failover cluster feature will be installed on CAFS1 and CAFS2 servers.

3.1 – Add the Cluster Feature on CAFS1

Open Server Manager. Click on “Manage” then click on “Add Roles and Features”.


Click on “Next” at the Welcome Screen.

Select “Role-based or feature-based installation” then click on “Next”.

The destination server is the local server by default, in this case “CAFS1”. Click on “Next”.

At the Roles pages, do not select any role. Click on “Next”.

At the Features page, select “Failover Clustering”.

Add the requested features and tick “Include management tools” then click “Add features”.

Back on the Features page, click on “Next”.

Click on “Install”.

Wait for the end of the installation process then click on “Close”.

3.2 – Add the Cluster Feature on CAFS2
Repeat the same step-by-step process done for CAFS1 from chapter 3.1.

4 –Cluster Configuration validation

Before you begin the cluster validation make sure that both servers CAFS1 and CAFS2 can communicate through the cluster dedicated network and the public network, the shared quorum must be visible in disk manager and the cluster feature has been successfully installed.

Open the Server Manager on CAFS1. From the “Tools” menu open the “Failover Cluster Manager”.


In the Failover Cluster Manager, “Management” section, click on “Validate Configuration”.

At the Welcome Screen click on “Next”.

Add the two servers “CAFS1” and “CAFS2” then click on “Next”.

Check “Run all tests” then click on “Next”.

Click on “Next” to begin all tests. Verify that the result is ok.

You can read “The configuration appears to be suitable for clustering”. Tick the “Create the cluster now using the validated nodes” checkbox then click on “Next”.

The cluster will now be created.

5 – Creation of the CAFS cluster

After you run the “Validate Configuration” wizard, the cluster creation wizard should pop-up. Click on “Next” to begin installation.

The Cluster Name is “CAFS” and the virtual IP address is set to “10.5.239.109”. Click on “Next”.

Tick the checkbox “Add all eligible storage to the cluster” then click on “Next”.

Wait for the cluster to be created.

When the cluster creation is finished, you can now see a new entry named “CAFS” in the Failover Cluster Management window. If you click on “Nodes” you can see the two servers “CAFS1” and “CAFS2” up and running.

If you click on “Disks” under the “Storage” menu you can see the quorum risk available and what server own the cluster shared disk.

You can ping the virtual IP “10.5.239.109” and check that the cluster is responding to the network requests.


In Active Directory, a virtual computer resource has been added with cluster’s name (CAFS).

 And a new record has been automatically added in the DNS zone.

 
The cluster deployment is now finished. We can now add clustered services (File server, DHCP, etc).

Feb 052016
 

I had to recently update Java to version 8 on some RDS servers. Java 8 has increased it’s security level by default, and many private website using unsigned Java code have stopped working with this version.

The only way to workaround is to deploy a “security.sites” file containing the URL of the websites for which we want to bypass the security check.

This is just simple file looking like this :

  • https://my-trusted-java-unsigned.but-using-ssl.com
  • https://trusted.company.website.com

I’ve tried to follow instructions from the Java.com manual to deploy the “security.sites” settings at the server level via files configuration. In this case you have to use a “deployment.config” located in <Windows Directory>\Sun\Java\Deployment\deployment.config.

But this is not very easy. First, the way Java is using path format inside the different config files is giving, to say the least, a real headache. The path format will change from files to files (really…). And on top of that, if you have hardened the security on your RDS server to deny users the read access on your system, it is quite difficult to troubleshoot why the “deployment.properties” method is not working.

Having a limited amount of time, I’ve chosen to deploy the “security.sites” file via the GPO preferences.

I’m working in a multi-tenant environment. We don’t have common share. So in this tutorrial I will store the “security.sites” file inside the GPO itself.

Launch the Group Policy Management Console. Right click on “Group Policy Objects” and select “New”. Name the GPO and click OK.

Now select your GPO in the left pane and click on the “Details” tab in the right pane.

We want to locate the “Unique ID” of the GPO. It will be something looking like : {44175C22-701C-4DD4-B378-9599CD5FACBA}.

Now we can locate and open the folder of the GPO. Just access your domain “SYSVOL” folder this way :

  • \\your.domain\SYSVOL\your.domain\Policies\{0C5DBD9B-592B-4E8F-93C9-D3C45243B58D}\

And I will copy the file in the following directory :

  • User\Scripts\Logon

So just copy and paste the “security.sites” in this location. Now edit the GPO with the Group Policy Management Console.

Open the User configuration / Preferences / Windows Settings / Files.

Create a new file policy.

Select “Replace” as the default action (the file will be replaced at each logon).

Select the GPO folder as the source path :

  • \\your.domain\SYSVOL\your.domain\Policies\{0C5DBD9B-592B-4E8F-93C9-D3C45243B58D}\User\Scripts\Logon\security.sites

Now the destination of the file is located in the user AppData folder (we will user the %USERPROFILE% variable to shorten the path) :

  • %USERPROFILE%\AppData\LocalLow\Sun\Java\Deployment\security\exception.sites

Now a very last action, in the “Common tab” select :

  • Run in logged-on user’s security context (user policy option)

Save it, close your GPO editor, and it’s all done ! Just link the GPO to your users OU and they will start using your own security list exception for Java 8.

Feb 052016
 

I had the chance to have a closer look at the Citrix User Profile Management, and I am sharing some of my notes in this article.

Introduction

The Citrix User Profile Management (UPM) v5.41 optimize Roaming Profiles with a “streaming” feature in order to improve user’s logon times. The Citrix UPM has been initially designed for XenApp and server based environments, but with the VDI era it has been extended to classic Microsoft desktops (Windows 7, Windows 8.1). In the company the deployment of the Citrix UPM on classic desktops can solve very slow logon time that users are experiencing with the default Microsoft Roaming Profile feature.

Infrastructure design

Citrix UPM only requires, at best, a highly available file server cluster and a GPO to activate and configure the UPM client on desktops / Citrix session-hosts (VDA). A clustered shared volume from the EMC SAN will be served by 2 nodes. The nodes are Windows 2012 R2 servers in a cluster. Folder Structure: For this deployment, profiles for desktops will be separated from profiles for servers. Two folders will be created:

Workstations

XenAppClient

configuration: A GPO named “Citrix UPM for Desktops” will be created for the client deployed on Windows 7 and 10 desktops. The GPO will be filtered by a security group name “Citrix UPM for Desktops” and located in an organizational unit.

Profile Streaming

The Citrix UPM has a feature named “Profile Steaming” working along with profile caching. When the user opens a session on a desktop with a roaming profile using Citrix UPM, the user doesn’t need to wait for all of the profile being downloaded from the server before getting access to his desktop. Instead, the Citrix UPM will allow the user to log-on to his desktop by synchronizing a small set of files and then “stream on demand” other files and settings.  It is possible to always cache (download) big files on logon to enhance the user experience.For profile optimization we will be using the user shell folders redirection.

Add alt textNo alt text provided for this image

 Citrix UPM Settings

The following settings are defined by GPO for Workstations (classic desktops):
Name – Enable Profile Management
Value – Enabled
Description – Turn UPM On or Off


Name – Path to User Store
Value – \\server\workstations\%username%.upm
Description – Define the profile storage path.

Name – Process logons of local administrators
Value – Disabled
Description – Allow local admins to logon using local profile instead of roaming profile. Useful for troubleshooting.

Name – Profile Streaming
Value – Enabled
Description – Turn the Profile Streaming feature on.

Name – Active Write Back
Value – Disabled
Description – Settings are written into the user profile store while the user session is running. Required for volatile desktops (pooled).

Name – Always Cache
Value – Not configured or Disabled
Description – The entire profile will be streamed into a local cache to save network IO on logon if treshold is set at 0. If the profile has big files to laod in priority set a size treshold in MB.

Name – Process Internet Cookies files on Logoff
Value – Enabled
Description – The UPM agent will read the IE index.dat and clear off unused cookies. IE11 or above use webcache.dat instead.

Name – Logoff user if a problem is encountered
Value – Disabled
Description – If the UPM profile is corrupt the UPM agent will deny logon access to the workstation. If disabled, the user can still logon but may be working with a temporary profile.

Name – Delete locally cached profiles on logoff
Value – Disabled
Description – On XenApp servers, profiles are usually deleted on logoff. On workstations the profile is kept on hard drive.

Name – Migration of existing profiles
Value – Local and Roaming
Description – The Citrix UPM agent will migrate Microsoft local and roaming profiles (cached on desktops) to the Citrix UPM Store.

Name – Local Profile Conflict Handling
Value – Rename local profile
Description – If a local profile exists and is detected as non-compatible with the Citrix UPM agent, the local profile will be renamed.