All Citrix products can be automated. This article covers the Citrix Director unattended installation and configuration with PowerShell and SCCM.
Change Log 01.09.2017: updated to Director 7.15 (from version 7.13). 17.12.2017: updated to Director 7.16. No major changes. Error correction in configuration script; the IIS attribute multipleSiteBindingsEnabled contained a spelling mistake (previously: multipleSiteBindingEnabled -> missing an ‘s’). 22.07.2018: updated the section Director Single Sign-on to include constrained delegation. |
Introduction
Before continuing with this article please be aware of the following:
- The version of Citrix Director in this article is 7.16 (but all scripts work from version 7.13 and higher).
- Director is part of the XenDesktop installation. The XenDesktop ISO file can be downloaded here:
https://www.citrix.com/downloads/xenapp-and-xendesktop/product-software/xenapp-and-xendesktop-716.html - The installation and configuration scripts are designed for the following operating systems:
- Microsoft Windows Server 2008 R2 SP1
- Microsoft Windows Server 2012 R2
- Microsoft Windows Server 2016
- The installation and configuration scripts have been tested on the following operating systems:
- Microsoft Windows Server 2008 R2 SP1
- Microsoft Windows Server 2012 R2
- Microsoft Windows Server 2016
- The assumption is that you execute the example script in this article on a server that has been pre-installed with one of the aforementioned operating systems including all the latest hotfixes and .Net Framework 4.6.x. Also, you should at least have PowerShell 3.0 installed on the system, although I recommend using the latest version.
I strongly urge you to read the following articles:
- Director 7.16 – by Carl Stalhood
This article explains the manual installation of Director step-by-step (very detailed!). This article roughly follows the structure of Carl’s article. - Citrix Director – by George Spiers
This article gives a very comprehensive overview of all the features of Director (very detailed!).
This article consists of three parts:
- Part 1 focuses on the installation of the Windows Roles and Features;
- Part 2 deals with the installation of Citrix Director;
- Part 3 describes the configuration of Citrix Director.
Part 1: Install Windows Roles and Features
Before we install Citrix Director, I recommend installing some commonly used roles and features. Part of these components are optional, but .Net Framework 3.5.1 for Windows Server 2008 R2 and .Net Framework 4.5.x for Windows Server 2012 and higher are not. If .Net Framework is not installed, the installation of Director ends in an error. This error can be found in the Director’s log files IIS_Install.txt and XenDesktop Installation.txt: “Parent features must be enabled before this feature can be enabled. WCF-HTTP-Activation(45)”.
IIS and Remote Assistance Please be aware that Internet Information Services (IIS) is a prerequisite for Director. The role Remote Assistance is required when you want to shadow users. Both of these components are installed automatically during the installation of Director. These components are NOT installed in this section of the article. |
In this example, we will install the following features:
- .Net Framework 3.5.1 (for W2K8R2 only)
- .Net Framework 4.5.x (for W2K12 and W2K16 only)
- Desktop experience (for W2K8R2 and W2K12 only)
- Group Policy Management Console
- Remote Server Administration Tools (AD DS Snap-Ins)
- Remote Desktop Licensing Tools
- Telnet Client
- Windows Process Activation Service
You can remove or add any role or feature you need. Please be aware though that not all features can be installed at the same time. Some features need a reboot. After that, other features can be installed.
In PowerShell, roles and features are installed using the Add-WindowsFeature (Windows Server 2008 R2) or Install-WindowsFeature command (Windows Server 2012 and higher). For example:
Windows Server 2008 (R2):
1 |
Add-WindowsFeature NET-Framework-Core,Desktop-Experience,GPMC,RSAT-ADDS-Tools,RSAT-RDS-Licensing,WAS,Telnet-Client |
Windows Server 2012 (R2):
1 |
Install-WindowsFeature NET-Framework-45-Core,Desktop-Experience,GPMC,RSAT-ADDS-Tools,RDS-Licensing-UI,WAS,Telnet-Client |
Windows Server 2016:
1 |
Install-WindowsFeature NET-Framework-45-Core,GPMC,RSAT-ADDS-Tools,RDS-Licensing-UI,WAS,Telnet-Client |
In some cases, the names of the individual roles and features differ between the various operating systems:
- .Net Framework:
- Windows Server 2008 R2 only offers version 3.5.1 (Add-WindowsFeature command NET-Framework-Core).
- Windows Server 2012 (R2) offers both the versions 3.5.1 and 4.5.x (Install-WindowsFeature command NET-Framework-Core and NET-Framework-45-Core), but I only install the 4.5.x version.
- Windows Server 2016 only offers version 4.5.x (Install-WindowsFeature command NET-Framework-45-Core).
- Desktop-Experience:
This feature no longer exists as a separate item in Windows Server 2016. Let me clarify; when you use the full version of Windows Server 2016 (not the core/nano version), desktop experience is included out of the box. - Remote Desktop Licensing Tools:
In Windows Server 2008 (R2), the abbreviation of this feature is called RSAT-RDS-Licensing. In Windows Server 2012 and higher, this feature is called RDS-Licensing-UI.
To establish the version of the running operating system, I use the following PowerShell command:
1 |
[string]$WindowsVersion = ( Get-WmiObject -class Win32_OperatingSystem ).Version |
Complete script for installing Roles and Features
In case you use my installation template, this is what the complete script, including logging, looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
#========================================================================== # # Installation of Microsoft Roles and Features for Citrix Director # # AUTHOR: Dennis Span (https://dennisspan.com) # DATE : 26.05.2017 # # COMMENT: # This script installs the following roles: # -.Net Framework 3.5 (W2K8R2 only) # -.Net Framework 4.6 (W2K12 + W2K16) # -Desktop experience (W2K8R2 + W2K12) # -Group Policy Management Console # -Remote Server Administration Tools (AD DS Snap-Ins) # -Remote Desktop Licensing Tools # -Telnet Client # -Windows Process Activation Service # # Please note that the following prerequisite roles for Citrix Director are # installed automatically during the installation of Citrix Director: # -Internet Information Service (IIS) # -Remote Assistance (if you need the shadow feature) # # This script has been prepared for Windows Server 2008 R2, 2012 R2 and 2016. # #========================================================================== # Get the script parameters if there are any param ( # The only parameter which is really required is 'Uninstall' # If no parameters are present or if the parameter is not # 'uninstall', an installation process is triggered [string]$Installationtype ) # define Error handling # note: do not change these values $global:ErrorActionPreference = "Stop" if($verbose){ $global:VerbosePreference = "Continue" } # FUNCTION DS_WriteLog #========================================================================== Function DS_WriteLog { <# .SYNOPSIS Write text to this script's log file .DESCRIPTION Write text to this script's log file .PARAMETER InformationType This parameter contains the information type prefix. Possible prefixes and information types are: I = Information S = Success W = Warning E = Error - = No status .PARAMETER Text This parameter contains the text (the line) you want to write to the log file. If text in the parameter is omitted, an empty line is written. .PARAMETER LogFile This parameter contains the full path, the file name and file extension to the log file (e.g. C:\Logs\MyApps\MylogFile.log) .EXAMPLE DS_WriteLog -InformationType "I" -Text "Copy files to C:\Temp" -LogFile "C:\Logs\MylogFile.log" Writes a line containing information to the log file .Example DS_WriteLog -InformationType "E" -Text "An error occurred trying to copy files to C:\Temp (error: $($Error[0]))" -LogFile "C:\Logs\MylogFile.log" Writes a line containing error information to the log file .Example DS_WriteLog -InformationType "-" -Text "" -LogFile "C:\Logs\MylogFile.log" Writes an empty line to the log file #> [CmdletBinding()] Param( [Parameter(Mandatory=$true, Position = 0)][String]$InformationType, [Parameter(Mandatory=$true, Position = 1)][AllowEmptyString()][String]$Text, [Parameter(Mandatory=$true, Position = 2)][AllowEmptyString()][String]$LogFile ) $DateTime = (Get-Date -format dd-MM-yyyy) + " " + (Get-Date -format HH:mm:ss) if ( $Text -eq "" ) { Add-Content $LogFile -value ("") # Write an empty line } Else { Add-Content $LogFile -value ($DateTime + " " + $InformationType + " - " + $Text) } } #========================================================================== ################ # Main section # ################ # Disable File Security $env:SEE_MASK_NOZONECHECKS = 1 # Custom variables [edit] $BaseLogDir = "C:\Logs" # [edit] add the location of your log directory here $PackageName = "Citrix Director Roles" # [edit] enter the display name of the software (e.g. 'Arcobat Reader' or 'Microsoft Office') # Global variables $StartDir = $PSScriptRoot # the directory path of the script currently being executed if (!($Installationtype -eq "Uninstall")) { $Installationtype = "Install" } $LogDir = (Join-Path $BaseLogDir $PackageName).Replace(" ","_") $LogFileName = "$($Installationtype)_$($PackageName).log" $LogFile = Join-path $LogDir $LogFileName # Create the log directory if it does not exist if (!(Test-Path $LogDir)) { New-Item -Path $LogDir -ItemType directory | Out-Null } # Create new log file (overwrite existing one) New-Item $LogFile -ItemType "file" -force | Out-Null DS_WriteLog "I" "START SCRIPT - $Installationtype $PackageName" $LogFile DS_WriteLog "-" "" $LogFile ################################################# # INSTALL MICROSOFT ROLES AND FEATURES # ################################################# DS_WriteLog "I" "Add Windows roles and features:" $LogFile DS_WriteLog "I" "-.Net Framework 3.5 (W2K8R2 only)" $LogFile DS_WriteLog "I" "-.Net Framework 4.6 (W2K12 + W2K16)" $LogFile DS_WriteLog "I" "-Desktop experience (W2K8R2 + W2K12)" $LogFile DS_WriteLog "I" "-Group Policy Management Console" $LogFile DS_WriteLog "I" "-Remote Server Administration Tools (AD DS Snap-Ins)" $LogFile DS_WriteLog "I" "-Remote Desktop Licensing Tools" $LogFile DS_WriteLog "I" "-Telnet Client" $LogFile DS_WriteLog "I" "-Windows Process Activation Service" $LogFile DS_WriteLog "-" "" $LogFile DS_WriteLog "I" "Retrieve the OS version and name" $LogFile # Check the windows version # URL: https://en.wikipedia.org/wiki/List_of_Microsoft_Windows_versions # -Windows Server 2016 -> NT 10.0 # -Windows Server 2012 R2 -> NT 6.3 # -Windows Server 2012 -> NT 6.2 # -Windows Server 2008 R2 -> NT 6.1 # -Windows Server 2008 -> NT 6.0 [string]$WindowsVersion = ( Get-WmiObject -class Win32_OperatingSystem ).Version switch -wildcard ($WindowsVersion) { "*10*" { $OSVER = "W2K16" $OSName = "Windows Server 2016" $LogFile2 = Join-Path $LogDir "Install_RolesAndFeatures.log" DS_WriteLog "I" "The current operating system is $($OSNAME) ($($OSVER))" $LogFile DS_WriteLog "-" "" $LogFile DS_WriteLog "I" "Roles and Features installation log file: $LogFile2" $LogFile DS_WriteLog "I" "Start the installation ..." $LogFile # Install Windows Features try { Install-WindowsFeature NET-Framework-45-Core,GPMC,RSAT-ADDS-Tools,RDS-Licensing-UI,WAS,Telnet-Client -logpath $LogFile2 DS_WriteLog "S" "The windows features were installed successfully!" $LogFile } catch { DS_WriteLog "E" "An error occurred while installing the windows features (error: $($error[0]))" $LogFile Exit 1 } } "*6.3*" { $OSVER = "W2K12R2" $OSName = "Windows Server 2012 R2" $LogFile2 = Join-Path $LogDir "Install_RolesAndFeatures.log" DS_WriteLog "I" "The current operating system is $($OSNAME) ($($OSVER))" $LogFile DS_WriteLog "-" "" $LogFile DS_WriteLog "I" "Roles and Features installation log file: $LogFile2" $LogFile DS_WriteLog "I" "Start the installation ..." $LogFile # Install Windows Features try { Install-WindowsFeature NET-Framework-45-Core,Desktop-Experience,GPMC,RSAT-ADDS-Tools,RDS-Licensing-UI,WAS,Telnet-Client -logpath $LogFile2 DS_WriteLog "S" "The windows features were installed successfully!" $LogFile } catch { DS_WriteLog "E" "An error occurred while installing the windows features (error: $($error[0]))" $LogFile Exit 1 } } "*6.2*" { $OSVER = "W2K12" $OSName = "Windows Server 2012" $LogFile2 = Join-Path $LogDir "Install_RolesAndFeatures.log" DS_WriteLog "I" "The current operating system is $($OSNAME) ($($OSVER))" $LogFile DS_WriteLog "-" "" $LogFile DS_WriteLog "I" "Roles and Features installation log file: $LogFile2" $LogFile DS_WriteLog "I" "Start the installation ..." $LogFile # Install Windows Features try { Install-WindowsFeature NET-Framework-45-Core,Desktop-Experience,GPMC,RSAT-ADDS-Tools,RDS-Licensing-UI,WAS,Telnet-Client -logpath $LogFile2 DS_WriteLog "S" "The windows features were installed successfully!" $LogFile } catch { DS_WriteLog "E" "An error occurred while installing the windows features (error: $($error[0]))" $LogFile Exit 1 } } "*6.1*" { $OSVER = "W2K8R2" $OSName = "Windows Server 2008 R2" $LogFile2 = Join-Path $LogDir "Install_RolesAndFeatures.log" DS_WriteLog "I" "The current operating system is $($OSNAME) ($($OSVER))" $LogFile DS_WriteLog "-" "" $LogFile DS_WriteLog "I" "Roles and Features installation log file: $LogFile2" $LogFile DS_WriteLog "I" "Start the installation ..." $LogFile # Install Windows Features try { Add-WindowsFeature NET-Framework-Core,Desktop-Experience,GPMC,RSAT-ADDS-Tools,RSAT-RDS-Licensing,WAS,Telnet-Client -logpath $LogFile2 DS_WriteLog "S" "The windows features were installed successfully!" $LogFile } catch { DS_WriteLog "E" "An error occurred while installing the windows features (error: $($error[0]))" $LogFile Exit 1 } } "*6.0*" { $OSVER = "W2K8" $OSName = "Windows Server 2008" $LogFile2 = Join-Path $LogDir "Install_RolesAndFeatures.log" DS_WriteLog "I" "The current operating system is $($OSNAME) ($($OSVER))" $LogFile DS_WriteLog "-" "" $LogFile DS_WriteLog "I" "Roles and Features installation log file: $LogFile2" $LogFile DS_WriteLog "I" "Start the installation ..." $LogFile # Install Windows Features try { Add-WindowsFeature NET-Framework-Core,Desktop-Experience,GPMC,RSAT-ADDS-Tools,RSAT-RDS-Licensing,WAS,Telnet-Client -logpath $LogFile2 DS_WriteLog "S" "The windows features were installed successfully!" $LogFile } catch { DS_WriteLog "E" "An error occurred while installing the windows features (error: $($error[0]))" $LogFile Exit 1 } } default { $OSName = ( Get-WmiObject -class Win32_OperatingSystem ).Caption DS_WriteLog "E" "The current operating system $($OSName) is unsupported" $LogFile DS_WriteLog "I" "This script will now be terminated" $LogFile DS_WriteLog "-" "" $LogFile Exit 1 } } # Enable File Security Remove-Item env:\SEE_MASK_NOZONECHECKS DS_WriteLog "-" "" $LogFile DS_WriteLog "I" "End of script" $LogFile |
Execute the script as follows:
powershell.exe -file %Directory%\Install_Citrix_Director_Roles.ps1
In case you get a security warning, execute the script as follows:
powershell.exe -executionpolicy bypass -file %Directory%\Install_Citrix_Director_Roles.ps1
Log files are created in the directory C:\Logs\Citrix_Director_Roles, but you can change this to any directory you want (see lines 96 and 97).
Note: The installation of the roles and features may require a reboot. The reboot is NOT part of the example script. You can either add it to the script yourself or if you use a deployment tool such as Microsoft SCCM, you can add a reboot task in the task sequence. |
If you want to go one step further and create an SCCM package as well, please follow the step-by-step explanation in the article Deep dive creating SCCM packages for Citrix.
Part 2: Install Citrix Director
The second part of this article focuses on the installation of the Citrix Director. So how do you start?
- Create an installation directory on the local computer or a file share (UNC path). For example C:\Temp\Citrix\Director.
- Create a subdirectory called Files.
- Download and extract the XenDesktop 7.16 ISO file to the folder Files in the installation directory. The contents of the directory Files should now look like this:
- Copy the complete PowerShell script at the end of this paragraph to a new PS1 file (e.g. Install_CitrixDirector.ps1) and add this file to the root of your installation directory (not in the subdirectory Files).
- Execute the PowerShell script:
powershell.exe -file C:\Temp\Citrix\DeliveryController\Install_CitrixDirector.ps1
The basic command line to install Director is as follows:
1 |
XenDesktopServerSetup.exe /components desktopdirector /configure_firewall /noreboot /quiet /logpath C:\Logs |
The installation file XenDesktopServerSetup.exe is located in the subfolder x64\XenDesktop Setup.
Complete script for installing Citrix Director
In case you use my installation template, this is what the complete script, including logging and error handling, looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
#========================================================================== # # Install Citrix Director # # AUTHOR: Dennis Span (https://dennisspan.com) # DATE : 26.05.2017 # # COMMENT: # This script has been prepared for Windows Server 2008 R2, 2012 R2 and 2016. # # The version of Director used in this script is 7.16 (released in Q4 2017), # but it also works for Director 7.13 to 7.15. # #========================================================================== # Get the script parameters if there are any param ( # The only parameter which is really required is 'Uninstall' # If no parameters are present or if the parameter is not # 'uninstall', an installation process is triggered [string]$Installationtype ) # define Error handling # note: do not change these values $global:ErrorActionPreference = "Stop" if($verbose){ $global:VerbosePreference = "Continue" } # FUNCTION DS_WriteLog #========================================================================== Function DS_WriteLog { <# .SYNOPSIS Write text to this script's log file .DESCRIPTION Write text to this script's log file .PARAMETER InformationType This parameter contains the information type prefix. Possible prefixes and information types are: I = Information S = Success W = Warning E = Error - = No status .PARAMETER Text This parameter contains the text (the line) you want to write to the log file. If text in the parameter is omitted, an empty line is written. .PARAMETER LogFile This parameter contains the full path, the file name and file extension to the log file (e.g. C:\Logs\MyApps\MylogFile.log) .EXAMPLE DS_WriteLog -InformationType "I" -Text "Copy files to C:\Temp" -LogFile "C:\Logs\MylogFile.log" Writes a line containing information to the log file .Example DS_WriteLog -InformationType "E" -Text "An error occurred trying to copy files to C:\Temp (error: $($Error[0]))" -LogFile "C:\Logs\MylogFile.log" Writes a line containing error information to the log file .Example DS_WriteLog -InformationType "-" -Text "" -LogFile "C:\Logs\MylogFile.log" Writes an empty line to the log file #> [CmdletBinding()] Param( [Parameter(Mandatory=$true, Position = 0)][String]$InformationType, [Parameter(Mandatory=$true, Position = 1)][AllowEmptyString()][String]$Text, [Parameter(Mandatory=$true, Position = 2)][AllowEmptyString()][String]$LogFile ) $DateTime = (Get-Date -format dd-MM-yyyy) + " " + (Get-Date -format HH:mm:ss) if ( $Text -eq "" ) { Add-Content $LogFile -value ("") # Write an empty line } Else { Add-Content $LogFile -value ($DateTime + " " + $InformationType + " - " + $Text) } } #========================================================================== # FUNCTION DS_InstallOrUninstallSoftware #========================================================================== Function DS_InstallOrUninstallSoftware { <# .SYNOPSIS Install or uninstall software (MSI or SETUP.exe) .DESCRIPTION Install or uninstall software (MSI or SETUP.exe) .PARAMETER File This parameter contains the file name including the path and file extension, for example C:\Temp\MyApp\Files\MyApp.msi or C:\Temp\MyApp\Files\MyApp.exe. .PARAMETER Installationtype This parameter contains the installation type, which is either 'Install' or 'Uninstall'. .PARAMETER Arguments This parameter contains the command line arguments. The arguments list can remain empty. In case of an MSI, the following parameters are automatically included in the function and do not have to be specified in the 'Arguments' parameter: /i (or /x) /qn /norestart /l*v "c:\Logs\MyLogFile.log" .EXAMPLE DS_InstallOrUninstallSoftware -File "C:\Temp\MyApp\Files\MyApp.msi" -InstallationType "Install" -Arguments "" Installs the MSI package 'MyApp.msi' with no arguments (the function already includes the following default arguments: /i /qn /norestart /l*v $LogFile) .Example DS_InstallOrUninstallSoftware -File "C:\Temp\MyApp\Files\MyApp.msi" -InstallationType "Uninstall" -Arguments "" Uninstalls the MSI package 'MyApp.msi' (the function already includes the following default arguments: /x /qn /norestart /l*v $LogFile) .Example DS_InstallOrUninstallSoftware -File "C:\Temp\MyApp\Files\MyApp.exe" -InstallationType "Install" -Arguments "/silent /logfile:C:\Logs\MyApp\log.log" Installs the SETUP file 'MyApp.exe' #> [CmdletBinding()] Param( [Parameter(Mandatory=$true, Position = 0)][String]$File, [Parameter(Mandatory=$true, Position = 1)][AllowEmptyString()][String]$Installationtype, [Parameter(Mandatory=$true, Position = 2)][AllowEmptyString()][String]$Arguments ) $FileName = ($File.Split("\"))[-1] $FileExt = $FileName.SubString(($FileName.Length)-3,3) # Prepare variables if ( !( $FileExt -eq "MSI") ) { $FileExt = "SETUP" } if ( $Installationtype -eq "Uninstall" ) { $Result1 = "uninstalled" $Result2 = "uninstallation" } else { $Result1 = "installed" $Result2 = "installation" } $LogFileAPP = Join-path $LogDir ( "$($Installationtype)_$($FileName.Substring(0,($FileName.Length)-4))_$($FileExt).log" ) # Logging DS_WriteLog "I" "File name: $FileName" $LogFile DS_WriteLog "I" "File full path: $File" $LogFile if ([string]::IsNullOrEmpty($Arguments)) { # check if custom arguments were defined DS_WriteLog "I" "File arguments: <no arguments defined>" $LogFile } Else { DS_WriteLog "I" "File arguments: $Arguments" $LogFile } # Install the MSI or SETUP.exe DS_WriteLog "-" "" $LogFile DS_WriteLog "I" "Start the $Result2" $LogFile if ( $FileExt -eq "MSI" ) { if ( $Installationtype -eq "Uninstall" ) { $FixedArguments = "/x ""$File"" /qn /norestart /l*v ""$LogFileAPP""" } else { $FixedArguments = "/i ""$File"" /qn /norestart /l*v ""$LogFileAPP""" } if ([string]::IsNullOrEmpty($Arguments)) { # check if custom arguments were defined $arguments = $FixedArguments DS_WriteLog "I" "Command line: Start-Process -FilePath 'msiexec.exe' -ArgumentList $arguments -Wait -PassThru" $LogFile $process = Start-Process -FilePath 'msiexec.exe' -ArgumentList $arguments -Wait -PassThru } Else { $arguments = $FixedArguments + " " + $arguments DS_WriteLog "I" "Command line: Start-Process -FilePath 'msiexec.exe' -ArgumentList $arguments -Wait -PassThru" $LogFile $process = Start-Process -FilePath 'msiexec.exe' -ArgumentList $arguments -Wait -PassThru } } Else { if ([string]::IsNullOrEmpty($Arguments)) { # check if custom arguments were defined DS_WriteLog "I" "Command line: Start-Process -FilePath ""$File"" -Wait -PassThru" $LogFile $process = Start-Process -FilePath "$File" -Wait -PassThru } Else { DS_WriteLog "I" "Command line: Start-Process -FilePath ""$File"" -ArgumentList $arguments -Wait -PassThru" $LogFile $process = Start-Process -FilePath "$File" -ArgumentList $arguments -Wait -PassThru } } # Check the result (the exit code) of the installation switch ($Process.ExitCode) { 0 { DS_WriteLog "S" "The software was $Result1 successfully (exit code: 0)" $LogFile } 3 { DS_WriteLog "S" "The software was $Result1 successfully (exit code: 3)" $LogFile } # Some Citrix products exit with 3 instead of 0 1603 { DS_WriteLog "E" "A fatal error occurred (exit code: 1603). Some applications throw this error when the software is already (correctly) installed! Please check." $LogFile } 1605 { DS_WriteLog "I" "The software is not currently installed on this machine (exit code: 1605)" $LogFile } 3010 { DS_WriteLog "W" "A reboot is required (exit code: 3010)!" $LogFile } default { [string]$ExitCode = $Process.ExitCode DS_WriteLog "E" "The $Result2 ended in an error (exit code: $ExitCode)!" $LogFile Exit 1 } } } #========================================================================== ################ # Main section # ################ # Disable File Security $env:SEE_MASK_NOZONECHECKS = 1 # Custom variables [edit] $BaseLogDir = "C:\Logs" # [edit] add the location of your log directory here $PackageName = "Citrix Director (installation)" # [edit] enter the display name of the software (e.g. 'Arcobat Reader' or 'Microsoft Office') # Global variables $StartDir = $PSScriptRoot # the directory path of the script currently being executed if (!($Installationtype -eq "Uninstall")) { $Installationtype = "Install" } $LogDir = (Join-Path $BaseLogDir $PackageName).Replace(" ","_") $LogFileName = "$($Installationtype)_$($PackageName).log" $LogFile = Join-path $LogDir $LogFileName # Create the log directory if it does not exist if (!(Test-Path $LogDir)) { New-Item -Path $LogDir -ItemType directory | Out-Null } # Create new log file (overwrite existing one) New-Item $LogFile -ItemType "file" -force | Out-Null DS_WriteLog "I" "START SCRIPT - $Installationtype $PackageName" $LogFile DS_WriteLog "-" "" $LogFile ################################################# # INSTALL CITRIX DIRECTOR # ################################################# DS_WriteLog "I" "Install the Citrix Director" $LogFile DS_WriteLog "-" "" $LogFile $File = Join-Path $StartDir "Files\x64\XenDesktop Setup\XenDesktopServerSetup.exe" $Arguments = "/components desktopdirector /configure_firewall /noreboot /quiet /logpath ""$LogDir""" DS_InstallOrUninstallSoftware -File $File -InstallationType "Install" -Arguments $Arguments # Enable File Security Remove-Item env:\SEE_MASK_NOZONECHECKS DS_WriteLog "-" "" $LogFile DS_WriteLog "I" "End of script" $LogFile |
Execute the script as follows, for example:
powershell.exe -file C:\Temp\Citrix\Director\Install_CitrixDirector.ps1
In case you get a security warning, set the execution policy to allow the script to run:
powershell.exe -executionpolicy bypass -file C:\Temp\Citrix\Director\Install_CitrixDirector.ps1
Log files are created in the directory C:\Logs\Citrix_Director_(installation), but you can change this to any directory you want (see lines 187 and 188).
Part 3: Configure Citrix Director
The third part of this article focuses on the configuration of the Citrix Director.
If you have created the installation directory as described in part 2, you can continue as follows:
- Make sure that your Citrix License Server is up and running (the latest version). The actual product licenses do not need to be installed yet. Please read the article Citrix License Server unattended installation with PowerShell and SCCM for more information on how to install and configure your Citrix License Server using PowerShell.
- Make sure that you have at least one XenDesktop site up and running including the monitoring database. Citrix Director retrieves its information from this database. Please see the article Citrix Delivery Controller unattended installation with PowerShell and SCCM for more information on how to create and configure a XenDesktop site.
- Copy the complete PowerShell script at the end of this part to a new PS1 file (e.g. Configure_CitrixDirector.ps1) and add this file to the root of your installation directory (not in the subdirectory Files).
- Execute the PowerShell script:
powershell.exe -file C:\Temp\Citrix\Director\Configure_CitrixDirector.ps1
The configuration steps below are in direct alignment with the structure of Carl Stalhood’s article Director 7.16.
The following paragraphs describe each step in the configuration of Director individually. The complete PowerShell script at the end of this part includes all of these steps. When you use this script, you do not need to execute any of the individual steps described below!
Note: Many of the customizations below involve modifying the web.config file, which by default is located in the folder C:\inetpub\wwwroot\Director. The settings in the web.config file can also be managed manually in the IIS manager. Go to IIS Manager \ You server \ Sites \ Default Web Site \ Director. In the right pane open Application Settings. All of these settings are read directly from the web.config file. |
Add XenDesktop controller (replacing localhost)
To install Director on a dedicated server (= on a different server than a Delivery Controller), you need to enter the name of (only) one controller for each site. Director then automatically discovers all other controllers in the same site(s).
As per Citrix: “When Director is installed on the Controller, it is automatically configured with localhost as the server address, and Director communicates with the local Controller by default”. […] Specify only one Controller address for each site that you monitor. Director automatically discovers all other Controllers in the same Site and falls back to those other Controllers if the Controller you specified fails”.
The easiest way to add the controller in an automated procedure is to modify the file web.config, which by default is located in the folder C:\inetpub\wwwroot\Director. This file contains most of the local Director settings.
The line which needs to be modified is:
<add key=”Service.AutoDiscoveryAddresses” value=”localhost” />
The value localhost needs to be replaced by the DNS name of one or more controllers (one controller per site). In the complete PowerShell script at the end of this part, the list of controllers is retrieved directly from the XenDesktop site database (using an SQL query). I use this same technique for configuring the XenDesktop site. Please make sure to customize the variables $DatabaseServer, $DatabaseServerPort, and $DatabaseName_Site in lines 128 to 130.
After retrieving the list of controllers, the script tries to add the first controller to the web.config file. Before adding the controller, a connection test is performed (using the PowerShell cmdlet Test-Connection) to see if the controller is up and running. If the test is successful, the controller is written to the web.config file. If an error occurs, the script continues with the following controller in the list. If none of the controllers are reachable, the script ends in an error.
If you want to take a closer look at the PowerShell code, please see lines 186 to 272 in the complete PowerShell script.
Note: The web.config file is written in XML format, so it can be easily modified using only a few lines of PowerShell code. For example, this is the code for modifying the key Service.AutoDiscoveryAddresses in the XML node Configuration \ appSettings \ add: The web.config file is stored in ANSI format, but in the first line of the file, the encoding is set to UTF-8. When modifying an XML file, PowerShell saves the file automatically as UTF-8. This seems to be no problem; the file is still interpreted correctly. For more information on managing XML files using PowerShell, please read the Microsoft blog Update XML File using PowerShell. |
Set Director Default Webpage
To make the Director website the default page within the IIS site, three steps are required:
- Create the file Director.html in the IIS root directory (default: C:\inetpub\wwwroot). You can use a different name other than Director.html. In this article, the redirection file is called Director.html.
- Remove the entry Director.html from the list of default documents in case it exists. This is an additional step to ensure that the next step works as planned.
- Add the value Director.html to the list of default documents and add the index number 0 (this puts Director.html to the top of the list).
Creating the Director.html is quite straightforward. All you need is the Set-Content PowerShell cmdlet, like this:
1 |
Set-Content "C:\inetpub\wwwroot\Director.html" -value ("$Text") -Encoding Default |
I have added the -Encoding parameter to ensure that the file is stored as a default ANSI file. The actual content is stored in the variable $Text. This variable is stored as a here-string. Here-strings are very useful in cases where the text is complex in terms of characters such as multiple quotation marks. A here-string allows you to write your text exactly the way you want it and PowerShell will interpret it literally. Please note that the here-string has to start from the first character of the line (without any indenting to the right). Otherwise, the here-string will fail.
The contents of the variable $Text is as follows:
1 2 3 4 5 6 7 |
$Text = @" <script type="text/javascript"> <!-- window.location="https://MyDomain.com/Director"; // --> </script> "@ |
There are two possible values for the window.location:
- For HTTP connections: window.location=”/Director“;
- For HTTPS connections: window.location=”https://MyURL/Director“;
In the complete PowerShell script at the end of this part, the Director URL is stored in the variable $DirectorURL (see line 129).
The next step is to check if Director.html is already part of the list of default documents. If yes, remove it. Removing it will make sure that the document is set to the first position in the list of default documents in the next step.
To check if the file is already present, I use the following PowerShell command:
1 2 3 |
Import-Module WebAdministration Get-WebConfigurationProperty -Filter "//defaultDocument" -PSPath "IIS:\sites\Default Web Site" -Name files.collection | Where-Object { $_.value -eq "Director.html" } |
To remove the file from the list in case it exists, I use the following PowerShell command:
1 2 3 |
Import-Module WebAdministration Remove-WebConfigurationProperty -Filter "//defaultDocument" -PSPath "IIS:\sites\Default Web Site" -Name files -atElement @{value="Director.html"} -ErrorAction Stop |
The final step is to (once again) add the Director.html to the list of default documents and to put this file to the top of the list. The PowerShell command is as follows:
1 2 3 |
Import-Module WebAdministration Add-WebConfiguration -Filter "//defaultDocument/files" -PSPath "IIS:\sites\Default Web Site" -AtIndex 0 -Value @{value="Director.html"} |
Please take note that, in the complete PowerShell script at the end of this part, the code looks slightly different. The reason for this is that I use variables for values such as Director.html as well as try\catch statements and lots of error handling and logging. You find the PowerShell code in lines 274 to 336.
Reference:
- How to Make Director the Default Page within IIS
https://support.citrix.com/article/CTX223907(link expired) - PowerTip: Use Here-Strings with PowerShell
https://devblogs.microsoft.com/scripting/powertip-use-here-strings-with-powershell/ - Windows PowerShell Tip of the Week
https://technet.microsoft.com/en-us/library/ee692792.aspx
Director Spinning Circle (enable multiple IIS Site Bindings)
In case you use multiple IIS site bindings, you will need to modify the Director’s web.config file, which by default is located in the folder C:\inetpub\wwwroot\Director.
The line which needs to be modified is:
<serviceHostingEnvironment aspNetCompatibilityEnabled=”true”/>
The key multipleSiteBindingsEnabled must be added and set to true:
<serviceHostingEnvironment aspNetCompatibilityEnabled=”true” multipleSiteBindingsEnabled=”true” />
The PowerShell code is as follows:
1 2 3 4 |
$xml = [xml](Get-Content "C:\inetpub\wwwroot\Director\web.config") $node = $xml.configuration."system.serviceModel".serviceHostingEnvironment $node.SetAttribute("multipleSiteBindingsEnabled","true"); # Add a new key/value $xml.Save("C:\inetpub\wwwroot\Director\web.config") |
The complete PowerShell script at the end of this part enables the binding of multiple sites. If you have not enabled this feature in IIS, please remove (or comment out) the corresponding lines (lines 338 to 363).
For more information on how to modify XML files using PowerShell, please see the following side note.
Reference:
- Supporting Multiple IIS Site Bindings
https://msdn.microsoft.com/en-us/library/ee358763(v=vs.110).aspx - Citrix Director Becomes Unresponsive after Submitting the Credentials when IIS X-Frame-Options is enabled
https://support.citrix.com/article/CTX202564
Director Spinning Circle (enable IIS X-Frame-Options header)
In case you added an X-Frame-Options header in IIS, you will need to modify the Director’s web.config file, which by default is located in the folder C:\inetpub\wwwroot\Director.
The line which needs to be modified is:
<add name=”X-Frame-Options” value=”SAMEORIGIN” />
The value SAMEORIGIN must be changed to ALLOW-FROM %URL%/director/default.html:
<add name=”X-Frame-Options” value=”ALLOW-FROM http://localhost/director/default.html” />
You can also use the FDQN, for example, ALLOW-FROM http://director.home.local/director/default.html and you can use https of course.
The PowerShell code is as follows:
1 2 3 4 |
$xml = [xml](Get-Content "C:\inetpub\wwwroot\Director\web.config") $node = $xml.configuration."system.webserver".httpProtocol.customHeaders.add | where {$_.Name -eq 'X-Frame-Options'} $node.value = "ALLOW-FROM http://localhost/director/default.html" # Change an existing value $xml.Save("C:\inetpub\wwwroot\Director\web.config") |
The complete PowerShell script at the end of this part configures the IIS X-Frame custom header. If you have not added an x-Frame-Options header in IIS, please remove (or comment out) the corresponding lines (lines 362 to 387).
For more information on how to modify XML files using PowerShell, please see the following side note.
Reference:
- Mitigating framesniffing with the X-Frame-Options header
https://support.microsoft.com/en-us/office/mitigating-framesniffing-with-the-x-frame-options-header-1911411b-b51e-49fd-9441-e8301dcdcd79?ui=en-us&rs=en-us&ad=us - Citrix Director Becomes Unresponsive after Submitting the Credentials when IIS X-Frame-Options is enabled
https://support.citrix.com/article/CTX202564
Pre-populate the domain field
It is possible to pre-populate the domain to the name of your choice. For this, it is necessary to modify the logon.aspx file, which by default is located in the folder C:\inetpub\wwwroot\Director. The user is still able to change the domain name if needed.
Line 450 needs to be extended with the variable Text=”MyDomain”. The original line is:
<asp:TextBox ID=”Domain” runat=”server” CssClass=”text-box” onfocus=”showIndicator(this);”
The modified line is as follows:
<asp:TextBox ID=”Domain” runat=”server” Text=”MyDomain” CssClass=”text-box” onfocus=”showIndicator(this);”
The file logon.aspx has to be stored in UTF-8 format!
The complete PowerShell script at the end of this part adds the domain name defined in the variable $DomainName in line 133 of the script.
If you want to take a closer look at the PowerShell code, please see lines 392 to 429.
Set the session time-out
By default, the idle time session limit of the Director is 245 minutes. This value can be changed in the Director’s web.config file, which by default is located in the folder C:\inetpub\wwwroot\Director.
The line which needs to be modified is:
<sessionState mode=”Custom” cookieless=”UseCookies” regenerateExpiredSessionId=”true” timeout=”245” cookieName=”DESKTOPDIRECTORSESSION” customProvider=”Citrix.Dmc.WebService.CustomSessionStateProvider”>
The PowerShell code is as follows:
1 2 3 4 |
$xml = [xml](Get-Content "C:\inetpub\wwwroot\Director\web.config") $node = $xml.configuration."system.web".sessionState $node.timeout = $SessionTimeOut # Change an existing value $xml.Save("C:\inetpub\wwwroot\Director\web.config") |
The value of the key timeout can be changed to any setting you require. In the complete PowerShell script at the end of this part, the time-out is set at the default of 245 minutes. In case you would like to change it, simply change the value $SessionTimeOut in line 138 to the number of minutes you require. If you want to take a closer look at the PowerShell code, please see lines 428 to 445.
For more information on how to modify XML files using PowerShell, please see the following side note.
Disable SSL Check
On the login page of Director, the message You are not using a secure connection is displayed when you are not connecting through SSL. To prevent this message from appearing, the value UI.EnableSslCheck has to be set to false. This value can be changed in the Director’s web.config file, which by default is located in the folder C:\inetpub\wwwroot\Director.
The line which needs to be modified is:
<add key=”UI.EnableSslCheck” value=”false” />
The value has to be changed from true to false.
The PowerShell code is as follows:
1 2 3 4 |
$xml = [xml](Get-Content "C:\inetpub\wwwroot\Director\web.config") $node = $xml.configuration.appSettings.add | where {$_.Key -eq 'UI.EnableSslCheck'} $node.value = "false" # Change an existing value $xml.Save("C:\inetpub\wwwroot\Director\web.config") |
In the complete PowerShell script at the end of this part, the value is set to false. If you want to take a closer look at the PowerShell code, please see lines 450 to 467.
For more information on how to modify XML files using PowerShell, please see the following side note.
Disable Activity Manager
As per Citrix: “By default, the Activity Manager in Director displays a list of all running applications for a user’s session. To protect the privacy of users and the applications they are running, you can disable the Applications tab from listing running applications.”
To disable the listing of running applications, the value UI.TaskManager.EnableApplications has to be set to false. This value can be changed in the Director’s web.config file, which by default is located in the folder C:\inetpub\wwwroot\Director.
The line which needs to be modified is:
<add key=”UI.TaskManager.EnableApplications” value=”false” />
The value has to be changed from true to false.
The PowerShell code is as follows:
1 2 3 4 |
$xml = [xml](Get-Content "C:\inetpub\wwwroot\Director\web.config") $node = $xml.configuration.appSettings.add | where {$_.Key -eq 'UI.TaskManager.EnableApplications'} $node.value = "false" # Change an existing value $xml.Save("C:\inetpub\wwwroot\Director\web.config") |
In the complete PowerShell script at the end of this part, the value is set to false. If you want to take a closer look at the PowerShell code, please see lines 469 to 486.
For more information on how to modify XML files using PowerShell, please see the following side note.
Large Active Directory / Multiple Forests
As per Citrix: “When searching user accounts, the search process is either slow or it fails within Desktop Director”.
This may happen in large Active Directory environments or in an environment with multiple forests. The reason is that by default Director searches all the Global Catalogs in the Active Directory Forest using LDAP. In large environments, this query can take some time or even time out.
By adding the value Connector.ActiveDirectory.Domains and setting it to false, Director only searches the current domain. By default, this value is hidden and set to true. This means that you will not find this value in the web.config file; it has to be created. The default location of the web.config file is C:\inetpub\wwwroot\Director.
The line which needs to be created is:
<add key=”Connector.ActiveDirectory.ForestSearch” value=”false” />
The PowerShell code to add a new element is as follows:
1 2 3 4 5 6 |
$xml = [xml](Get-Content "C:\inetpub\wwwroot\Director\web.config") # Open the XML file $NewElement = $xml.CreateElement("add") # Create a new XML element called 'add' (at this moment this is only created in memory) -> do not use capital letters or the XML file will not be accepted $NewElement.SetAttribute("key","Connector.ActiveDirectory.ForestSearch") # Add a new key/value combination: key="Connector.ActiveDirectory.ForestSearch" $NewElement.SetAttribute("value","false") # Add a new key/value combination: value="false" $xml.configuration.appSettings.AppendChild($NewElement) # Add the new element in the correct part of the XML file, under <configuration> \ <appSettings>' $xml.Save(C:\inetpub\wwwroot\Director\web.config) # Save the XML file |
In case Director should search more than the current domain, add the list of domains to the value Connector.ActiveDirectory.Domains. The line which needs to be changed is:
<add key=”Connector.ActiveDirectory.Domains” value=”(user),(server)” />
The new line is as follows:
<add key=”Connector.ActiveDirectory.Domains” value=”MyDomain1.com,MyDomain2.com” />
The PowerShell code is as follows:
1 2 3 4 |
$xml = [xml](Get-Content "C:\inetpub\wwwroot\Director\web.config") $node = $xml.configuration.appSettings.add | where {$_.Key -eq 'Connector.ActiveDirectory.Domains'} $node.value = $SearchableDomains # Change an existing value $xml.Save("C:\inetpub\wwwroot\Director\web.config") |
Please take note that, in the complete PowerShell script at the end of this part, the above code looks slightly different, because I use variables, try\catch statements, and lots of error handling and logging. The list of custom search domains is stored in the variable $SearchableDomains (see line 142). If you want to take a closer look at the complete PowerShell code, please see lines 488 to 527.
For more information on how to modify XML files using PowerShell, please see the following side note.
Saved Filters (change location)
In Director you can create and save filters. By default, these filters are stored on the Director server itself in the directory C:\Inetpub\wwwroot\Director\UserData.
To change this path to a different directory (a network share for example), the value Service.UserSettingsPath in the Director’s web.config file needs to be changed. The web.config file is located in the folder C:\inetpub\wwwroot\Director by default.
The line which needs to be changed is:
<add key=”Service.UserSettingsPath” value=”..\UserData” />
The new line should be as follows:
<add key=”Service.UserSettingsPath” value=”\\MyServer\MyShare” />
The PowerShell code is as follows:
1 2 3 4 |
$xml = [xml](Get-Content "C:\inetpub\wwwroot\Director\web.config") $node = $xml.configuration.appSettings.add | where {$_.Key -eq 'Service.UserSettingsPath'} $node.value = $SavedFiltersLocation # Change an existing value $xml.Save("C:\inetpub\wwwroot\Director\web.config") |
Please make sure that the directory you enter exists and that all administrators have the appropriate permissions.
If you want to take a closer look at the complete PowerShell code, please see lines 526 to 543 in the complete PowerShell script. The new directory for the saved filters is stored in the variable $SavedFiltersLocation (see line 137).
For more information on how to modify XML files using PowerShell, please see the following side note.
Director and Self-Service Password Reset (SSPR)
Installing the StoreFront Self-Service Password Reset (SSPR) tool on the same server as Director may break Director, unless:
- Multiple IIS Site Bindings is enabled;
- Port 8080 is removed from the Default Web Site bindings.
The complete PowerShell script at the end of this part first checks if multiple IIS site bindings are enabled. The script reads the XML node configuration \ “system.serviceModel” \ serviceHostingEnvironment \ multipleSiteBindingsEnabled in the file C:\inetpub\wwwroot\Director\web.config. If this setting exists and is set to true, no further action is necessary. If this setting is missing or set to false, the script continues to remove port 8080 from the Default Web Site bindings using the Remove-WebBinding PowerShell cmdlet.
1 2 3 4 5 |
# Import the IIS Web Administration module Import-Module WebAdministration # Remove port 8080 from the "Default Web Site" Remove-WebBinding -name "Default Web Site" -Port 8080 |
If you want to take a closer look at the PowerShell code, please see lines 548 to 582.
Director Grooming
By default in XenDesktop 7.16, historical data is groomed at either 8, 32, or 365 days, depending on your license. As per Citrix;
- Trend reports of up to last year (365 days) are available on platinum-licensed sites.
- Trend reports of up to the last month (31 days) are available on enterprise-licensed sites.
- Trend reports of up to the last 7 days on non-platinum and non-enterprise licensed sites.
These values are configured on the Citrix Delivery Controller, not in Director. See the section Configure grooming settings in the article Citrix Delivery Controller unattended installation with PowerShell and SCCM on how to configure the grooming settings using PowerShell.
Director Single Sign-on
You can configure Director to support Integrated Windows Authentication (Single Sign-on). This setup involves some configurations in Active Directory and some on the local Director server. I recommend configuring Active Directory manually and automating the modifications on the local Director server.
Configure the following settings manually. In case you do not load balance Director, do the following:
- Set up the http SPN for the Director server using the setspn.exe command: setspn -s http/MyURL MyDomain\MyDirectorServer. The variable MyURL has to be entered without the prefix http:// or https://.
- Go to the properties of the computer account in Active Directory. On the tab Delegation, set Trust this computer for delegation to any service (Kerberos only).
- Repeat the previous two steps for each non-load balanced Director server in your environment.
In case you load balance Director, you can ignore the previous manual configurations. Please configure the following:
- Create a (service) account in Active Directory to be used for the IIS application pool identity;
- Set up the http SPN for this service account using the setspn.exe command: setspn -s http/MyURL MyDomain\MyServiceAccount. The variable MyURL has to be entered without the prefix http:// or https://.
- Go back to the service account in Active Directory. A new tab called Delegation is now available. Set delegation to Trust this user for delegation to any service (Kerberos only).
In case your security department is not too happy with the delegation Trust this user for delegation to any service (Kerberos only) (circled in red in the image below), it is possible to narrow it down to specific users or computers (so-called constrained delegation). Select Trust this computer for delegation to specified services only (circled in orange in the image below). Make sure that Use Kerberos only is selected. Click add to add the computer accounts that host the Director service.
The example below also applies when configuring delegation on the user (service) account instead of the computer account.
Click the Users or Computers button and add the Director servers:
Locate and select the http (Director) service.
Repeat these steps for each Director server.
Note: I did not fully test the scenario with constrained delegation. It may be that more services need to be added.
Now we can continue to configure the local Director server. In total, we need to configure four IIS settings:
Enable Windows authentication on the Director site
The PowerShell command is as follows:
1 2 3 |
Import-Module WebAdministration Set-WebConfigurationProperty -filter "/system.webServer/security/authentication/windowsAuthentication" -name "enabled" -value "true" -PSPath "IIS:\sites\Default Web Site\Director" |
Disable anonymous authentication on the Director site
The PowerShell command is as follows:
1 2 3 |
Import-Module WebAdministration Set-WebConfigurationProperty -filter "/system.webServer/security/authentication/anonymousAuthentication" -name "enabled" -value "false" -PSPath "IIS:\sites\Default Web Site\Director" |
Enter the application pool credentials (required when load balancing multiple Director servers)
The PowerShell command is as follows:
1 2 3 |
Import-Module WebAdministration Set-ItemProperty "IIS:\AppPools\Director" -name "processModel" -value @{userName="MyDomain\MyUser";password="MyPassword";identitytype=3} |
The identity type 3 refers to a “specific user account”. Please see the article Process Model Settings for an Application Pool <processModel> for more information. To manually add a password using the IIS Manager, open the IIS Manager and go to YourServer \ Application Pools \ Director. With a right-mouse-click on Director, go to Advanced Settings. In the section Process Model, click on Identity. Select the button with the three dots, choose Custom Account, and add your service account (e.g. MyDomain\MyServiceAccount).
Enable the use of the ‘useAppPoolCredentials’
In the previous step, we entered the service account in the application pool credentials. However, these credentials cannot be used right away. You see, by default in IIS, Kerberos service tickets are handled by the local machine. The local machine however has no permission to decrypt these tickets. By enabling the use of application pool credentials, we allow IIS to use the service account to decrypt Kerberos tickets from the clients. For more information, please see the following two articles: Internet Information Services (IIS) 7.0 Kernel Mode Authentication Settings and Configuring Kerberos Authentication on IIS Website. By default, certain sections of the Default Web Site in IIS are locked and configuration changes cannot be made. We first have to unlock the section system.webServer / security / authentication / windowsAuthentication. The PowerShell code is as follows:
1 2 3 4 5 6 |
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Web.Administration") $oIIS = New-Object Microsoft.Web.Administration.ServerManager $oGlobalConfig = $oIIS.GetApplicationHostConfiguration() $oConfig = $oGlobalConfig.GetSection("system.webServer/security/authentication/windowsAuthentication", "Default Web Site") $oConfig.OverrideMode="Allow" $oIIS.CommitChanges() |
Now we can continue to enable the use of application pool credentials. This is the PowerShell command:
1 2 3 |
Import-Module WebAdministration Set-WebConfigurationProperty -filter "/system.webServer/security/authentication/windowsAuthentication" -name "useAppPoolCredentials" -value "true" -PSPath "IIS:\sites\Default Web Site" |
It is not necessary to disable the Kernel mode authentication if you configure the WindowsAuthentication element on the application pool (reference: Understanding Kernel mode authentication in IIS 7).
Securing password: There seems to be a security issue concerning passwords in IIS. According to the following article, passwords are encrypted in the IIS application pool configuration file when they are set using the IIS Manager or the executable AppCmd.exe: The configuration file for the application pools is located here: C:\Windows\System32\inetsrv\config\applicationHost.config. When you open the file after manually configuring the password, you see an encrypted string (as far as you can call this truly “encrypted”). When you use the PowerShell command, the password is stored in plain text. However, as soon as you view or export the application pool settings, the password is written in plain text, even if the password was stored encrypted:
This is a security bug in IIS and cannot be prevented (as far as my knowledge goes). If you do not want to store your password in plain-text format in your PowerShell script, please see the article Encrypting passwords in a PowerShell script. |
If you want to take a closer look at the complete PowerShell code, please see lines 592 to 658 in the complete PowerShell script. The user name and password for the application pool identity are stored in the variables $AppPoolUserName and $AppPoolPassword (see lines 126 to 127).
Multiple XenDesktop Sites
Adding an XenDesktop controller to Director is covered in the section Add XenDesktop controller (replacing localhost). You can re-use the PowerShell code to add multiple controllers, or you can extend the code to check multiple SQL databases and/or multiple SQL servers.
Please note that if you want to monitor multiple XenDesktop sites using one Director, only one controller per site should be entered in the web.config file. Do not enter more than one controller; Director automatically discovers the other controllers in the site.
Generate SNMP traps
SNMP traps are generated by the Delivery Controller, not Director. By default, the cmdlets are not present on a stand-alone Director server (e.g. Get-MonitorNotificationSnmpServerConfiguration).
Please see the following Citrix article on how to configure SNMP traps: Alerts and notifications.
SCOM Integration
Since Director 7.7, it is possible to aggregate SCOM alerts in the Director console. The way how to enable this is to:
- Enable WinRM (if not already enabled)
Remote management (WinRM) is a prerequisite. WinRM is enabled by default on operating systems from Windows Server 2012 and higher. On a server running Windows Server 2008, WinRM has to be explicitly enabled. - Add the SCOM server to the list of trusted hosts (WinRM)
- Configure SCOM for Director
Executing the command C:\inetpub\wwwroot\Director\tools\DirectorConfig.exe /configscom to configure SCOM for Director.
The DirectorConfig.exe command requires some input, such as the name of your SCOM server and the ports to use for WinRM (default: 5985 for HTTP and 5986 for HTTPS). The DirectorConfig.exe then continues to do the following:- Add new entries to the web.config file (default location: C:\inetpub\wwwroot\Director):
- <add key=”Connector.SCOM.ManagementServer” value=”MySCOMServer.mydomain.com” />
- <add key=”Connector.SCOM.Identity” value=”User” />
- <add key=”Connector.SCOM.WSManTransport” value=”http” />
- <add key=”Connector.SCOM.WSManPort” value=”5985” />
- On Windows Server 2008 R2:
Install ASP.NET (4.x) without changing existing web applications to use this version of ASP.Net. The command which is executed is C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe -ir is used. - On Windows Server 2012 and higher:
Install several Windows features using the Microsoft Deployment Image Servicing and Management tool (DISM.exe). The exact command executed by the DirectorConfig.exe is: “C:\Windows\system32\Dism.exe” /Online /Enable-Feature /FeatureName:IIS-NetFxExtensibility45 /FeatureName:IIS-ASPNET45 /FeatureName:WCF-HTTP-Activation45
- Add new entries to the web.config file (default location: C:\inetpub\wwwroot\Director):
Reference: Citrix article Alerts and notifications (in the section SCOM alerts).
Note: So how can I be so sure which features the DirectorConfig.exe installs? Well, in the verbose output of the DirectorConfig.exe (on a W2K12 and W2K16 server), I saw that the tool DISM.exe is used. By default, DISM.exe writes all output to a log file. This log file can be found here: C:\Windows\Logs\DISM\dism.log. After searching the log file for DISM.EXE: Executing command line I found the exact command line syntax. On a W2K8R2 server, DISM.exe is not used. Here, the executable aspnet_regiis.exe is used. The exact command executed by the DirectorConfig.exe is C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe -ir. |
Now that we know exactly what needs to be configured, we can start automating the procedure.
First, we will check if WinRM is enabled; if not, it will be enabled. The PowerShell code is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 |
try { Enter-PSsession -computername localhost -ErrorAction stop # If this works than WinRM is enabled DS_WriteLog "S" "WinRM (remote management) has already been enabled. Nothing to do" $LogFile } catch { try { Enable-PSRemoting -Force | out-Null DS_WriteLog "I" "WinRM (remote management) has been successfully enabled" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to enable WinRM (remote management) (error: $($Error[0]))" $LogFile Exit 1 } } |
The PowerShell command Enter-PSsession -computername localhost can be used to test if WinRM is up and running. To enable WinRM use the command Enable-PSRemoting -Force.
Reference: Microsoft article Enable-PSRemoting.
Add SCOM server to trusted hosts (WinRM)
The next step is to add the SCOM server to the list of WinRM trusted hosts, but first, we have to check the list of existing trusted hosts. I use the following PowerShell command for this:
1 |
[string]$ExistingTrustedHosts = (Get-Item WSMAN:\localhost\Client\TrustedHosts).Value |
The variable $ExistingTrustedHosts now contains the list of existing hosts. In case no trusted hosts exist, this variable is empty.
Now we will check the contents of the variable $ExistingTrustedHosts to verify if the SCOM server is perhaps already included in the list of trusted hosts:
1 2 3 |
if ( ($ExistingTrustedHosts).Contains("MySCOMServer") { [...] } |
If the SCOM server is not included in the list of trusted hosts, it is added in the next step:
1 |
Set-Item WSMAN:\localhost\Client\TrustedHosts -Value "MySCOMServer,$ExistingTrustedHosts" -force |
Besides the SCOM server, all previously existing trusted hosts are added as well. Without this, adding the SCOM server would overwrite all pre-existing values.
The web.config file, by default located in the directory C:\inetpub\wwwroot\Director, needs to be modified. The following PowerShell code adds the required four lines with the SCOM configuration:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
$xml = [xml](Get-Content "C:\inetpub\wwwroot\Director\web.config") # Open the XML file # Create new element 'Connector.SCOM.ManagementServer' $NewElement = $xml.CreateElement("add") # Create a new XML element called 'add' (at this moment this is only created in memory) -> do not use capital letters or the XML file will not be accepted $NewElement.SetAttribute("key","Connector.SCOM.ManagementServer") # Add the key value for the new key/value combination $NewElement.SetAttribute("value",$SCOMServer) # Add the value for the new key/value combination $xml.configuration.appSettings.AppendChild($NewElement) | Out-Null # Add the new element in the correct part of the XML file, under <configuration> \ <appSettings>' # Create new element 'Connector.SCOM.Identity' $NewElement = $xml.CreateElement("add") # Create a new XML element called 'add' (at this moment this is only created in memory) -> do not use capital letters or the XML file will not be accepted $NewElement.SetAttribute("key","Connector.SCOM.Identity") # Add the key value for the new key/value combination $NewElement.SetAttribute("value","User") # Add the value for the new key/value combination $xml.configuration.appSettings.AppendChild($NewElement) | Out-Null # Add the new element in the correct part of the XML file, under <configuration> \ <appSettings>' # Create new element 'Connector.SCOM.WSManTransport' $NewElement = $xml.CreateElement("add") # Create a new XML element called 'add' (at this moment this is only created in memory) -> do not use capital letters or the XML file will not be accepted $NewElement.SetAttribute("key","Connector.SCOM.WSManTransport") # Add the key value for the new key/value combination $NewElement.SetAttribute("value",$SCOMProtocol) # Add the value for the new key/value combination $xml.configuration.appSettings.AppendChild($NewElement) | Out-Null # Add the new element in the correct part of the XML file, under <configuration> \ <appSettings>' # Create new element 'Connector.SCOM.WSManPort' $NewElement = $xml.CreateElement("add") # Create a new XML element called 'add' (at this moment this is only created in memory) -> do not use capital letters or the XML file will not be accepted $NewElement.SetAttribute("key","Connector.SCOM.WSManPort") # Add the key value for the new key/value combination $NewElement.SetAttribute("value",$SCOMPort) # Add the value for the new key/value combination $xml.configuration.appSettings.AppendChild($NewElement) | Out-Null # Add the new element in the correct part of the XML file, under <configuration> \ <appSettings>' # Save the XMl file $xml.Save("C:\inetpub\wwwroot\Director\web.config") |
Since these lines are completely new, a new XML element has to be created in the existing node Configuration \ appSettings. The same function is executed four times; one for each of the four lines. The last line saves the new elements to the XML file (the web.config file).
Install ASP.NET (on Windows Server 2008 R2)
The last thing the DirectorConfig.exe does is to install ASP.NET using the executable aspnet_regiis.exe (default location: C:\Windows\Microsoft.NET\Framework\v4.0.30319) and the parameter -ir.
The following PowerShell code shows how to execute the aforementioned command using PowerShell.
1 2 3 4 5 6 7 8 9 10 |
$Error.Clear() DS_WriteLog "I" "The operating system is Windows Server 2008 (R2)" $LogFile DS_WriteLog "I" "Install ASP.NET (required for SCOM in Director)" $LogFile & "$env:SystemRoot\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe" -ir if ( $LASTEXITCODE -eq 0 ) { DS_WriteLog "S" "ASP.NET has been successfully installed" $LogFile } else { DS_WriteLog "E" "An error occurred trying to install ASP.NET (error: $($Error[0]))" $LogFile Exit 1 } |
Install Windows features (on Windows Server 2012 and higher)
The last thing the DirectorConfig.exe does is install three Windows features. The following PowerShell code shows how to add a feature using PowerShell and the DISM.exe.
1 2 3 4 5 6 7 8 9 10 11 |
$FeatureName = "IIS-NetFxExtensibility45" $Error.Clear() DS_WriteLog "I" "The operating system is Windows Server 2012 or higher" $LogFile DS_WriteLog "I" "Install Windows feature '$FeatureName' (required for SCOM in Director)" $LogFile & Dism.exe /Online /Enable-Feature /NoRestart /FeatureName:$FeatureName if ( $LASTEXITCODE -eq 0 ) { DS_WriteLog "S" "The Windows feature '$FeatureName' has been successfully installed" $LogFile } else { DS_WriteLog "E" "An error occurred trying to install the Windows feature '$FeatureName' (error: $($Error[0]))" $LogFile Exit 1 } |
The main command line in the above example is: Dism.exe /Online /Enable-Feature /NoRestart /FeatureName:$FeatureName
If you want to take a closer look at the complete PowerShell code, please see lines 673 to 805 in the complete PowerShell script. The SCOM server name, SCOM protocol, and SCOM port are stored in the variables $SCOMServer, $SCOMProtocol, and $SCOMPort (see lines 138 to 140). The script determines the version of the operating system automatically. It then executes the aspnet_regiis.exe or DISM.exe command depending on the version.
For more information on how to modify XML files using PowerShell, please see the following side note.
Complete script for configuring Citrix Director
In case you use my installation template, this is what the complete script, including logging and error handling, looks like.
Important! Please be aware that the complete script includes all of the above-mentioned configurations. In most cases, you will only need a few of these. Therefore, I strongly recommend you go through the script and remove or comment-out the sections you do not need. The script follows the same structure as Part 3 in this article. |
Please make sure to customize the variables from line 126 to line 144 to your requirements!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 |
#========================================================================== # # Configure Citrix Director # # AUTHOR: Dennis Span (https://dennisspan.com) # DATE : 29.05.2017 # # COMMENT: # This script has been prepared for Windows Server 2008 R2, 2012 R2 and 2016. # # The version of Director used in this script is 7.16 (released in Q4 2017), # but it also works for Director 7.13 to 7.15. # # This script configures the local Citrix Director server and includes the following sections: # -Add controller (replacing localhost) # -Set Director Default Webpage # -Director Spinning Circle (enable multiple IIS Site Bindings / MultiSiteBinding) # -Director Spinning Circle (enable IIS X-Frame-Options header) # -Pre-populate the domain field # -Set the session time-out # -Disable SSL check # -Disable Activity Manager # -Large Active Directory / Multiple Forests (search current domain or fix list of domains) # -Saved Filters (change location) # -Director and Self-Service Password Reset (SSPR) # -Director Grooming # -Director Single Sign-on # -Director – Multiple XenDesktop Sites # -Generate SNMP traps # -SCOM Integration # #========================================================================== # Get the script parameters if there are any param ( # The only parameter which is really required is 'Uninstall' # If no parameters are present or if the parameter is not # 'uninstall', an installation process is triggered [string]$Installationtype ) # define Error handling # note: do not change these values $global:ErrorActionPreference = "Stop" if($verbose){ $global:VerbosePreference = "Continue" } # FUNCTION DS_WriteLog #========================================================================== Function DS_WriteLog { <# .SYNOPSIS Write text to this script's log file .DESCRIPTION Write text to this script's log file .PARAMETER InformationType This parameter contains the information type prefix. Possible prefixes and information types are: I = Information S = Success W = Warning E = Error - = No status .PARAMETER Text This parameter contains the text (the line) you want to write to the log file. If text in the parameter is omitted, an empty line is written. .PARAMETER LogFile This parameter contains the full path, the file name and file extension to the log file (e.g. C:\Logs\MyApps\MylogFile.log) .EXAMPLE DS_WriteLog -InformationType "I" -Text "Copy files to C:\Temp" -LogFile "C:\Logs\MylogFile.log" Writes a line containing information to the log file .Example DS_WriteLog -InformationType "E" -Text "An error occurred trying to copy files to C:\Temp (error: $($Error[0]))" -LogFile "C:\Logs\MylogFile.log" Writes a line containing error information to the log file .Example DS_WriteLog -InformationType "-" -Text "" -LogFile "C:\Logs\MylogFile.log" Writes an empty line to the log file #> [CmdletBinding()] Param( [Parameter(Mandatory=$true, Position = 0)][String]$InformationType, [Parameter(Mandatory=$true, Position = 1)][AllowEmptyString()][String]$Text, [Parameter(Mandatory=$true, Position = 2)][AllowEmptyString()][String]$LogFile ) $DateTime = (Get-Date -format dd-MM-yyyy) + " " + (Get-Date -format HH:mm:ss) if ( $Text -eq "" ) { Add-Content $LogFile -value ("") # Write an empty line } Else { Add-Content $LogFile -value ($DateTime + " " + $InformationType + " - " + $Text) } } #========================================================================== ################ # Main section # ################ # Disable File Security $env:SEE_MASK_NOZONECHECKS = 1 # Custom variables [edit] $BaseLogDir = "C:\Logs" # [edit] add the location of your log directory here $PackageName = "Citrix Director (configure)" # [edit] enter the display name of the software (e.g. 'Arcobat Reader' or 'Microsoft Office') # Global variables $ComputerName = $env:ComputerName $StartDir = $PSScriptRoot # the directory path of the script currently being executed if (!($Installationtype -eq "Uninstall")) { $Installationtype = "Install" } $LogDir = (Join-Path $BaseLogDir $PackageName).Replace(" ","_") $LogFileName = "$($Installationtype)_$($PackageName).log" $LogFile = Join-path $LogDir $LogFileName # Create the log directory if it does not exist if (!(Test-Path $LogDir)) { New-Item -Path $LogDir -ItemType directory | Out-Null } # Create new log file (overwrite existing one) New-Item $LogFile -ItemType "file" -force | Out-Null DS_WriteLog "I" "START SCRIPT - $PackageName" $LogFile DS_WriteLog "-" "" $LogFile # ----------------------------------- # CUSTOMIZE THE FOLLOWING VARIABLES TO YOUR REQUIREMENTS # ----------------------------------- $AppPoolUserName ="MyDomain\MyUser" # The service account (user account) to be used for the application pool credentials $AppPoolPassword ="Password" # The password of the service account (user account) to be used for the application pool credentials $DatabaseServer = "MySQLServer" # The name of your SQL server or SQL server instance (e.g. "SQLServer1.mycompany.com" or "SQLCTX01.mycompany.com\InStance1") $DatabaseServerPort = "1433" # The SQL port number (1433 by default) $DatabaseName_Site = "MySiteDB" # The SQL database name of the site database. For example: 'CTX_Site_DB' $DirectorRedirectFileName = "Director.html" # The file name of the Director's redirection file without the path (for example: 'Director.html') $DirectorURL = "https://director.mycompany.com/Director" # The URL including 'http' or 'https' and virtual directory (for example: 'https://director.corp.com/Director' or '/Director' for http connections) $DomainName = "MyDomain" # The domain name used to pre-populate the 'logon.aspx' file $IISSiteName = "Default Web Site" # The IIS site name of Director (default is: 'Default Web Site') $IISRootDir = "$env:SystemDrive\inetpub\wwwroot" # The full path to the Director's IIS root directory (default location is: 'C:\inetpub\wwwroot') $LogonASPXFile = Join-Path $IISRootDir "Director\logon.aspx" # The full path and file name of the Director's 'logon.aspx' file (default location is: 'C:\inetpub\wwwroot\Director\logon.aspx') $SavedFiltersLocation = "\\MyServer\MyShare" # The full path of the 'saved filters' directory (e.g. "\\MyServer\MShare") $SCOMServer = "MySCOMServer.mycompany.com" # The name of the SCOM server (e.g. "MySCOMServer.mycompany.com"). $SCOMProtocol = "http" # The protocol used to communicate to the SCOM server (possible options are 'http' or 'https') $SCOMPort = "5985" # The port used to communicate to the SCOM server (default ports are 5985 for 'http' and 5986 for 'https') $SessionTimeOut = "245" # The time in minutes an idle session remains active (default = 245 minutes) $SearchableDomains = "MyDomain1.com,MyDomain2.com" # The comma separated list of domains to add to the Director's searchable domain list $SelfServicePort = 8080 # The port used for Citrix Self-Service Password Reset (default is port 8080) -> reference: http://www.jgspiers.com/citrix-self-service-password-reset/ $WebConfigFile = Join-Path $IISRootDir "Director\web.config" # The full path and file name of the Director's 'web.config' file (default location is: 'C:\inetpub\wwwroot\Director\web.config') # ----------------------------------- # Log variables DS_WriteLog "I" "Your custom variables:" $LogFile DS_WriteLog "I" "-Database server (+ instance): $DatabaseServer" $LogFile DS_WriteLog "I" "-Database server port: $DatabaseServerPort" $LogFile DS_WriteLog "I" "-Database name for XenDesktop site: $DatabaseName_Site" $LogFile DS_WriteLog "I" "-Director redirection file name: $DirectorRedirectFileName" $LogFile DS_WriteLog "I" "-Director URL: $DirectorURL" $LogFile DS_WriteLog "I" "-Domain name for 'logon.aspx' file: $DomainName" $LogFile DS_WriteLog "I" "-IIS site name: $IISSiteName" $LogFile DS_WriteLog "I" "-IIS root directory: $IISRootDir" $LogFile DS_WriteLog "I" "-List of searchable domains: $SearchableDomains" $LogFile DS_WriteLog "I" "-Web config file (full path + file name): $WebConfigFile" $LogFile DS_WriteLog "I" "-Logon.aspx file (full path + file name): $LogonASPXFile" $LogFile DS_WriteLog "I" "-'Saved Filter' location: $SavedFiltersLocation" $LogFile DS_WriteLog "I" "-SCOM server: $SCOMServer" $LogFile DS_WriteLog "I" "-SCOM protocol: $SCOMProtocol" $LogFile DS_WriteLog "I" "-SCOM port: $SCOMPort" $LogFile DS_WriteLog "I" "-Self-service port: $SelfServicePort" $LogFile DS_WriteLog "I" "-Service account (for IIS application pool): $AppPoolUserName" $LogFile DS_WriteLog "I" "-Service account password (for IIS application pool) : xxxxxx" $LogFile DS_WriteLog "I" "-Session time-out (in minutes): $SessionTimeOut" $LogFile DS_WriteLog "-" "" $LogFile # --------------------------------------------------------------------------------------------------------------------------- # Import the WebAdministration module DS_WriteLog "I" "Import the WebAdministration module" $LogFile try { Import-Module WebAdministration DS_WriteLog "S" "Successfully imported the WebAdministration module" $LogFile } catch { DS_WriteLog "E" "Cannot import the PowerShell module 'WebAdministration' (error: $($Error[0]))" $LogFile Exit 1 } DS_WriteLog "-" "" $LogFile ######################################## # Add controller (replacing localhost) ######################################## # Notes: # -Default location: C:\inetpub\wwwroot\Director\web.config # -In IIS Manager: Sites \ Default Web Site \ Director \ Application Settings # -Reference: Update XML File using PowerShell (https://blogs.msdn.microsoft.com/sonam_rastogi_blogs/2014/05/14/update-xml-file-using-powershell/) # ------------------------------------- # Check if a valid controller is entered in the web.config file DS_WriteLog "I" "Add controller (replacing localhost) in the 'web.config' file" $LogFile DS_WriteLog "I" "Check if a valid controller is entered in the 'web.config' file" $LogFile try { $xml = [xml](Get-Content $WebConfigFile) [string]$ListOfDDC = ($xml.configuration.appSettings.add | where {$_.Key -eq 'Service.AutoDiscoveryAddresses'}).Value if ( $ListOfDDC.Contains("localhost") ) { DS_WriteLog "I" "The current value '$ListOfDDC' is not valid. Director on a stand-alone server requires the DNS hostname of one controller per site" $LogFile DS_WriteLog "I" "Notes:" $LogFile DS_WriteLog "I" " -This script connects to the site database and retrieves the list of controllers. Only one is added to the 'web.config' file ($WebConfigFile)" $LogFile DS_WriteLog "I" " -Only one controller per site should be added. Director automatically discovers the other controllers in the site(s)" $LogFile DS_WriteLog "I" " -Multiple controllers can be added as long as each of them represents a different site" $LogFile # Connect to XenDesktop SQL site database and retrieve the list of XenDesktop controllers try { DS_WriteLog "I" "Connect to XenDesktop SQL site database and retrieve the list of XenDesktop controllers." $LogFile $SQL_ConnectionString = "Server=$DatabaseServer,$DatabaseServerPort;Database=$DatabaseName_Site;Integrated Security=True;" $SQL_Connection = New-Object System.Data.SqlClient.SqlConnection $SQL_Connection.ConnectionString = $SQL_ConnectionString $SQL_Connection.Open() $SQL_Command = $SQL_Connection.CreateCommand() $SQL_Query = "SELECT [MachineName] FROM [ConfigurationSchema].[Services]" $SQL_Command.CommandText = $SQL_Query $SQL_QUERY_Controllers = new-object "System.Data.DataTable" $SQL_QUERY_Controllers.Load( $SQL_Command.ExecuteReader() ) $ServerAlreadyJoined = $False } catch { DS_WriteLog "E" "An error occurred trying to retrieve the list of controllers (error: $($Error[0]))" $LogFile Exit 1 } # Add the first controller found to the value 'Service.AutoDiscoveryAddresses' in the 'web.config' file $ValidController = $False foreach ( $Element in $SQL_QUERY_Controllers ) { $Controller = $Element.MachineName # Test if the controller is up-and-running DS_WriteLog "I" "Controller found! Name: '$Controller'" $LogFile DS_WriteLog "I" "Test if the controller '$Controller' is reachable" $LogFile try { Test-Connection $Controller | Out-Null DS_WriteLog "S" "The controller '$Controller' is reachable" $LogFile # Add controller to the 'web.config' file DS_WriteLog "I" "Add controller '$Controller' to the 'web.config' file" $LogFile try { $node = $xml.configuration.appSettings.add | where {$_.Key -eq 'Service.AutoDiscoveryAddresses'} $node.value = "$Controller" # Change an existing value $xml.Save($WebConfigFile) DS_WriteLog "S" "Controller '$Controller' was successfully added to the 'web.config' file" $LogFile $ValidController = $True Break } catch { DS_WriteLog "E" "An error occurred trying to add controller '$Controller' to the 'web.config' file (error: $($Error[0]))" $LogFile } } catch { DS_WriteLog "E" "The controller '$Controller' cannot be reached (error: $($Error[0]))" $LogFile } } $SQL_Connection.Close() # If no valid controller was found exit the script with an error if ( $ValidController -eq $False ) { DS_WriteLog "E" "No valid controller could be found or reached!" $LogFile DS_WriteLog "E" "Note:" $LogFile DS_WriteLog "E" " -Please make sure that your XenDesktop controllers are up-and-running! and that all Citrix services, that are set to 'automatic', are running as well!" $LogFile DS_WriteLog "E" " -Please make sure that all Citrix services (which are set to 'automatic') are running!" $LogFile Exit 1 } } else { DS_WriteLog "I" "It seems that one or more valid controllers are present in the 'web.config' file. Nothing to do." $LogFile } } catch { DS_WriteLog "E" "An error occurred trying to check and correct the list of controllers in the 'web.config' file (error: $($Error[0]))" $LogFile Exit 1 } DS_WriteLog "-" "" $LogFile ################################ # Set Director Default Webpage ################################ # The following PowerShell "here-string" contains the text to be added to the redirection file $Text = @" <script type="text/javascript"> <!-- window.location="$DirectorURL"; // --> </script> "@ # Set Director default webpage DS_WriteLog "I" "Set Director default webpage (logon.aspx)" $LogFile # Create the redirection file in the IIS root directory of Director $DirectorRedirectFile = Join-Path $IISRootDir $DirectorRedirectFileName DS_WriteLog "I" "Create the redirection file in the IIS root directory of Director" $LogFile DS_WriteLog "I" "Redirection file: $DirectorRedirectFile" $LogFile try { Set-Content $DirectorRedirectFile -value ("$Text") -Encoding Default DS_WriteLog "S" "The redirection file '$DirectorRedirectFile' has been created successfully" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to write the redirection file '$DirectorRedirectFile' (error: $($Error[0]))" $LogFile Exit 1 } # Add the redirection file to the top of the list of default documents in IIS (first remove the entry from the default documents if it exists) # Reference: https://technet.microsoft.com/en-us/library/hh867867(v=wps.630).aspx DS_WriteLog "I" "Add the redirection file '$DirectorRedirectFileName' to the top of the list of default documents in IIS" $LogFile # Remove the redirection file from the list of default documents (if exists) DS_WriteLog "I" "Remove the redirection file from the list of default documents (if exists)" $LogFile try { if ( Get-WebConfigurationProperty -Filter "//defaultDocument" -PSPath "IIS:\sites\$IISSiteName" -Name files.collection | Where-Object { $_.value -eq $DirectorRedirectFileName } ) { DS_WriteLog "I" "The file '$DirectorRedirectFileName' already exists. Removing file (in IIS, not from disk) ..." $LogFile try { Remove-WebConfigurationProperty -Filter "//defaultDocument" -PSPath "IIS:\sites\$IISSiteName" -Name files -atElement @{value=$DirectorRedirectFileName} -ErrorAction Stop DS_WriteLog "S" "The file '$DirectorRedirectFileName' was successfully removed from the default documents" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to remove the file '$DirectorRedirectFileName' from the default documents (error: $($Error[0]))" $LogFile Exit 1 } } else { DS_WriteLog "I" "The file '$DirectorRedirectFileName' is not listed in the default documents" $LogFile } } catch { DS_WriteLog "E" "An error occurred trying to check and remove the file '$DirectorRedirectFileName' from the default documents (error: $($Error[0]))" $LogFile Exit 1 } # Add the redirection file to the top of the list of default documents in IIS (first remove the entry from the default documents if it exists) DS_WriteLog "I" "Add the redirection file '$DirectorRedirectFileName' to the default documents" $LogFile try { Add-WebConfiguration -Filter "//defaultDocument/files" -PSPath "IIS:\sites\$IISSiteName" -AtIndex 0 -Value @{value=$DirectorRedirectFileName} DS_WriteLog "S" "The file '$DirectorRedirectFileName' was successfully added to the top of the default documents" $LogFile } catch { DS_WriteLog "E" "An error occurred trying add the file '$DirectorRedirectFileName' to the top of the default documents (error: $($Error[0]))" $LogFile Exit 1 } DS_WriteLog "-" "" $LogFile ################################################################################## # Director Spinning Circle (enable multiple IIS Site Bindings / MultiSiteBinding) ################################################################################## # Notes: # -Reference: # -Citrix Director Becomes Unresponsive after Submitting the Credentials when IIS X-Frame-Options is enabled # https://support.citrix.com/article/CTX202564 # -Supporting Multiple IIS Site Bindings # https://msdn.microsoft.com/en-us/library/ee358763(v=vs.110).aspx # --------------------------------------------------------------- # Enable MultiSiteBinding DS_WriteLog "I" "Enable MultiSiteBinding (in the 'web.config' file)" $LogFile try { $xml = [xml](Get-Content $WebConfigFile) $node = $xml.configuration."system.serviceModel".serviceHostingEnvironment $node.SetAttribute("multipleSiteBindingsEnabled","true"); # Add a new key/value $xml.Save($WebConfigFile) DS_WriteLog "S" "MultiSiteBinding has been successfully enabled" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to enable MultiSiteBinding (error: $($Error[0]))" $LogFile Exit 1 } DS_WriteLog "-" "" $LogFile ################################################################ # Director Spinning Circle (enable IIS X-Frame-Options header) ################################################################ # Notes: # -Reference: # -Citrix Director Becomes Unresponsive after Submitting the Credentials when IIS X-Frame-Options is enabled # https://support.citrix.com/article/CTX202564 # -Mitigating framesniffing with the X-Frame-Options header # https://support.microsoft.com/en-us/help/2694329/mitigating-framesniffing-with-the-x-frame-options-header # --------------------------------------------------------------- # Configure IIS X-Frame custom header DS_WriteLog "I" "Configure IIS X-Frame custom header (in the 'web.config' file)" $LogFile try { $xml = [xml](Get-Content $WebConfigFile) $node = $xml.configuration."system.webserver".httpProtocol.customHeaders.add | where {$_.Name -eq 'X-Frame-Options'} $node.value = "ALLOW-FROM http://localhost/director/default.html" # Change an existing value $xml.Save($WebConfigFile) DS_WriteLog "S" "The IIS X-Frame custom header has been successfully configured" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to configure the IIS X-Frame custom header (error: $($Error[0]))" $LogFile Exit 1 } DS_WriteLog "-" "" $LogFile #################################### # Pre-populate the domain field #################################### # Pre-populate the domain field DS_WriteLog "I" "Pre-populate the domain field in the file '$LogonASPXFile' with the domain name '$DomainName'" $LogFile $OldText = "TextBox ID=""Domain"" runat=""server""" $NewText = "TextBox ID=""Domain"" runat=""server"" Text=""$DomainName""" # Read each line of the file and pre-populate the domain name try { $Content = Get-Content $LogonASPXFile DS_WriteLog "I" "Check the 'logon.aspx' file line by line ..." $LogFile if ( ([string]$Content).Contains($NewText) ) { DS_WriteLog "I" "The domain field has already been pre-populated. Nothing to do" $LogFile } else { Foreach ( $Line in $Content ) { $Line = ( $Line -replace $OldText, $NewText) + "`r`n" $ContentNew = $ContentNew + $Line } # Write the new file try { Set-Content $LogonASPXFile -value $ContentNew -Encoding UTF8 DS_WriteLog "I" "The domain field has been successfully pre-populated" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to pre-populate the domain field (error: $($Error[0]))" $LogFile Exit 1 } } } catch { DS_WriteLog "E" "An error occurred trying to pre-populate the domain field (error: $($Error[0]))" $LogFile Exit 1 } DS_WriteLog "-" "" $LogFile ########################### # Set the session time-out ########################### # Set the session time-out DS_WriteLog "I" "Set the session time-out to $SessionTimeOut minutes (in the 'web.config' file)" $LogFile try { $xml = [xml](Get-Content $WebConfigFile) $node = $xml.configuration."system.web".sessionState $node.timeout = $SessionTimeOut # Change an existing value $xml.Save($WebConfigFile) DS_WriteLog "S" "The session time-out has been successfully modified" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to modify the session time-out (error: $($Error[0]))" $LogFile Exit 1 } DS_WriteLog "-" "" $LogFile ########################### # Disable SSL check ########################### # Disable SSL check DS_WriteLog "I" "Disable SSL Check (in the 'web.config' file)" $LogFile try { $xml = [xml](Get-Content $WebConfigFile) $node = $xml.configuration.appSettings.add | where {$_.Key -eq 'UI.EnableSslCheck'} $node.value = "false" # Change an existing value $xml.Save($WebConfigFile) DS_WriteLog "S" "The SSL check has been disabled successfully" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to disable the SSL check (error: $($Error[0]))" $LogFile Exit 1 } DS_WriteLog "-" "" $LogFile ########################### # Disable Activity Manager ########################### # Disable Activity Manager DS_WriteLog "I" "Disable Activity Manager (in the 'web.config' file)" $LogFile try { $xml = [xml](Get-Content $WebConfigFile) $node = $xml.configuration.appSettings.add | where {$_.Key -eq 'UI.TaskManager.EnableApplications'} $node.value = "false" # Change an existing value $xml.Save($WebConfigFile) DS_WriteLog "S" "Activity Manager was disabled successfully" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to disable Activity Manager (error: $($Error[0]))" $LogFile Exit 1 } DS_WriteLog "-" "" $LogFile ########################### # Large Active Directory / Multiple Forests # # -Reference: # -Desktop Director User Account Search Process is Slow or Fails # https://support.citrix.com/article/CTX133013 ############################ # Large Active Directory / Multiple Forests (Search only current domain) # Reference: https://blogs.technet.microsoft.com/heyscriptingguy/2010/07/29/how-to-add-data-to-an-xml-file-using-windows-powershell/ DS_WriteLog "I" "Configure Large Active Directory / Multiple Forests (Search only current domain) (in the 'web.config' file)" $LogFile try { $xml = [xml](Get-Content $WebConfigFile) # Open the XML file $NewElement = $xml.CreateElement("add") # Create a new XML element called 'add' (at this moment this is only created in memory) -> do not use capital letters or the XML file will not be accepted $NewElement.SetAttribute("key","Connector.ActiveDirectory.ForestSearch") # Add the key value for the new key/value combination $NewElement.SetAttribute("value","false") # Add the value for the new key/value combination $xml.configuration.appSettings.AppendChild($NewElement) | Out-Null # Add the new element in the correct part of the XML file, under <configuration> \ <appSettings>' $xml.Save($WebConfigFile) # Save the XML file DS_WriteLog "S" "'Search only current domain' was enabled successfully" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to enable 'Search only current domain' (error: $($Error[0]))" $LogFile Exit 1 } DS_WriteLog "-" "" $LogFile # Add searchable domains DS_WriteLog "I" "Add searchable domains ('$SearchableDomains') (in the 'web.config' file)" $LogFile try { $xml = [xml](Get-Content $WebConfigFile) $node = $xml.configuration.appSettings.add | where {$_.Key -eq 'Connector.ActiveDirectory.Domains'} $node.value = $SearchableDomains # Change an existing value $xml.Save($WebConfigFile) DS_WriteLog "S" "The following searchable domains were successfully added: $SearchableDomains" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to add the list of searchable domains (error: $($Error[0]))" $LogFile Exit 1 } DS_WriteLog "-" "" $LogFile ########################### # Saved Filters (change location) ########################### # Saved Filters (change location) DS_WriteLog "I" "Set location for 'saved filters' to '$SavedFiltersLocation' (in the 'web.config' file)" $LogFile try { $xml = [xml](Get-Content $WebConfigFile) $node = $xml.configuration.appSettings.add | where {$_.Key -eq 'Service.UserSettingsPath'} $node.value = $SavedFiltersLocation # Change an existing value $xml.Save($WebConfigFile) DS_WriteLog "S" "The location for the 'saved filters' was successfully set to '$SavedFiltersLocation'" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to set the location for the 'saved filters' to '$SavedFiltersLocation' (error: $($Error[0]))" $LogFile Exit 1 } DS_WriteLog "-" "" $LogFile ########################### # Director and Self-Service Password Reset (SSPR) # Fix: problem with multiple bindings on "Default Web Site" when SSPR is installed (enable multiple IIS Site Bindings or delete port 8080) ########################### # Fix issue with Director DS_WriteLog "I" "Director and Self-Service Password Reset (SSPR): prevent Director from breaking" $LogFile DS_WriteLog "I" "Fix 1: enable multiple IIS Site Bindings" $LogFile DS_WriteLog "I" "Fix 2: remove port 8080 from ""Default Web Site"" binding" $LogFile # Check the 'web.config' file if multiple IIS Site Bindings is enabled DS_WriteLog "I" "Check if multiple IIS Site Bindings is enabled. If not, remove port from bindings" $LogFile $xml = [xml](Get-Content $WebConfigFile) [string]$Setting = $xml.configuration."system.serviceModel".serviceHostingEnvironment.multipleSiteBindingsEnabled if ( $Setting -eq "true") { DS_WriteLog "I" "Multiple IIS Site Bindings is enabled (in the 'web.config' file). Nothing to do" $LogFile } else { DS_WriteLog "I" "Multiple IIS Site Bindings is NOT enabled. Remove port from bindings" $LogFile # Remove binding DS_WriteLog "I" "Remove port $SelfServicePort from the bindings on IIS site '$IISSiteName'" $LogFile if ( Get-WebBinding -name $IISSiteName -Port $SelfServicePort ) { try { Remove-WebBinding -name $IISSiteName -Port $SelfServicePort -ErrorAction Stop DS_WriteLog "S" "Port $SelfServicePort has been successfully removed from the IIS site '$IISSiteName'" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to remove port $SelfServicePort from the IIS site '$IISSiteName' (error: $($Error[0]))" $LogFile } } else { DS_WriteLog "I" "Port $SelfServicePort was not bound to the IIS site '$IISSiteName'. Nothing to do" $LogFile } } DS_WriteLog "-" "" $LogFile ########################### # Director Grooming ########################### # Grooming settings are configured on the Delivery Controller # See my article 'Citrix Delivery Controller unattended installation with PowerShell and SCCM' for more information: # https://dennisspan.com/citrix-delivery-controller-unattended-installation-with-powershell-and-sccm/#ConfigureGrooming ########################### # Director Single Sign-on # # -Reference: # https://stackoverflow.com/questions/21415291/how-to-specify-application-pool-identity-user-and-password-from-powershell ########################### # Enable Windows authentication on the Director site DS_WriteLog "I" "Enable Windows authentication on the Director site (in IIS)" $LogFile try { Set-WebConfigurationProperty -filter "/system.webServer/security/authentication/windowsAuthentication" -name "enabled" -value "true" -PSPath "IIS:\sites\$IISSiteName\Director" DS_WriteLog "S" "Windows authentication was successfully enabled" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to enable Windows authentication (error: $($Error[0]))" $LogFile Exit 1 } # Disable anonymous authentication on the Director site DS_WriteLog "I" "Disable anonymous authentication on the Director site (in IIS)" $LogFile try { Set-WebConfigurationProperty -filter "/system.webServer/security/authentication/anonymousAuthentication" -name "enabled" -value "false" -PSPath "IIS:\sites\$IISSiteName\Director" DS_WriteLog "S" "Anonymous authentication was successfully enabled" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to disable anonymous authentication (error: $($Error[0]))" $LogFile Exit 1 } # Add the service account in the application pool credentials DS_WriteLog "I" "Add the service account in the application pool credentials (in IIS)" $LogFile try { Set-ItemProperty "IIS:\AppPools\Director" -name "processModel" -value @{userName=$AppPoolUserName;password=$AppPoolPassword;identitytype=3} DS_WriteLog "S" "The service account has been added in the application pool credentials" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to add the service account in the application pool credentials (error: $($Error[0]))" $LogFile Exit 1 } # Unlock sections: certain changes to the Default Web Site cannot be made unless the individual sections are unlocked # -Reference: https://stackoverflow.com/questions/5717154/programmatically-unlocking-iis-configuration-sections-in-powershell DS_WriteLog "I" "Unlock IIS section 'windowsAuthentication' (to be able to change its configuration) in IIS" $LogFile try { [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Web.Administration") $oIIS = New-Object Microsoft.Web.Administration.ServerManager $oGlobalConfig = $oIIS.GetApplicationHostConfiguration() $oConfig = $oGlobalConfig.GetSection("system.webServer/security/authentication/windowsAuthentication", $IISSiteName) $oConfig.OverrideMode="Allow" $oIIS.CommitChanges() DS_WriteLog "S" "The IIS section 'windowsAuthentication' was successfully unlocked" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to unlock the IIS section 'windowsAuthentication' (error: $($Error[0]))" $LogFile Exit 1 } # Pause the script for two seconds to prevent errors in the next step Start-Sleep 2 # Enable the use of the 'useAppPoolCredentials' DS_WriteLog "I" "Enable the setting 'useAppPoolCredentials' (in IIS)" $LogFile try { Set-WebConfigurationProperty -filter "/system.webServer/security/authentication/windowsAuthentication" -name "useAppPoolCredentials" -value "true" -PSPath "IIS:\sites\$IISSiteName" DS_WriteLog "S" "The setting 'useAppPoolCredentials' was successfully set to enabled" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to enable the setting 'useAppPoolCredentials' (error: $($Error[0]))" $LogFile Exit 1 } DS_WriteLog "-" "" $LogFile ####################################### # Director – Multiple XenDesktop Sites ####################################### # Use the code in the top section 'Add controller' to add more controllers ########################### # Generate SNMP traps ########################### # SNMP traps have to be configured on the Delivery Controller. See the following Citrix article for more information: # http://docs.citrix.com/en-us/xenapp-and-xendesktop/7-13/director/alerts-notifications.html#par_anchortitle_6b0f ########################### # SCOM Integration # Note: the following commands emulate the steps of the command line tool 'C:\inetpub\wwwroot\Director\tools\DirectorConfig.exe /configscom' # Reference: # -http://docs.citrix.com/en-us/xenapp-and-xendesktop/7-13/director/alerts-notifications.html#par_anchortitle_6b0f # -https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/enable-psremoting ########################### # Enable WinRM (remote management) DS_WriteLog "I" "Enable WinRM (remote management)" $LogFile try { Enter-PSsession -computername localhost -ErrorAction stop # If this works than WinRM is enabled DS_WriteLog "S" "WinRM (remote management) has already been enabled. Nothing to do" $LogFile } catch { try { Enable-PSRemoting -Force | out-Null DS_WriteLog "I" "WinRM (remote management) has been successfully enabled" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to enable WinRM (remote management) (error: $($Error[0]))" $LogFile Exit 1 } } # Add the SCOM server to the list of TrustedHosts (WinRM) DS_WriteLog "I" "Add the SCOM server '$SCOMServer' to the list of TrustedHosts (WinRM)" $LogFile try { [string]$ExistingTrustedHosts = (Get-Item WSMAN:\localhost\Client\TrustedHosts).Value # Retrieve the list of existing trusted hosts if ( ($ExistingTrustedHosts).Contains($SCOMServer) ) { # Check if the SCOM server is already included in the list of trusted hosts DS_WriteLog "I" "The SCOM server '$SCOMServer' is already included in the list of TrustedHosts ('($ExistingTrustedHosts'). Nothing to do" $LogFile } else { if ( [string]::IsNullOrEmpty($ExistingTrustedHosts) ) { $TrustedHosts = $SCOMServer } else { $TrustedHosts = "$SCOMServer,$ExistingTrustedHosts" } try { Set-Item WSMAN:\localhost\Client\TrustedHosts -Value $TrustedHosts -force # Add the SCOM server (+ the already existing trusted hosts) to list of trusted hosts DS_WriteLog "S" "The SCOM server '$SCOMServer' has been successfully added to the list of TrustedHosts ('$TrustedHosts')" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to add the SCOM server '$SCOMServer' to the list of TrustedHosts ('$TrustedHosts') (error: $($Error[0]))" $LogFile Exit 1 } } } catch { DS_WriteLog "E" "An error occurred trying to retrieve the list of TrustedHosts (error: $($Error[0]))" $LogFile Exit 1 } # Configure the SCOM and WinRM settings DS_WriteLog "I" "Configure the SCOM and WinRM settings for Director (in the 'web.config' file)" $LogFile try { $xml = [xml](Get-Content $WebConfigFile) # Open the XML file # Create new element 'Connector.SCOM.ManagementServer' $NewElement = $xml.CreateElement("add") # Create a new XML element called 'add' (at this moment this is only created in memory) -> do not use capital letters or the XML file will not be accepted $NewElement.SetAttribute("key","Connector.SCOM.ManagementServer") # Add the key value for the new key/value combination $NewElement.SetAttribute("value",$SCOMServer) # Add the value for the new key/value combination $xml.configuration.appSettings.AppendChild($NewElement) | Out-Null # Add the new element in the correct part of the XML file, under <configuration> \ <appSettings>' # Create new element 'Connector.SCOM.Identity' $NewElement = $xml.CreateElement("add") # Create a new XML element called 'add' (at this moment this is only created in memory) -> do not use capital letters or the XML file will not be accepted $NewElement.SetAttribute("key","Connector.SCOM.Identity") # Add the key value for the new key/value combination $NewElement.SetAttribute("value","User") # Add the value for the new key/value combination $xml.configuration.appSettings.AppendChild($NewElement) | Out-Null # Add the new element in the correct part of the XML file, under <configuration> \ <appSettings>' # Create new element 'Connector.SCOM.WSManTransport' $NewElement = $xml.CreateElement("add") # Create a new XML element called 'add' (at this moment this is only created in memory) -> do not use capital letters or the XML file will not be accepted $NewElement.SetAttribute("key","Connector.SCOM.WSManTransport") # Add the key value for the new key/value combination $NewElement.SetAttribute("value",$SCOMProtocol) # Add the value for the new key/value combination $xml.configuration.appSettings.AppendChild($NewElement) | Out-Null # Add the new element in the correct part of the XML file, under <configuration> \ <appSettings>' # Create new element 'Connector.SCOM.WSManPort' $NewElement = $xml.CreateElement("add") # Create a new XML element called 'add' (at this moment this is only created in memory) -> do not use capital letters or the XML file will not be accepted $NewElement.SetAttribute("key","Connector.SCOM.WSManPort") # Add the key value for the new key/value combination $NewElement.SetAttribute("value",$SCOMPort) # Add the value for the new key/value combination $xml.configuration.appSettings.AppendChild($NewElement) | Out-Null # Add the new element in the correct part of the XML file, under <configuration> \ <appSettings>' # Save the XMl file $xml.Save($WebConfigFile) # Save the XML file DS_WriteLog "S" "The SCOM and WinRM settings for Director were configured successfully" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to configure the SCOM and WinRM settings for Director (error: $($Error[0]))" $LogFile Exit 1 } # Install ASP.NET (W2K8R2) / Windows features (W2K12 and higher) for SCOM in Director # Note: # -Web-Net-Ext45 = .NET Extensibility 4.6 = IIS-NetFxExtensibility45 # -Web-Asp-Net45 = ASP.NET 4.6 = IIS-ASPNET45 # -NET-WCF-HTTP-Activation45 = HTTP Activation = WCF-HTTP-Activation45 [string]$WindowsVersion = ( Get-WmiObject -class Win32_OperatingSystem ).Version if ( ($WindowsVersion -like "*6.0*") -Or ($WindowsVersion -like "*6.1*") ) { # The server OS is Windows Server 2008 (R2) $Error.Clear() DS_WriteLog "I" "The operating system is Windows Server 2008 (R2)" $LogFile DS_WriteLog "I" "Install ASP.NET (required for SCOM in Director)" $LogFile & "$env:SystemRoot\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe" -ir if ( $LASTEXITCODE -eq 0 ) { DS_WriteLog "S" "ASP.NET has been successfully installed" $LogFile } else { DS_WriteLog "E" "An error occurred trying to install ASP.NET (error: $($Error[0]))" $LogFile Exit 1 } } else { $FeatureName = "IIS-NetFxExtensibility45" $Error.Clear() DS_WriteLog "I" "The operating system is Windows Server 2012 or higher" $LogFile DS_WriteLog "I" "Install Windows feature '$FeatureName' (required for SCOM in Director)" $LogFile & Dism.exe /Online /Enable-Feature /NoRestart /FeatureName:$FeatureName if ( $LASTEXITCODE -eq 0 ) { DS_WriteLog "S" "The Windows feature '$FeatureName' has been successfully installed" $LogFile } else { DS_WriteLog "E" "An error occurred trying to install the Windows feature '$FeatureName' (error: $($Error[0]))" $LogFile Exit 1 } $FeatureName = "IIS-ASPNET45" $Error.Clear() DS_WriteLog "I" "Install Windows feature '$FeatureName' (required for SCOM in Director)" $LogFile & Dism.exe /Online /Enable-Feature /NoRestart /FeatureName:$FeatureName if ( $LASTEXITCODE -eq 0 ) { DS_WriteLog "S" "The Windows feature '$FeatureName' has been successfully installed" $LogFile } else { DS_WriteLog "E" "An error occurred trying to install the Windows feature '$FeatureName' (error: $($Error[0]))" $LogFile Exit 1 } $FeatureName = "WCF-HTTP-Activation45" $Error.Clear() DS_WriteLog "I" "Install Windows feature '$FeatureName' (required for SCOM in Director)" $LogFile & Dism.exe /Online /Enable-Feature /NoRestart /FeatureName:$FeatureName if ( $LASTEXITCODE -eq 0 ) { DS_WriteLog "S" "The Windows feature '$FeatureName' has been successfully installed" $LogFile } else { DS_WriteLog "E" "An error occurred trying to install the Windows feature '$FeatureName' (error: $($Error[0]))" $LogFile Exit 1 } } # Enable File Security Remove-Item env:\SEE_MASK_NOZONECHECKS DS_WriteLog "-" "" $LogFile DS_WriteLog "I" "End of script" $LogFile |
Note: in some cases, the database connection (lines 214 to 218) may end in an error (“cannot open database”). This can happen if you use the standard SQL port 1433. In this case, please remove the variable $DatabaseServerPort (including the preceding comma) from line 212. The credit (and my thanks) for finding this error goes to Thorsten Enderlein (@endi24). |
Execute the script as follows, for example:
powershell.exe -file C:\Temp\Citrix\Director\Configure_CitrixDirector.ps1
In case you get a security warning, set the execution policy to allow the script to run:
powershell.exe -executionpolicy bypass -file C:\Temp\Citrix\Director\Configure_CitrixDirector.ps1
Log files are created in the directory C:\Logs\Citrix_Director_(configure), but you can change this to any directory you want (see lines 103 and 104).
Conclusion
This concludes this article on the Citrix Director unattended installation. You can customize the scripts in this article in any way you see fit.
As stated at the beginning of this article, I strongly recommend you read the following two articles:
- Director 7.16 – by Carl Stalhood
- Citrix Director – by George Spiers
If you want to go one step further and create an SCCM package, please follow the step-by-step explanation in the article Deep dive creating SCCM packages for Citrix.
If you have any questions or recommendations please leave a comment below. Happy scripting!
Dennis Span works as a Lead Account Technology Strategist at Cloud Software Group in Vienna, Austria. He holds multiple Citrix certifications (CCE-V). Dennis has been a Citrix Technology Advocate (CTA) since 2017 (+ one year as Citrix Technology Professional, CTP). Besides his interest in virtualization technologies and blogging, he loves spending time with his family as well as snowboarding, playing basketball and rowing. He is fluent in Dutch, English, German and Slovak and speaks some Spanish.
Pingback: Citrix Director – JGSpiers.com
Pingback: Detailed Change Log – Carl Stalhood
Pingback: Director 7.14 – Carl Stalhood
Pingback: Site Updates – June 2017 – Carl Stalhood
Pingback: Director 7.15 – Carl Stalhood
Pingback: Citrix Director – Digital Cloud Zone
Pingback: EUC Weekly Digest – June 24, 2017 – Carl Stalhood
Pingback: Scripting the complete list of Citrix components with PowerShell - Dennis Span
Pingback: Director 7.17 – Carl Stalhood
Pingback: Director 7.18 – Carl Stalhood
Pingback: Director 1808 – Carl Stalhood
Great article !!
Thanks a lot Josef!
These posts are great and invaluable to me right now! It looks like Citrix has updated their Director installation media for the latest version, 7 1808_2.
Thanks a lot David! I am happy that my articles are of some help to you. What did you mean with the remark concerning the Director installation? I know I have to update my article to include the latest version of Director, but did something change in such a way the information in my article was incorrect or incomplete?
Pingback: Director 1811 – Carl Stalhood
Pingback: Citrix Director 1906 – Carl Stalhood
Pingback: Director 7.16 – Carl Stalhood
Pingback: Citrix Director 1912 LTSR – Carl Stalhood
Pingback: Citrix Director 2203 LTSR – Carl Stalhood