Olivier Marchetta

Apr 052020
 

When trying to connect to a domain-joined Microsoft SQL Server 2017 with a domain authentication (user account belonging to the same AD Domain as the Microsoft SQL Server) from a non-domain joined computer or a computer joined to another non-trusted domain, you get a “login failed” on every connection attempts.

The solution (or workaround) is to use the Windows Credential Manager to pre-configure the domain user account to be trusted by SSMS with the following steps:

  1. Open Credential Manager (type Credential Manager from the Start Menu)
  2. Click “Add A Windows Credential”
  3. Populate the network address field with the name and port number of the SQL instance you wish to store credentials. For example: MyMSSQLServer.domain.org:1433 (1433 is the default port, you may need a different port, especially if you are connecting to a named instance).
  4. Populate the “User Name” including the domain name: “DOMAIN\Username”
  5. Enter the “Password”
  6. Click OK

Done! Restart SSMS, try connecting to the remote SQL Server from your non-domain joined machine and this time your login should work!

Mar 052020
 

For Citrix XenApp and XenDesktop migrations, I have reworked a quick and easy PS script found on Internet to list installed apps (from the Registry x64 and Wow6432).

    $array = @()
    $computername = "XENAPP-SERVER1"
    $UninstallKey64="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
    $UninstallKey32="SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall" 
    $reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$computername) 
    #x64
    $regkey=$reg.OpenSubKey($UninstallKey64) 
    $subkeys=$regkey.GetSubKeyNames() 
    foreach($key in $subkeys){
        $thisKey=$UninstallKey64+"\\"+$key 
        $thisSubKey=$reg.OpenSubKey($thisKey) 
        $obj = New-Object PSObject
        $obj | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value $computername
        $obj | Add-Member -MemberType NoteProperty -Name "DisplayName" -Value $($thisSubKey.GetValue("DisplayName"))
        $obj | Add-Member -MemberType NoteProperty -Name "DisplayVersion" -Value $($thisSubKey.GetValue("DisplayVersion"))
        $obj | Add-Member -MemberType NoteProperty -Name "InstallLocation" -Value $($thisSubKey.GetValue("InstallLocation"))
        $obj | Add-Member -MemberType NoteProperty -Name "Publisher" -Value $($thisSubKey.GetValue("Publisher"))
        $array += $obj
    } 
    #Wow6432
        $regkey=$reg.OpenSubKey($UninstallKey32) 
    $subkeys=$regkey.GetSubKeyNames() 
    foreach($key in $subkeys){
        $thisKey=$UninstallKey32+"\\"+$key 
        $thisSubKey=$reg.OpenSubKey($thisKey) 
        $obj = New-Object PSObject
        $obj | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value $computername
        $obj | Add-Member -MemberType NoteProperty -Name "DisplayName" -Value $($thisSubKey.GetValue("DisplayName"))
        $obj | Add-Member -MemberType NoteProperty -Name "DisplayVersion" -Value $($thisSubKey.GetValue("DisplayVersion"))
        $obj | Add-Member -MemberType NoteProperty -Name "InstallLocation" -Value $($thisSubKey.GetValue("InstallLocation"))
        $obj | Add-Member -MemberType NoteProperty -Name "Publisher" -Value $($thisSubKey.GetValue("Publisher"))
        $array += $obj
    } 

 #output
 $array | Where-Object { $_.DisplayName } | select ComputerName, DisplayName, DisplayVersion, Publisher | Export-CSV .\appslist.txt
Feb 152020
 

In Windows Server 2012 and 2016 (and possibly 2019), the smart card service behavior was changed by Microsoft. It is now triggered by the insertion of a smart card.  In Windows Server 2008, the smart card service was always on.

This can pose challenges in some environments, especially for Citrix / RDS deployments when using the smart card redirection virtual channel. The smart card service will not detect the smart card insertion.

The workaround is to deploy a scheduled task to restart the smart card service “SCardSvr” when stopped.

The scheduled task trigger is “On an event” with a custom XML query looking for the event ID “7036” (source: Service Control Manager) containing the keywords “Smart Card” and “stopped”. The XML query is the following: 

<QueryList>
  <Query Id="0" Path="System">
    <Select Path="System">
     *[System[Provider[@Name='Service Control Manager'] and (Level=4) and (EventID=7036)]]
     and
     *[EventData[Data[@Name='param1'] and (Data='Smart Card')]]
     and
     *[EventData[Data[@Name='param2'] and (Data='stopped')]] 
    </Select>
  </Query>

The task action is to start a program “SC” with the arguments “start SCardSvr”. The task can be run as SYSTEM and with high privileges.

When this task is deployed, the smart card service will be continuously restarted on the server, solving the issues when using a redirected smart card reader with Citrix / RDS.

Dec 192018
 

PVS Server Memory Sizing

Citrix PVS server will use the system memory as a cache for the streaming operation. It is recommended to allocate enough memory to the server to get the best streaming performances by caching the virtual disks into the RAM. I use Sysinternal RAMMap on the PVS servers under load to find the maximum cache-in-memory value for each virtual disks file (in the “File Summary” tab). For the memory sizing, I’m using a modified formula from the Citrix PVS blog:

(Recommended Memory + (#XenApp_vDisks* 4GB) + (#XenDesktop_vDisks * 3GB)) + 5% buffer

So in example, for a Windows 2012 R2 PVS server streaming two XenApp (Windows 2012 R2) and seven XenDesktop (Windows 7 x64) virtual disks, the memory sizing formula is as follows:

(2 + (2*4) + (7*3))*1.05 = 32.55 GB of memory per PVS server

In this scenario, the PVS servers is configured with 32 GB RAM.

PVS Ports and Threads configuration

The base formula for configuration the number of ports and the number of threads is:

“# of ports” * “# of threads/port” = “max clients”

The number of threads should match the number of virtual CPUs on the server, making it a constant value in the formula. In our scenario, the PVS server will stream virtual disks to 500 targets with 6 virtual CPUs. Note that “# of ports” can only be an integer. This gives us the following formula: {84 * 6 = 504}. The PVS server will be configured with 84 listening ports (6910 to 6094) and 6 threads per port for a theoretical 504 maximum concurrent streaming targets.

PVS MTU configuration

The default MTU size on the PVS server is set to 1506 which correspond to: the payload size + UDP header (L4) + IP header (L3) + Ethernet header (L2).

1464 (payload) + 8 (UDP header) + 20 (IP header) + 14 (Ethernet header) = 1506

To prevent PVS traffic from being rejected or segmented, the PVS server network interface used for the streaming traffic should have a Layer 3 MTU size greater than the PVS Server MTU size without the Ethernet Header. In our case, this correspond to: 1464+8+20= 1492. Usually, the default Layer 3 MTU size on Windows Server is {1500 bytes}. To check the layer 3 MTU setting on the PVS server, the following command should be used: “netsh interface ipv4 show subinterface“. The global default MTU size including the Ethernet header is usually {1514 bytes} (Cisco Networking Infrastructure). To check the global MTU size the {ping} command should be used with a payload corresponding to the PVS Server payload. Note that the ICMP header has the same size as the UDP header so the payload doesn’t need to be adjusted to make the test. For example you can ping the gateway with a specific payload using the following command: “ping {gateway IP} -S {PVS Streaming IP} -f -l 1464″. The “-f” parameter prevent the packet from being segmented. If the packet is segmented then the following error is returned: “Packet needs to be fragmented but DF set“. This is the sign that the packet will be fragmented and the payload is too large for the current network infrastructure.

PVS Burst Size configuration

Only 32 packets can be sent over the network in a single IO Burst. 2 packets are reserved for the traffic overhead. Therefore the I/O burst size formula is the following: (MTU Payload * 30). In our case: 1464 * 32 = 43,920. The IO Burst Size will be set at 44 Kbytes.

PVS I/O Limit configuration

In my personal benchmarks, settings the concurrent I/O limit (transactions) slider to “0” had a negative impact on the overall streaming performance. Setting the slider to “128” for both “local” and “remote” concurrent I/O limit had the best performance output.

PVS Buffers per threads configuration

The number of buffers per threads is calculated with the following formula: (IOBurstSize / MaximumTransmissionUnit) + 2). The IOBurstSize is obtained with the formula (MTU Payload * 32). So in our example the formula is ((1464*30)/1464)+2. The number of buffers per threads is set to “32“.

PVS TCP/UDP Offloading

Large Send Offload and TSO Offload are responsible for re-segmenting TCP and UDP packets which is not properly supported by the Citrix PVS streaming traffic. Disabling TCP and UPD Offload on Windows Server 2012 R2 and on the network card interface driver options did not improve the network performance in our case, but had a negative impact on the CPU consumption under load. This settings should be tested for each deployment.

PVS Network dedicated NIC configuration

The best network performance was achived by disabling all network layers except IPv4 on a dedicated network interface, and disabling all WINS and NetBIOS listeners:

PVS targets RAM Cache sizing

In order to define the size limit of the RAM cache to prevent Boot and Logon performance issues, the target cache is redirected on the PVS server and the size will be monitored 10 minutes after the initial OS Boot, and 10 minutes after a user logon. Result: The Operating System (Windows 7 x64) will use the cache up to an average of 512 MB at boot time. This value should be set on all vDisks to reduce OS Boot storms effects in the VDI infrastructure. When a test user logs on, the cache will quickly fills up to an average of 1024 MB of cache. This value should be used on all vDisk to avoid OS Boot AND Logon storms on the VDI infrastructure by redirecting the I/O into the target’s RAM.

PVS vDisks defragmentation

Defragmenting the master vDisks offline after a merged base is a best practice for PVS environments. The merged vDisk (VHD file) is mounted on the PVS server as a disk drive (via Windows “mount” built-in command, from the right click menu). The disk is then defragmented via the native defragmenter tool from Windows (disk tools), but also using a third-party application named “Defraggler”. The latest showed slighly better results. The gain after a disk defrag was around 10 MBps in read speed using a benchmark tool on the PVS target.

Dec 052018
 

It can be usefull to launch certain tasks on the behalf of the user, in the user security context, on a CVAD server.

Create the task in the Task Scheduler then export it as XML. Find and replace the “<Principals>” entries with either Interactive Users or Users:

Interactive Users:
  <Principals>
    <Principal id="InteractiveUsers">
      <GroupId>S-1-5-4</GroupId>
      <RunLevel>LeastPrivilege</RunLevel>
    </Principal>
  </Principals>

or Users
  <Principals>
    <Principal id="InteractiveUsers">
      <GroupId>S-1-5-32-545</GroupId>
      <RunLevel>HighestAvailable</RunLevel>
    </Principal>
  </Principals>

Interactive users is the one I use the most. I keep the Users group ID in case I would neet to run a task with high privileges. But this is not recommended and even dangerous. Interactive tasks (impersonating users security context) can be used for small local tasks, but should not be used to run more important applications.

What about GPPs?

When deploying an interactive task via GPP to all Citrix CVAD servers, “%LogonDomain%\%LogonUser%” can be used in the graphic interface.

Nov 252018
 

When defining the policy “Default Associations Configuration File” with an XML definition file, users are still able to use the “Open with…” command in the context menu and set their own file type association. This is by design. One solution to enforce the FTA at logon is to use the “SetUserFTA” software from Christoph Kolbicz’s Blog. Another way is to detect and remove user defined File Type Associations in the registry via a script. The registry key is locked down with a “Deny” access control set to everyone including the Administrators. The following script will remove the “Deny” access control, and then proceed to the deletion of the user defined file type association. This script runs at logon and at logoff and have been tested successfully.

# REMOVE HKCU File Type Association
# in addition to OEMDefaultAssociation.xml
Function RegACL-Reset
{
$hkey = 2147483649 
$reg = [wmiclass]"root\default:StdRegProv"
$ace = $reg.GetSecurityDescriptor($hkey,$hsubkey).Descriptor.DACL
$reg.psbase.Scope.Options.EnablePrivileges = $true
$sd = ([WMIClass] "Win32_SecurityDescriptor").CreateInstance()
$sd.ControlFlags = 0x0004
for($i=0;$i -lt $ace.length;$i++)
{
 if($ace[$i].AceType -ne 1)
 {
  $SD.dacl += $ace[$i] 
 }
}
$reg.SetSecurityDescriptor($hkey,$hsubkey,$sd)
}

# .XML - Remove user defined .XML file type association
$testreg = Test-Path -Path HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.xml
if ($testreg -eq $true){
#$hsubkey = "Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.xml\UserChoice"
RegACL-Reset -hsubkey "Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.xml\UserChoice"
Remove-Item -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.xml" -Force -Recurse
Nov 052018
 

Long story short, this is the script I’ve found and is the most accurate / reliable in my experience when used in recurse mode on Citrix user profiles to get the profiles size:

function Get-FolderSize {
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
$Path,
[ValidateSet("KB","MB","GB")]
$Units = "MB"
)
  if ( (Test-Path $Path) -and (Get-Item $Path).PSIsContainer ) {
    $Measure = Get-ChildItem $Path -Recurse -Force -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum
    $Sum = $Measure.Sum / "1$Units"
    $Sum = "{0:N2} $Units" -f $Sum
    [PSCustomObject]@{
      "Path" = $Path
      "Size($Units)" = $Sum
    }
  }
}

Get-FolderSize D:\HomeDir\UPM

Oct 052018
 

Remove-Item cannot be used to remove symbolic link to directory as it will remove the content of the directory it points to (so be careful!).

To safely remove a symbolic link from the file system using PowerShell, add the following function to your script:

From: PowerShell Gallery | Private, LinkUtils

function Remove-Symlink {
    <# 
    .SYNOPSIS 
        Removes a symbolic link. 
    #>
    param (
        [Parameter(Position=0, Mandatory=$true)]
        [string] $Link
    )    
    # Remove-Item cannot be used to remove symbolic link to directory as it
    # will remove the content of the directory it points to.
    [System.IO.Directory]::Delete($Link, $true)
}

Remove-Symlink -Link [path to the symbolic link you wish to remove]

Done! 🙂

Apr 192018
 

I’ve come across a strange configuration in a recent XenApp deployment. I had to implement Folder Redirection on a XenApp server pointing to a local OneDrive folder on the client, shared as a network drive “\\client\onedrive$”.

So all I needed to do was to configure the Microsoft Folder Redirection in a GPO right? Wrong. The built-in redirection mechanism was not very happy with the network path “\\client\onedrive$”. I had recurrent failed drive mappings in the event logs and the users wouldn’t get their XenApp documents redirected to their local One Drive folder.

After several attempts, I finally decided to go via the old fashioned way hacking the User Shell Folders in the Registry. But with Windows 10 and Server 2016 the registry keys and values have changed since the Windows XP-7 era. It’s less intuitive and I had to dig to find what GUIDs was used to point to a local Document or Desktop folders. Finally, the setup is working fine! But I wanted to leave a billet with the GUIDs I needed to hack in the Registry to manually point the XenApp user folders to the client’s One Drive shared folder.

Now the list of the GUIDs you may use to manually implement folder redirections:

Desktop:

Hive: HKEY_CURRENT_USER
Key: Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
Name: Desktop
Type: REG_SZ
Value: \\client\onedrive$\Desktop

Documents (redirection 1):

Hive: HKEY_CURRENT_USER
Key: Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
Name: Personal
Type: REG_SZ
Value: \\client\onedrive$\Documents

Documents (redirection 2):

Hive: HKEY_CURRENT_USER
Key: Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
Name: {F42EE2D3-909F-4907-8871-4C22FC0BF756}
Type: REG_SZ
Value: \\client\onedrive$\Documents

Pictures (redirection 1):

Hive: HKEY_CURRENT_USER
Key: Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
Name: My Pictures
Type: REG_SZ
Value: \\client\onedrive$\Pictures

Pictures (redirection 2):

Hive: HKEY_CURRENT_USER
Key: Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
Name: {0DDD015D-B06C-45D5-8C4C-F59713854639}
Type: REG_SZ
Value: \\client\onedrive$\Pictures

Music (redirection 1):

Hive: HKEY_CURRENT_USER
Key: Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
Value: My Music
Type: REG_SZ
Value: \\client\onedrive$\Music

Music (redirection 2):

Hive: HKEY_CURRENT_USER
Key: Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
Name: {A0C69A99-21C8-4671-8703-7934162FCF1D}
Type: REG_SZ
Value: \\client\onedrive$\Music

Videos (redirection 1):

Hive: HKEY_CURRENT_USER
Key: Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
Name: My Video
Type: REG_SZ
Value: \\client\onedrive$\Videos

Videos (redirection 2):

Hive: HKEY_CURRENT_USER
Key: Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
Name: {35286A68-3C57-41A1-BBB1-0EAE73D76C95}
Type: REG_SZ
Value: \\client\onedrive$\Videos

Downloads:

Hive: HKEY_CURRENT_USER
Key: Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
Name: {374DE290-123F-4565-9164-39C4925E467B}
Type: REG_SZ
Value: \\client\onedrive$\Downloads

Links:

Hive: HKEY_CURRENT_USER
Key: Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
Name: {BFB9D5E0-C6A9-404C-B2B2-AE6DB6AF4968}
Type: REG_SZ
Value: \\client\onedrive$\Links
Mar 052018
 

The PowerShell Get-Process commandlet cannot return the process owner in the v3 implementation. In PowerShell v4, the Get-Process cmdlet now has a “-IncludeUserName” parameter but it will only run as elevated (administrator).

I’ve been searching for an optimized script, running in a non-elevated context. But all I’ve found was slow and high resource demanding scripts to match or find a process owner. I needed a simple, short and efficient script to run in Citrix XenApp environments.

So I’m happy to share with you my “optimized” version of the script:

$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}}

This example runs in the user context, find the explorer.exe owned by the user and restart it by stopping the process (explorer.exe will automatically restart when stopped via a PS script). I need to restart explorer.exe if the user shell folders (folder redirections) have changed since the last logon.

But this script could be reused to find the owner of a high CPU usage process (I’m thinking at some PDF readers stuck at 99% CPU usage when the user is disconnected). Finding the process owner is not easy with the current version of PowerShell, but you can use my own script freely if you like it.