All Citrix products can be automated. This article covers the Citrix StoreFront unattended installation and configuration with PowerShell.
In case you want to configure StoreFront settings and you are looking for the corresponding PowerShell commands, see my article Translating the Citrix StoreFront console to PowerShell.
Change Log 01.02.2018: updated the Complete script for configuring Citrix StoreFront. The function DS_BindCertificateToIISPort now also works with wildcard certificates (e.g. *.mydomain.com) as well as Subject Alternative Name (SAN) certificates. Many thanks to Julian Mooren (@citrixguyblog) for reporting this issue to me. The function DS_CreateStoreFrontStore has two new parameters, CertSubjectName and AddHostHeaderToIISSiteBinding. |
Introduction
Before continuing with this article please be aware of the following:
- The version of Citrix StoreFront in this article is 3.13 released in Q4 2017.
- StoreFront is part of the XenDesktop installation, but can also be downloaded separately. In this article, I will work with the stand-alone installer.
- The XenDesktop ISO file can be downloaded here: https://www.citrix.com/downloads/xenapp-and-xendesktop/product-software/xenapp-and-xendesktop-716.html.
- The stand-alone source files can be downloaded here: https://www.citrix.com/downloads/storefront/.
- In this article, StoreFront is installed on a separate machine (and NOT on a machine with the Delivery Controller role installed).
- 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 in this article have been tested on the following operating systems:
- Microsoft Windows Server 2016 version 1607
- Make sure to execute the scripts in this article on a server that has been pre-installed with one of the aforementioned operating systems including all latest hotfixes and .Net Framework 4.6.x. Also, you should at least have PowerShell 4.0 installed on the system, although I recommend using the latest version.
I strongly urge you to read the following article:
- StoreFront 2308 through 3.5 – Basic Configuration – by Carl Stalhood
This article explains the manual installation of StoreFront step-by-step (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 StoreFront;
- Part 3 describes the configuration of Citrix StoreFront.
Part 1: Install Windows Roles and Features (optional)
Before we install Citrix StoreFront, you may want to install some commonly used roles and features. These roles and features are optional.
Internet Information Services Please be aware that Internet Information Services (IIS) is a prerequisite for StoreFront. IIS is installed automatically during the installation of StoreFront and is 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 253 254 255 256 257 258 259 260 261 |
#========================================================================== # # Installation of Microsoft Roles and Features for Citrix StoreFront # # AUTHOR: Dennis Span (https://dennisspan.com) # DATE : 25.09.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 StoreFront are # installed automatically during the installation of Citrix StoreFront: # -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)][ValidateSet("I","S","W","E","-",IgnoreCase = $True)][String]$InformationType, [Parameter(Mandatory=$true, Position = 1)][AllowEmptyString()][String]$Text, [Parameter(Mandatory=$true, Position = 2)][AllowEmptyString()][String]$LogFile ) begin { } process { $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.ToUpper() + " - " + $Text) } } end { } } #========================================================================== ################ # 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 StoreFront 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 -executionpolicy bypass -file %Directory%\Install_Citrix_StoreFront_Roles.ps1
Log files are created in the directory C:\Logs\Citrix_StoreFront_Roles, but you can change this to any directory you want (see lines 105 and 106).
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 StoreFront
The second part of this article focuses on the installation of the Citrix StoreFront. So how do you start?
- Create an installation directory on the local computer or on a file share (UNC path). For example: C:\Temp\Citrix\StoreFront.
- Create a subdirectory called Files.
- Download and copy the StoreFront stand-alone installer CitrixStoreFront-x64.exe to the folder Files in the installation directory.
- Copy the complete PowerShell script at the end of this paragraph to a new PS1 file (e.g. Install_CitrixStoreFront.ps1) and add this file to the root of your installation directory (not in the subdirectory Files).
- Execute the PowerShell script:
powershell.exe -executionpolicy bypass -file C:\Temp\Citrix\DeliveryController\Install_CitrixStoreFront.ps1
In case you prefer to use the XenDesktop ISO instead of the StoreFront stand-alone installer, the basic command line to install StoreFront is:
1 |
XenDesktopServerSetup.exe /components storefront /configure_firewall /noreboot /quiet /logpath C:\Logs |
When using the stand-alone installer (used in this article), the command is as follows:
1 2 3 |
CitrixStoreFront-x64.exe [-silent] [-INSTALLDIR installationlocation] [-WINDOWS_CLIENT filelocation\filename.exe] [-MAC_CLIENT filelocation\filename.dmg] |
By default, the installation creates three log files in the directory C:\Windows\Temp\StoreFront. These are:
- CitrixMsi-CitrixStoreFront-x64-%Date%_%Time%.log
- CitrixMsi-TelemetryServiceInstaller_x64-%Date%_%Time%.log
- CitrixMsi-TelemetryServiceInstaller_x64-%Date%_%Time%.log
In the complete PowerShell script at the end of this paragraph, these log files are copied to the custom log directory defined in line 347 in the script.
During installation, the path to the StoreFront PowerShell modules (C:\Program Files\Citrix\Receiver StoreFront\PowerShellSDK\Modules) is added to the environment variable PSModulePath.
This means that no reference to the specific directory is required when importing StoreFront PowerShell modules in a script. For more information please see the section File locations in the article Understanding PowerShell fundamentals.
StoreFront 3.13 contains a total of 206 cmdlets. The following command lists all cmdlets:
1 |
Get-Command -all -commandtype cmdlet | where-object {$_.ModuleName -like "*StoreFront*"} |
The following command counts the total number of available cmdlets:
1 |
(Get-Command -all -commandtype cmdlet | where-object {$_.ModuleName -like "*StoreFront*"}).count |
Note: The installation of StoreFront requires a reboot. Without the reboot, the StoreFront PowerShell modules cannot be loaded, which is required for the configuration of StoreFront in Part 3 of this article. The reboot is NOT part of the example script. You can either add it to the script yourself or if you use a software deployment tool such as Microsoft SCCM, you can add a reboot task in the task sequence. |
Complete script for installing Citrix StoreFront
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 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 |
#========================================================================== # # Install Citrix StoreFront # # AUTHOR: Dennis Span (https://dennisspan.com) # DATE : 19.01.2018 # # COMMENT: # This script has been prepared for Windows Server 2008 R2, 2012 R2 and 2016. # # The version of StoreFront used in this script is 3.13 (released in Q4 2017), # but it should also work for previous versions. # #========================================================================== # 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)][ValidateSet("I","S","W","E","-",IgnoreCase = $True)][String]$InformationType, [Parameter(Mandatory=$true, Position = 1)][AllowEmptyString()][String]$Text, [Parameter(Mandatory=$true, Position = 2)][AllowEmptyString()][String]$LogFile ) begin { } process { $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.ToUpper() + " - " + $Text) } } end { } } #========================================================================== # 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 ) begin { [string]$FunctionName = $PSCmdlet.MyInvocation.MyCommand.Name DS_WriteLog "I" "START FUNCTION - $FunctionName" $LogFile } process { $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 # Check if the installation file exists if (! (Test-Path $File) ) { DS_WriteLog "E" "The file '$File' does not exist!" $LogFile Exit 1 } # Check if custom arguments were defined if ([string]::IsNullOrEmpty($Arguments)) { 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 } 1619 { DS_WriteLog "E" "The installation files cannot be found. The PS1 script should be in the root directory and all source files in the subdirectory 'Files' (exit code: 1619)" $LogFile Exit 1 } 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 } } } end { DS_WriteLog "I" "END FUNCTION - $FunctionName" $LogFile } } #========================================================================== # FUNCTION DS_CopyFile #========================================================================== Function DS_CopyFile { <# .SYNOPSIS Copy one or more files .DESCRIPTION Copy one or more files .PARAMETER SourceFiles This parameter can contain multiple file and folder combinations including wildcards. UNC paths can be used as well. Please see the examples for more information. To see the examples, please enter the following PowerShell command: Get-Help DS_CopyFile -examples .PARAMETER Destination This parameter contains the destination path (for example 'C:\Temp2' or 'C:\MyPath\MyApp'). This path may also include a file name. This situation occurs when a single file is copied to another directory and renamed in the process (for example '$Destination = C:\Temp2\MyNewFile.txt'). UNC paths can be used as well. The destination directory is automatically created if it does not exist (in this case the function 'DS_CreateDirectory' is called). This works both with local and network (UNC) directories. In case the variable $Destination contains a path and a file name, the parent folder is automatically extracted, checked and created if needed. Please see the examples for more information.To see the examples, please enter the following PowerShell command: Get-Help DS_CopyFile -examples .EXAMPLE DS_CopyFile -SourceFiles "C:\Temp\MyFile.txt" -Destination "C:\Temp2" Copies the file 'C:\Temp\MyFile.txt' to the directory 'C:\Temp2' .EXAMPLE DS_CopyFile -SourceFiles "C:\Temp\MyFile.txt" -Destination "C:\Temp2\MyNewFileName.txt" Copies the file 'C:\Temp\MyFile.txt' to the directory 'C:\Temp2' and renames the file to 'MyNewFileName.txt' .EXAMPLE DS_CopyFile -SourceFiles "C:\Temp\*.txt" -Destination "C:\Temp2" Copies all files with the file extension '*.txt' in the directory 'C:\Temp' to the destination directory 'C:\Temp2' .EXAMPLE DS_CopyFile -SourceFiles "C:\Temp\*.*" -Destination "C:\Temp2" Copies all files within the root directory 'C:\Temp' to the destination directory 'C:\Temp2'. Subfolders (including files within these subfolders) are NOT copied. .EXAMPLE DS_CopyFile -SourceFiles "C:\Temp\*" -Destination "C:\Temp2" Copies all files in the directory 'C:\Temp' to the destination directory 'C:\Temp2'. Subfolders as well as files within these subfolders are also copied. .EXAMPLE DS_CopyFile -SourceFiles "C:\Temp\*.txt" -Destination "\\localhost\Temp2" Copies all files with the file extension '*.txt' in the directory 'C:\Temp' to the destination directory '\\localhost\Temp2'. The directory in this example is a network directory (UNC path). #> [CmdletBinding()] Param( [Parameter(Mandatory=$true, Position = 0)][String]$SourceFiles, [Parameter(Mandatory=$true, Position = 1)][String]$Destination ) begin { [string]$FunctionName = $PSCmdlet.MyInvocation.MyCommand.Name DS_WriteLog "I" "START FUNCTION - $FunctionName" $LogFile } process { DS_WriteLog "I" "Copy the source file(s) '$SourceFiles' to '$Destination'" $LogFile # Retrieve the parent folder of the destination path if ( $Destination.Contains(".") ) { # In case the variable $Destination contains a dot ("."), return the parent folder of the path $TempFolder = split-path -path $Destination } else { $TempFolder = $Destination } # Check if the destination path exists. If not, create it. DS_WriteLog "I" "Check if the destination path '$TempFolder' exists. If not, create it" $LogFile if ( Test-Path $TempFolder) { DS_WriteLog "I" "The destination path '$TempFolder' already exists. Nothing to do" $LogFile } else { DS_WriteLog "I" "The destination path '$TempFolder' does not exist" $LogFile DS_CreateDirectory -Directory $TempFolder } # Copy the source files DS_WriteLog "I" "Start copying the source file(s) '$SourceFiles' to '$Destination'" $LogFile try { Copy-Item $SourceFiles -Destination $Destination -Force -Recurse DS_WriteLog "S" "Successfully copied the source files(s) '$SourceFiles' to '$Destination'" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to copy the source files(s) '$SourceFiles' to '$Destination'" $LogFile Exit 1 } } end { DS_WriteLog "I" "END FUNCTION - $FunctionName" $LogFile } } #========================================================================== # FUNCTION DS_DeleteDirectory # Description: delete the entire directory #========================================================================== Function DS_DeleteDirectory { <# .SYNOPSIS Delete a directory .DESCRIPTION Delete a directory .PARAMETER Directory This parameter contains the full path to the directory which needs to be deleted (for example C:\Temp\MyOldFolder). .EXAMPLE DS_DeleteDirectory -Directory "C:\Temp\MyOldFolder" Deletes the directory "C:\Temp\MyNewFolder" #> [CmdletBinding()] Param( [Parameter(Mandatory=$true, Position = 0)][String]$Directory ) begin { [string]$FunctionName = $PSCmdlet.MyInvocation.MyCommand.Name DS_WriteLog "I" "START FUNCTION - $FunctionName" $LogFile } process { DS_WriteLog "I" "Delete directory $Directory" $LogFile if ( Test-Path $Directory ) { try { Remove-Item $Directory -force -recurse | Out-Null DS_WriteLog "S" "Successfully deleted the directory $Directory" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to delete the directory $Directory (exit code: $($Error[0])!" $LogFile Exit 1 } } else { DS_WriteLog "I" "The directory $Directory does not exist. Nothing to do." $LogFile } } end { DS_WriteLog "I" "END FUNCTION - $FunctionName" $LogFile } } #========================================================================== ################ # 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 StoreFront (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 STOREFRONT # ################################################# DS_WriteLog "I" "Install Citrix StoreFront" $LogFile DS_WriteLog "-" "" $LogFile # Set the default to the StoreFront log files $DefaultStoreFrontLogPath = "$env:SystemRoot\temp\StoreFront" # Delete old log files (= delete the directory C:\Windows\temp\StoreFront" DS_WriteLog "I" "Delete old log folders" $LogFile DS_DeleteDirectory -Directory $DefaultStoreFrontLogPath DS_WriteLog "-" "" $LogFile # Install StoreFront $File = Join-Path $StartDir "Files\CitrixStoreFront-x64.exe" $Arguments = "-silent" DS_InstallOrUninstallSoftware -File $File -InstallationType "Install" -Arguments $Arguments DS_WriteLog "-" "" $LogFile # Copy the StoreFront log files from the StoreFront default log directory to our custom directory DS_WriteLog "I" "Copy the log files from the directory $DefaultStoreFrontLogPath to $LogDir" $LogFile DS_CopyFile -SourceFiles (Join-Path $DefaultStoreFrontLogPath "*.log") -Destination $LogDir # 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 -executionpolicy bypass -file C:\Temp\Citrix\StoreFront\Install_CitrixStoreFront.ps1
Log files are created in the directory C:\Logs\Citrix_StoreFront_(installation), but you can change this to any directory you want (see lines 347 and 348).
Part 3: Configure Citrix StoreFront
The third part of this article focuses on the configuration of the Citrix StoreFront.
If you have created the installation directory as described in Part 2, you can continue as follows:
- Copy the complete PowerShell script at the end of this part to a new PS1 file (e.g. Configure_CitrixStoreFront.ps1) and add this file to the root of your installation directory (not in the subdirectory Files).
- Optional: copy your certificate files to the installation directory in the subdirectory Files.
- Execute the PowerShell script:
powershell.exe -executionpolicy bypass -file C:\Temp\Citrix\StoreFront\Configure_CitrixStoreFront.ps1
StoreFront configuration steps
The configuration of your StoreFront server depends on your environment. StoreFront can be deployed as a single-site and a multi-site deployment (multiple IIS sites) You may want to create multiple stores in a single IIS site and perhaps even multiple farms and Receiver for Web sites per store. Besides this multitude of architectural scenarios, each of the aforementioned StoreFront components contains many detailed settings.
It is impossible for me to offer you a 100% “one-size-fits-all” solution or script. What I can offer you are some ready-to-use PowerShell functions and some practical examples of how to use these functions. This will allow you to configure your environment to your requirements.
At the end of this article, I have prepared a complete PowerShell script that offers all functions required to create a single-site and multi-site deployment containing one or more stores, farms, Receiver for Web services, and more. In the following paragraphs, I explain the script in detail by going through each of the main sections, which are:
- Install (SSL) certificates (optional)
- Create the StoreFront deployments, stores, and farms
- Disable CEIP (optional)
Note: In this article and in the complete PowerShell script I offer only minimum customizing of the store, farm, and Receiver for Web settings. The main focus of this article is to get your StoreFront configuration up and running. I am preparing a separate article that deals with the detailed configuration of StoreFront. This article will be ready in February 2018. A link to this article will be added to this article. |
Install (SSL) certificates (optional)
For those of you who would like to connect to StoreFront using SSL, continue reading.
Before creating the StoreFront deployment, the complete PowerShell script first installs the necessary certificates on the StoreFront server. These can be Trusted Root, Intermediate, personal certificates, including wildcard and Subject Alternative Name (SAN) certificates, and more. Certificate installation is handled by the function DS_InstallCertificate. The function excepts the following command line parameters:
- StoreScope
This parameter determines whether the local machine or the current user store is to be used (possible values are: CurrentUser or LocalMachine). - StoreName
This parameter contains the name of the store (possible values are: CA, My, Root, TrustedPublisher, and more).- My = Personal
- Root = Trusted Root Certificates Authorities
- CA = Intermediate Certificates Authorities
- TrustedPublisher = Trusted Publishers
- CertFile
This parameter contains the name, including path and file extension, of the certificate file (e.g. C:\MyCert.cer). - CertPassword
[Optional] This parameter is optional and is required in case the exported certificate is password protected (e.g. password = “abcdef”). Please see the article Encrypting passwords in a PowerShell script to learn how to avoid plain-text passwords in your scripts.
Note: I would be remiss to mention that you can also choose to use a Microsoft Group Policy to deploy your certificates to your servers. |
You can add the certificate files to the directory Files of the StoreFront installation directory as described in Part 2 of this article. Here are a couple of examples of how to call the function:
- DS_InstallCertificate -StoreScope “LocalMachine” -StoreName “Root” -CertFile “Files\MyRootCert.cer”
Install the root certificate ‘MyRootCert.cer’ in the Trusted Root Certificates Authorities store of the local machine. - DS_InstallCertificate -StoreScope “LocalMachine” -StoreName “CA” -CertFile “Files\MyIntermediateCert.cer”
Install the intermediate certificate ‘MyIntermediateCert.cer’ in the Intermediate Certificates Authorities store of the local machine - DS_InstallCertificate -StoreScope “LocalMachine” -StoreName “My” -CertFile “Files\MyPersonalCert.pfx” -CertPassword “mypassword”
Install the password-protected personal certificate (including private key) ‘MyUserCert.pfx’ in the Personal store of the local machine.
The complete PowerShell script at the end of this part includes the import of one root certificate and one personal certificate in lines 1088 to 1096.
Create the StoreFront deployments, stores, and farms
In case your infrastructure requires certificates, please make sure to install them on your StoreFront server before starting with your deployment. See the previous paragraph Install (SSL) certificates (optional) for more information.
The complete PowerShell script includes the function DS_CreateStoreFrontStore, which is responsible for creating your deployment. This function is the heart of the script and supports all kinds of deployment scenarios, such as:
- Single-site deployment (= basic deployment) with one store and one farm
- Single-site deployment with one or more stores and one or more farm per store
- Multi-site deployment* (= multiple IIS sites) with one or more stores per deployment and one or more farms per store:
- In case the host base URL is different than any of the existing ones, the function automatically creates the IIS site.
- Port bindings and SSL certificate bindings are automatically configured.
The function can be called multiple times to create multiple configurations on the same local server. In case a deployment, store, farm, Receiver for Web, or PNAgent service already exists, the function recognizes this and simply skips this step and continues with the next one.
*Since StoreFront 3.8, Citrix officially supports multi-site deployments. Multi-site deployments are multiple IIS sites on one StoreFront server. Each IIS site contains one or more stores and each store consists of one or more farms. Please be aware that when you implement a multi-site deployment, you can no longer use the StoreFront console!
This also means that you cannot use the console to propagate changes to other StoreFront servers in the StoreFront group. You have to use PowerShell to configure each of your StoreFront servers.
The function DS_CreateStoreFrontStore is quite a complex one and comes with many parameters (listed below). The execution process of the function is as follows:
- Import the required StoreFront and IIS PowerShell modules
- Check if a StoreFront deployment exists:
- If not, create the first deployment using the Default Web Site (= IIS site ID 1):
- Delete any existing bindings on the Default Web Site
- In case the variable $HTTPSPort was populated, bind the certificate specified in the parameter $CertSubjectName to the HTTPS port of the Default Web Site. In case the $CertSubjectName parameter is not used, the value of $HostbaseUrl is used instead.
- Add the URL to the host header of the Default Web Site if the variable $AddHostHeaderToIISSiteBinding is set to $True, otherwise do not set the host header (= default).
- Create the first StoreFront deployment using the PowerShell cmdlet Add-STFDeployment.
- If yes, check if the host base URL (= hostname on the IIS site, see Edit Bindings) exists.
- If yes, continue with that particular IIS site.
- If not, create a new IIS site (= multi-site deployment):
- The name of the IIS directory is defined in the variable $IISSiteDir (e.g. C:\inetpub\MyFarm). In case this optional parameter was not used, the function automatically generates a new directory name based on the new IIS site ID, for example, C:\inetpub\wwwroot2.
- Create the IIS site directory
- Create the IIS site with the port bindings as defined in the variables $HTTPPort and $HTTPSPort. The name of the IIS site (as visible in the IIS Manager) is the farm name, defined in the variable $FarmName.
- Retrieve the site ID of the new site.
- In case the variable $HTTPSPort was populated, bind the certificate specified in the parameter $CertSubjectName to the HTTPS port of the new IIS site. In case the $CertSubjectName parameter is not used, the value of $HostbaseUrl is used instead.
- Add the URL to the host header of the new IIS site if the variable $AddHostHeaderToIISSiteBinding is set to $True, otherwise do not set the host header (= default).
- Create the new StoreFront deployment using the PowerShell cmdlet Add-STFDeployment.
- Determine the virtual paths for the Authentication and Receiver for Web service.
- Determine if the Authentication service exists (Get-STFAuthenticationService).
- If yes, continue with the next step.
- If not, create it (Add-STFAuthenticationService).
- Determine if the store service exists at the path defined in the parameter $StoreVirtualPath in the corresponding IIS site.
- If yes, continue with the next step and check if the farm, as defined in the parameter $FarmName, already exists.
- If not, create the new farm (Add-STFStoreFarm) using the parameters $FarmName, $FarmType, $FarmServers, $SSLRelayPort (XA 6.5 only), $LoadBalanceServers, $XMLPort, and $TransportType.
- If yes, continue with the next step.
- If not, create the store, the authentication service, and the first farm of that store (Add-STFStoreService) using the parameters $FriendlyName, $SiteId, $StoreVirtualPath, $authentication, $FarmName, $FarmType, $FarmServers, $SSLRelayPort, $LoadbalanceServers, $XMLPort and $TransportType.
- If yes, continue with the next step and check if the farm, as defined in the parameter $FarmName, already exists.
- Determine if the Receiver for Web service exists (Get-STFWebReceiverService).
- If yes, continue with the next step.
- If not, create it (Add-STFWebReceiverService) using the virtual path defined in the parameter $receiverVirtualPath.
- Determine if the PNAgent service exists (Get-STFStorePna).
- If yes, enable it if the parameter $EnablePNAgent is set to $True. Also configure the logon method ($LogonMethod), whether or not the user is allowed to change their password ($PNAgentAllowUserPwdChange), and whether or not the PNAgent is to be set as default ($PNAgentDefaultService).
- If not, create the first deployment using the Default Web Site (= IIS site ID 1):
The function DS_CreateStoreFrontStore includes the following parameters:
- FriendlyName
[Optional] This parameter configures the friendly name of the store, for example, “MyStore” or “Marketing”.
If this parameter is omitted, the script generates the friendly name automatically based on the farm name. For example, if the farm name is MyFarm, the friendly name would be Store – MyFarm. - HostBaseUrl
[Mandatory] This parameter determines the URL of the IIS site (the StoreFront “deployment”), for example, “https://mysite.com” or “http://mysite.com”. - CertSubjectName
[Optional] This parameter determines the Certificate Subject Name of the certificate you want to bind to the IIS SSL port on the local StoreFront server.
If this parameter is omitted, the Certificate Subject Name (the DNS name) will be automatically extracted from the host base URL (see the previous variable HostBaseUrl). Two commonly used values are:- $($env:ComputerName).mydomain.local -> for certificates where the subject name is the same as the local machine name.
- *.mydomain.local -> for wildcard certificates or Subject Alternative Name (SAN) certificates. SAN certificates contain more than one subject name.
- AddHostHeaderToIISSiteBinding
[Optional] This parameter determines whether the host base URL (the host header) is added to IIS site binding.
If this parameter is omitted, the value is set to ‘$false’ and the host header is NOT added to IIS site binding. - IISSiteDir
[Optional] This parameter contains the directory path to the IIS site. This parameter is only used in multiple deployment configurations whereby multiple IIS sites are created.
If this parameter is omitted, a directory will be automatically generated by the script. - Farmtype
[Optional] This parameter determines the farm type. Possible values are XenDesktop | XenApp | AppController | VDIinaBox.
If this parameter is omitted, the default value “XenDesktop” is used. - FarmName
[Mandatory] This parameter contains the name of the farm within the store. The farm name should be unique within a store. - FarmServers
[Mandatory] This parameter, whose data type is an array, contains a list of farm servers (XML brokers or Delivery Controller). Enter the list comma separated (e.g. -FarmServers “Server1.fqdn.com”,”Server2.fqdn.com”,”Server3.fqdn.com”). - StoreVirtualPath
[Optional] This parameter contains the partial path of the StoreFront store, for example: -StoreVirtualPath “/Citrix/MyStore” or -StoreVirtualPath “/Citrix/Store1”.
If this parameter is omitted, the default value /Citrix/Store is used - ReceiverVirtualPath
[Optional] This parameter contains the partial path of the Receiver for Web site in the StoreFront store, for example: -ReceiverVirtualPath “/Citrix/MyStoreWeb” or -ReceiverVirtualPath “/Citrix/Store1ReceiverWeb”.
If this parameter is omitted, the default value /Citrix/StoreWeb is used - SSLRelayPort
[Mandatory] This parameter contains the SSL Relay port (XenApp 6.5 only) used for communicating with the XenApp servers. The default value is 443 (HTTPS). - LoadBalanceServers
[Optional] This parameter determines whether to load balance the Delivery Controllers or to use them in fail-over order (if specifying more than one server). Possible values are: $True | $False.
If this parameter is omitted, the default value $False is used, which means that fail-over is used instead of load balancing. - XMLPort
[Optional] This parameter contains the XML service port used for communicating with the XenApp\XenDesktop servers. Default values are 80 (HTTP) and 443 (HTTPS), but you can also use other ports (depending on how you configured your XenApp/XenDesktop servers).
If this parameter is omitted, the default value 80 is used. - HTTPPort
[Optional] This parameter contains the port used for HTTP communication on the IIS site. The default value is 80, but you can also use other ports.
If this parameter is omitted, the default value 80 is used. - HTTPSPort
[Optional] This parameter contains the port used for HTTPS communication on the IIS site. If this value is not set, no HTTPS binding is created. - TransportType
[Optional] This parameter contains the type of transport to use for the XML service communication. Possible values are HTTP | HTTPS | SSL.
If this parameter is omitted, the default value HTTP is used. - EnablePNAgent
[Mandatory] This parameter determines whether the PNAgent site is created and enabled. Possible values are: $True | $False.
If this parameter is omitted, the default value $True is used. - PNAgentAllowUserPwdChange
[Optional] This parameter determines whether the user is allowed to change their password on a PNAgent. Possible values are: $True | $False.
Note: this parameter can only be used if the logon method for PNAgent is set to ‘prompt’.
!! Only add this parameter when the parameter EnablePNAgent is set to $True
If this parameter is omitted, the default value $True is used. - PNAgentDefaultService
[Optional] This parameter determines whether this PNAgent site is the default PNAgent site in the store. Possible values are: $True | $False.
!! Only add this parameter when the parameter EnablePNAgent is set to $True
If this parameter is omitted, the default value $True is used. - LogonMethod
[Optional] This parameter determines the logon method for the PNAgent site. Possible values are Anonymous | Prompt | SSON | Smartcard_SSON | Smartcard_Prompt. Only one value can be used at a time.
!! Only add this parameter when the parameter EnablePNAgent is set to $True
If this parameter is omitted, the default value “SSON” (Single Sign-On) is used.
Here are a couple of examples of how to call the function:
- DS_CreateStoreFrontStore -HostBaseUrl “https://myurl.com” -FarmName “MyFarm” -FarmServers “Server1″,”Server2” -EnablePNAgent $True
Creates a basic StoreFront deployment, a XenDesktop store, farm, Receiver for Web site, and a PNAgent site. The above example uses only the 4 mandatory parameters and does not include any of the optional parameters. In case the HostBaseUrl is different than an already configured one on the local StoreFront server, a new IIS site is automatically created. The other settings are configured automatically:- The name of the store (the friendly or display name) will be set to “Store – $FarmName”
- The IIS site directory will be C:\inetpub\wwwroot if it is the first deployment on the local StoreFront server. Otherwise, the directory will be C:\inetpub\wwwroot$SiteID, for example, C:\inetpub\wwwroot2.
- The farm type will be set to XenDesktop.
- The store virtual path will be set to /Citrix/Store.
- The receiver virtual path will be set to /Citrix/StoreWeb.
- The XML port will be set to 80 and the transport type to HTTP for communication with the delivery controllers.
- On the local IIS site, ports 80 and 443 will be bound.
- The PNAgent will be enabled and set to default. The login method is set to Single Sign-On (SSON).
- DS_CreateStoreFrontStore -FriendlyName “MyStore” -HostBaseUrl “https://myurl.com” -CertSubjectName “$($env:ComputerName).mydomain.local” -FarmName “MyFarm” -FarmServers “Server1″,”Server2” -StoreVirtualPath “/Citrix/MyStore” -ReceiverVirtualPath “/Citrix/MyStoreWeb” -XMLPort 443 -TransportType “HTTPS” -HTTPSPort 443 -EnablePNAgent $True -PNAgentAllowUserPwdChange $False -PNAgentDefaultService $True -LogonMethod “Prompt”
Creates a StoreFront deployment, a XenDesktop store, farm, Receiver for Web site, and a PNAgent site. The communication with the Delivery Controllers uses port 443 and transport type HTTPS. An SSL port is added to the StoreFront (IIS) site (variable HTTPSPort). The subject name of the certificate that will be bound to that HTTPS port is set in the variable CertSubjectName and is different from the host base URL. PNAgent users are not allowed to change their passwords and the logon method is “prompt” instead of the default SSON (= Single Sign-On). In case the HostBaseUrl is different than an already configured one on the local StoreFront server, a new IIS site is automatically created. - DS_CreateStoreFrontStore -FriendlyName “MyStore” -HostBaseUrl “https://anotherurl.com” -FarmName “MyFarm” -FarmServers “Server1″,”Server2” -EnablePNAgent $False
Creates a StoreFront deployment, a XenDesktop store, farm, and Receiver for Web site, but DOES NOT create and enable a PNAgent site. The communication with the Delivery Controllers uses the default port 80 and transport type HTTP.
In case the HostBaseUrl is different than an already configured one on the local StoreFront server, a new IIS site is automatically created.
The complete PowerShell script at the end of this part includes the creation of one StoreFront deployment in line 1101.
One more thing, the function DS_CreateStoreFrontStore uses another function called DS_BindCertificateToIISPort. This function is responsible for binding a certificate to the HTTPS port and requires two variables: the host base URL or a domain name and the port number to bind the certificate to. The function automatically checks for a wildcard or Subject Alternative Name (SAN) certificate in case the host base URL does not match the subject name of any of the installed certificates.
Disable CEIP (optional)
The Customer Experience Improvement Program (CEIP) can be disabled in Citrix StoreFront. During installation, CEIP is enabled by default. The first upload of data occurs approximately seven days after you install StoreFront. You can change this default in a registry setting.
Disabling CEIP can be done in the registry by setting the registry item Enabled (DWORD) in the key HKLM\Software\Citrix\Telemetry\CEIP to 0.
Using PowerShell, the command to disable CEIP is as follows:
1 2 3 4 5 6 7 |
try { New-ItemProperty -Path HKLM:\SOFTWARE\Citrix\Telemetry\CEIP -Name Enabled -PropertyType DWORD -Value 0 -EA Stop | Out-Null Write-Host "CEIP was successfully disabled" } catch { Write-Host "An error occurred trying to disable CEIP (error: $($Error[0]))" Exit 1 } |
Reference: https://docs.citrix.com/en-us/storefront/current-release/install-standard.html
Please note that I am not stating that CEIP should be disabled! I am only showing you how to do it in case you want to.
The complete PowerShell script at the end of this part includes the disabling of CEIP including detailed logging and error handling in line 1107.
StoreFront deployment scenarios
In the following paragraphs, I outline a number of StoreFront deployment scenarios as well as a detailed description of how to create these using the complete PowerShell script below.
Scenario 1: create a single-site deployment with a single store and a single farm
The minimum deployment is to create one IIS site containing one store containing one farm. The authentication service, the Receiver for Web, and PNAgent service are also created.
In the complete PowerShell script you simply add the following line.
1 2 |
# Create a basic StoreFront deployment containing one store, one farm, an authentication service, a Receiver for Web service and a PNAgent service DS_CreateStoreFrontStore -FriendlyName "MyStore" -HostBaseUrl "https://myurl.com" -StoreVirtualPath "/Citrix/Store for Marketing" -FarmName "MyFarm" -FarmServers "Server1.mydomain.com","Server2.mydomain.com" -EnablePNAgent $True |
In the example above the PowerShell code calls the function DS_CreateStoreFrontStore.
Scenario 2: create a single-site deployment with multiple stores and one farm per store
In case you would like to create a single-site deployment, but with multiple stores, call the function DS_CreateStoreFrontStore as many times as you need stores, but each time with a different virtual path (= variable -StoreVirtualPath). The virtual path is the parameter that determines whether a new store is created or not.
The following PowerShell code creates one deployment containing three stores (and one farm per store). Add these lines to the complete PowerShell script.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# Create a StoreFront deployment with three stores and one farm per store (and one Receiver for Web service and a PNAgent service per store) # Create the deployment "https://myurl.com" and create the first store called MyStore1 with one farm called MyFarm1 DS_CreateStoreFrontStore -FriendlyName "MyStore1" -StoreVirtualPath "/Citrix/Store1" -HostBaseUrl "https://myurl.com" -FarmType "XenDesktop" -FarmName "MyFarm1" -FarmServers "Server1.mydomain.com","Server1.mydomain.com" -EnablePNAgent $True DS_WriteLog "-" "" $LogFile # Use the existing deployment "https://myurl.com" and create the second store called MyStore2 with one farm called MyFarm1 DS_CreateStoreFrontStore -FriendlyName "MyStore2" -StoreVirtualPath "/Citrix/Store2" -HostBaseUrl "https://myurl.com" -FarmType "XenDesktop" -FarmName "MyFarm1" -FarmServers "Server1.mydomain.com","Server1.mydomain.com" -EnablePNAgent $True DS_WriteLog "-" "" $LogFile # Use the existing deployment "https://myurl.com" and create the third store called MyStore3 with one farm called MyFarm2 DS_CreateStoreFrontStore -FriendlyName "MyStore3" -StoreVirtualPath "/Citrix/Store3" -HostBaseUrl "https://myurl.com" -FarmType "XenDesktop" -FarmName "MyFarm2" -FarmServers "Server100.mydomain.com","Server100.mydomain.com" -EnablePNAgent $True DS_WriteLog "-" "" $LogFile |
Scenario 3: create a single-site deployment with one store and multiple farms
In case you would like to create a single-site deployment with one store, but with multiple farms, call the function DS_CreateStoreFrontStore as many times as you need a new farm, but each time with a different farm name (= variable -FarmName). The farm name is the parameter that determines whether a new farm is created or not. As you can see in the example, the virtual path to the store (= variable -StoreVirtualPath) remains the same. This ensures that no new store is created.
The following PowerShell code creates one deployment containing one store and three farms. Add these lines to the complete PowerShell script.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# Create a StoreFront deployment with one store and three farms and one Receiver for Web service and PNAgent service # Create the deployment "https://myurl.com" and create the store called MyStore with one farm called MyFarm1 DS_CreateStoreFrontStore -FriendlyName "MyStore" -StoreVirtualPath "/Citrix/MyStore" -HostBaseUrl "https://myurl.com" -FarmType "XenDesktop" -FarmName "MyFarm1" -FarmServers "Server1.mydomain.com","Server1.mydomain.com" -EnablePNAgent $True DS_WriteLog "-" "" $LogFile # Use the existing deployment "https://myurl.com" and the existing store called MyStore and create the second farm called MyFarm2 DS_CreateStoreFrontStore -FriendlyName "MyStore" -StoreVirtualPath "/Citrix/MyStore" -HostBaseUrl "https://myurl.com" -FarmType "XenpApp" -FarmName "MyFarm2" -FarmServers "Server5.mydomain.com","Server6.mydomain.com" -EnablePNAgent $True DS_WriteLog "-" "" $LogFile # Use the existing deployment "https://myurl.com" and the existing store called MyStore and create the third farm called MyFarm3 DS_CreateStoreFrontStore -FriendlyName "MyStore" -StoreVirtualPath "/Citrix/MyStore" -HostBaseUrl "https://myurl.com" -FarmType "XenDesktop" -FarmName "MyFarm3" -FarmServers "Server100.mydomain.com","Server101.mydomain.com" -EnablePNAgent $True DS_WriteLog "-" "" $LogFile |
Let’s take a closer look at the PowerShell code above.
The function DS_CreateStoreFrontStore is called three times. Each time, the virtual path to the store is the same (/Citrix/MyStore). The first time the function is called, the store is created. The second and third time the function is called, the function realizes that the store already exists and proceeds with the next step. Since the farm name is different each time the function is called, the function creates a new farm in the existing store. A total of three farms are created within the same store.
Scenario 4: create a single-site deployment with two stores, two farms per store, and two Receiver for Web sites
In case you would like to create a single-site deployment with multiple stores and multiple farms, call the function DS_CreateStoreFrontStore as many times as you need a new store and a new farm. Each time you have to use a different combination of the variables -FarmName (the farm name) and -StoreVirtualPath (the virtual path to the store).
The following PowerShell code creates one deployment containing two stores and two farms per store. Add these lines to the complete PowerShell script.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# Create a StoreFront deployment with one store and three farms and one Receiver for Web service and PNAgent service # Create the deployment "https://myurl.com" and create the store called MyStore1 with one farm called MyFarm1 DS_CreateStoreFrontStore -FriendlyName "MyStore1" -StoreVirtualPath "/Citrix/MyStore1" -HostBaseUrl "https://myurl.com" -FarmType "XenDesktop" -FarmName "MyFarm1" -FarmServers "Server1.mydomain.com","Server1.mydomain.com" -EnablePNAgent $True DS_WriteLog "-" "" $LogFile # Use the existing store Store1 and create the farm called MyFarm2 DS_CreateStoreFrontStore -FriendlyName "MyStore1" -StoreVirtualPath "/Citrix/MyStore1" -HostBaseUrl "https://myurl.com" -FarmType "XenpApp" -FarmName "MyFarm2" -FarmServers "Server5.mydomain.com","Server6.mydomain.com" -EnablePNAgent $True DS_WriteLog "-" "" $LogFile # Create a new store called Store2 with one farm called MyFarm3 DS_CreateStoreFrontStore -FriendlyName "MyStore2" -StoreVirtualPath "/Citrix/MyStore2" -HostBaseUrl "https://myurl.com" -FarmType "XenDesktop" -FarmName "MyFarm3" -FarmServers "Server100.mydomain.com","Server101.mydomain.com" -EnablePNAgent $True DS_WriteLog "-" "" $LogFile # Use the existing store Store2 and create the farm called MyFarm4 DS_CreateStoreFrontStore -FriendlyName "MyStore2" -StoreVirtualPath "/Citrix/MyStore2" -HostBaseUrl "https://myurl.com" -FarmType "XenpApp" -FarmName "MyFarm4" -FarmServers "Server500.mydomain.com","Server501.mydomain.com" -EnablePNAgent $True DS_WriteLog "-" "" $LogFile |
Let’s take a closer look at the PowerShell code above.
In line 4, the function DS_CreateStoreFrontStore creates the first store called MyStore1 in the virtual path /Citrix/MyStore1 with the XenDesktop farm MyFarm1. Line 9 contains the same store details. The function DS_CreateStoreFrontStore will determine that the store already exists and will continue with the next step. However, the function will determine that the farm called Farm2 does not yet exist and will proceed to create this farm.
The same process is repeated in line 14 and 19. In line 14, the store called MyStore2 with farm Farm3 is created. In line 19, the farm Farm4 is added to the store MyStore2.
Scenario 5: create a multi-site deployment (two IIS sites) with one store per deployment and two farms for each store
In case you would like to create a multi-site deployment (two IIS sites in this example) with one store per deployment and two farms per store, you need to call the function DS_CreateStoreFrontStore a total of four times. Two different host base URLs are used to create the two deployments (= IIS sites). Also, two different store names (= variable -StoreVirtualPath) and four different farm names (= variable -FarmName) are used. The store name is the parameter that determines if a new store is created; the farm name is the parameter that determines whether a new farm is created or not.
The following PowerShell code creates two deployments containing one store per deployment with two farms per store. Add these lines to the complete PowerShell script.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# Create two StoreFront deployments with one store per deployment and two farms per store # Create the first deployment (= IIS site) "https://myurl.com" with the store "Store1" and the farm "MyFarm1" DS_CreateStoreFrontStore -FriendlyName "MyStore1" -HostBaseUrl "https://myurl.com" -StoreVirtualPath "/Citrix/MyStore1" -FarmType "XenDesktop" -FarmName "MyFarm1" -FarmServers "Server1.mydomain.com","Server1.mydomain.com" -EnablePNAgent $True DS_WriteLog "-" "" $LogFile # Use the first deployment (https://myurl.com) with the store "Store1" and create the second farm "MyFarm2" DS_CreateStoreFrontStore -FriendlyName "MyStore1" -HostBaseUrl "https://myurl.com" -StoreVirtualPath "/Citrix/MyStore1" -FarmType "XenDesktop" -FarmName "MyFarm2" -FarmServers "Server10.mydomain.com","Server11.mydomain.com" -EnablePNAgent $True DS_WriteLog "-" "" $LogFile # Create the second deployment (= IIS site) "https://myurl2.com" with the store "Store2" and the farm "MyFarm3" DS_CreateStoreFrontStore -FriendlyName "MyStore2" -HostBaseUrl "https://myurl2.com" -StoreVirtualPath "/Citrix/MyStore2" -FarmType "XenDesktop" -FarmName "MyFarm3" -FarmServers "Server20.mydomain.com","Server21.mydomain.com" -EnablePNAgent $True DS_WriteLog "-" "" $LogFile # Use the second deployment (https://myurl2.com) with the store "Store2" and create the second farm "MyFarm4" DS_CreateStoreFrontStore -FriendlyName "MyStore2" -HostBaseUrl "https://myurl2.com" -StoreVirtualPath "/Citrix/MyStore2" -FarmType "XenDesktop" -FarmName "MyFarm4" -FarmServers "Server30.mydomain.com","Server31.mydomain.com" -EnablePNAgent $True DS_WriteLog "-" "" $LogFile |
Let’s take a closer look at the PowerShell code above.
The function DS_CreateStoreFrontStore is called four times. The first two times, the host base URL is the same, which means that the same IIS site is used. Also, the store name is the same, which means that the same store is used. However, two different farm names are used, which means that two farms are created within the one store.
The third and fourth time the function is executed, a different host base URL is used, which means that an additional deployment (= IIS site) is created. In both cases the store Store2 is used, which means that this second deployment contains one store only. Two different farm names are used, which results in two farms being created in Store2.
Complete script for configuring Citrix StoreFront
In case you use my installation template, this is what the complete script, including logging and error handling, looks like.
Please customize the variables $BaseLogDir and $PackageName in lines 1066 and 1067 to your requirements (or leave them as-is). Also, customize the code in lines 1088 to 1107 (e.g. the certificate file names in lines 1089 and 1095).
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 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 |
#========================================================================== # # Configure Citrix StoreFront # # AUTHOR: Dennis Span (https://dennisspan.com) # DATE : 19.01.2018 # # COMMENT: # This script has been prepared for Windows Server 2008 R2, 2012 R2 and 2016. # This script has been tested on Windows Server 2016 version 1607. # # The version of StoreFront used in this script is 3.13 (released in Q4 2017), but will most likely # work on older versions as well (at least from StoreFront 3.8 and newer). # # This script configures the local Citrix StoreFront server and includes the following sections: # -Install and bind SSL certificate # -Create StoreFront deployment, stores, farms, Receiver for Web services and more # -Disable CEIP # #========================================================================== # 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)][ValidateSet("I","S","W","E","-",IgnoreCase = $True)][String]$InformationType, [Parameter(Mandatory=$true, Position = 1)][AllowEmptyString()][String]$Text, [Parameter(Mandatory=$true, Position = 2)][AllowEmptyString()][String]$LogFile ) begin { } process { $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.ToUpper() + " - " + $Text) } } end { } } #========================================================================== # FUNCTION DS_CreateRegistryKey #========================================================================== Function DS_CreateRegistryKey { <# .SYNOPSIS Create a registry key .DESCRIPTION Create a registry key .PARAMETER RegKeyPath This parameter contains the registry path, for example 'hklm:\Software\MyApp' .EXAMPLE DS_CreateRegistryKey -RegKeyPath "hklm:\Software\MyApp" Creates the new registry key 'hklm:\Software\MyApp' #> [CmdletBinding()] Param( [Parameter(Mandatory=$true, Position = 0)][String]$RegKeyPath ) begin { [string]$FunctionName = $PSCmdlet.MyInvocation.MyCommand.Name DS_WriteLog "I" "START FUNCTION - $FunctionName" $LogFile } process { DS_WriteLog "I" "Create registry key $RegKeyPath" $LogFile if ( Test-Path $RegKeyPath ) { DS_WriteLog "I" "The registry key $RegKeyPath already exists. Nothing to do." $LogFile } else { try { New-Item -Path $RegkeyPath -Force | Out-Null DS_WriteLog "S" "The registry key $RegKeyPath was created successfully" $LogFile } catch{ DS_WriteLog "E" "An error occurred trying to create the registry key $RegKeyPath (exit code: $($Error[0])!" $LogFile DS_WriteLog "I" "Note: define the registry path as follows: hklm:\Software\MyApp" $LogFile Exit 1 } } } end { DS_WriteLog "I" "END FUNCTION - $FunctionName" $LogFile } } #========================================================================== # FUNCTION DS_SetRegistryValue #========================================================================== Function DS_SetRegistryValue { <# .SYNOPSIS Set a registry value .DESCRIPTION Set a registry value .PARAMETER RegKeyPath This parameter contains the registry path, for example 'hklm:\Software\MyApp' .PARAMETER RegValueName This parameter contains the name of the new registry value, for example 'MyValue' .PARAMETER RegValue This parameter contains the value of the new registry entry, for example '1' .PARAMETER Type This parameter contains the type (possible options are: String, Binary, DWORD, QWORD, MultiString, ExpandString) .EXAMPLE DS_SetRegistryValue -RegKeyPath "hklm:\Software\MyApp" -RegValueName "MyStringValue" -RegValue "Enabled" -Type "String" Creates a new string value called 'MyStringValue' with the value of 'Enabled' .Example DS_SetRegistryValue -RegKeyPath "hklm:\Software\MyApp" -RegValueName "MyBinaryValue" -RegValue "01" -Type "Binary" Creates a new binary value called 'MyBinaryValue' with the value of '01' .Example DS_SetRegistryValue -RegKeyPath "hklm:\Software\MyApp" -RegValueName "MyDWORDValue" -RegValue "1" -Type "DWORD" Creates a new DWORD value called 'MyDWORDValue' with the value of 1 .Example DS_SetRegistryValue -RegKeyPath "hklm:\Software\MyApp" -RegValueName "MyQWORDValue" -RegValue "1" -Type "QWORD" Creates a new QWORD value called 'MyQWORDValue' with the value of 1 .Example DS_SetRegistryValue -RegKeyPath "hklm:\Software\MyApp" -RegValueName "MyMultiStringValue" -RegValue "Value1,Value2,Value3" -Type "MultiString" Creates a new multistring value called 'MyMultiStringValue' with the value of 'Value1 Value2 Value3' .Example DS_SetRegistryValue -RegKeyPath "hklm:\Software\MyApp" -RegValueName "MyExpandStringValue" -RegValue "MyValue" -Type "ExpandString" Creates a new expandstring value called 'MyExpandStringValue' with the value of 'MyValue' #> [CmdletBinding()] Param( [Parameter(Mandatory=$true, Position = 0)][String]$RegKeyPath, [Parameter(Mandatory=$true, Position = 1)][String]$RegValueName, [Parameter(Mandatory=$false, Position = 2)][String[]]$RegValue = "", [Parameter(Mandatory=$true, Position = 3)][String]$Type ) begin { [string]$FunctionName = $PSCmdlet.MyInvocation.MyCommand.Name DS_WriteLog "I" "START FUNCTION - $FunctionName" $LogFile } process { DS_WriteLog "I" "Set registry value $RegValueName = $RegValue (type $Type) in $RegKeyPath" $LogFile # Create the registry key in case it does not exist if ( !( Test-Path $RegKeyPath ) ) { DS_CreateRegistryKey $RegKeyPath } # Create the registry value try { if ( ( "String", "ExpandString", "DWord", "QWord" ) -contains $Type ) { New-ItemProperty -Path $RegKeyPath -Name $RegValueName -Value $RegValue[0] -PropertyType $Type -Force | Out-Null } else { New-ItemProperty -Path $RegKeyPath -Name $RegValueName -Value $RegValue -PropertyType $Type -Force | Out-Null } DS_WriteLog "S" "The registry value $RegValueName = $RegValue (type $Type) in $RegKeyPath was set successfully" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to set the registry value $RegValueName = $RegValue (type $Type) in $RegKeyPath" $LogFile DS_WriteLog "I" "Note: define the registry path as follows: hklm:\Software\MyApp" $LogFile Exit 1 } } end { DS_WriteLog "I" "END FUNCTION - $FunctionName" $LogFile } } #========================================================================== # FUNCTION DS_CreateDirectory #========================================================================== Function DS_CreateDirectory { <# .SYNOPSIS Create a new directory .DESCRIPTION Create a new directory .PARAMETER Directory This parameter contains the name of the new directory including the full path (for example C:\Temp\MyNewFolder). .EXAMPLE DS_CreateDirectory -Directory "C:\Temp\MyNewFolder" Creates the new directory "C:\Temp\MyNewFolder" #> [CmdletBinding()] Param( [Parameter(Mandatory=$true, Position = 0)][String]$Directory ) begin { [string]$FunctionName = $PSCmdlet.MyInvocation.MyCommand.Name DS_WriteLog "I" "START FUNCTION - $FunctionName" $LogFile } process { DS_WriteLog "I" "Create directory $Directory" $LogFile if ( Test-Path $Directory ) { DS_WriteLog "I" "The directory $Directory already exists. Nothing to do." $LogFile } else { try { New-Item -ItemType Directory -Path $Directory -force | Out-Null DS_WriteLog "S" "Successfully created the directory $Directory" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to create the directory $Directory (exit code: $($Error[0])!" $LogFile Exit 1 } } } end { DS_WriteLog "I" "END FUNCTION - $FunctionName" $LogFile } } #========================================================================== # Function DS_InstallCertificate #========================================================================== # The following main certificate stores exist: # -"CA" = Intermediate Certificates Authorities # -"My" = Personal # -"Root" = Trusted Root Certificates Authorities # -"TrustedPublisher" = Trusted Publishers # Note: to find the names of all existing certificate stores, use the following PowerShell command: Get-Childitem cert:\localmachine # Note: to secure passwords in a PowerShell script, see my article https://dennisspan.com/encrypting-passwords-in-a-powershell-script/ Function DS_InstallCertificate { <# .SYNOPSIS Install a certificate .DESCRIPTION Install a certificate .PARAMETER StoreScope This parameter determines whether the local machine or the current user store is to be used (possible values are: CurrentUser or LocalMachine) .PARAMETER StoreName This parameter contains the name of the store (possible values are: CA, My, Root, TrustedPublisher and more) .PARAMETER CertFile This parameter contains the name, including path and file extension, of the certificate file (e.g. C:\MyCert.cer) .PARAMETER CertPassword This parameter is optional and is required in case the exported certificate is password protected .EXAMPLE DS_InstallCertificate -StoreScope "LocalMachine" -StoreName "Root" -CertFile "C:\Temp\MyRootCert.cer" Installs the root certificate 'MyRootCert.cer' in the Trusted Root Certificates Authorities store of the local machine .EXAMPLE DS_InstallCertificate -StoreScope "LocalMachine" -StoreName "CA" -CertFile "C:\Temp\MyIntermediateCert.cer" Installs the intermediate certificate 'MyIntermediateCert.cer' in the Intermediate Certificates Authorities store of the local machine .EXAMPLE DS_InstallCertificate -StoreScope "LocalMachine" -StoreName "My" -CertFile "C:\Temp\MyPersonalCert.cer" -CertPassword "mypassword" Installs the password protected intermediate certificate 'MyPersonalCert.cer' in the Personal store of the local machine .EXAMPLE DS_InstallCertificate -StoreScope "CurrentUser" -StoreName "My" -CertFile "C:\Temp\MyUserCert.pfx" Installs the user certificate 'MyUserCert.pfx' in the Personal store of the current user #> [CmdletBinding()] param ( [parameter(mandatory=$True,Position=1)] [string] $StoreScope, [parameter(mandatory=$True,Position=2)] [string] $StoreName, [parameter(mandatory=$True,Position=3)] [string] $CertFile, [parameter(mandatory=$False,Position=4)] [string] $CertPassword ) begin { [string]$FunctionName = $PSCmdlet.MyInvocation.MyCommand.Name DS_WriteLog "I" "START FUNCTION - $FunctionName" $LogFile } process { # Translation table for StoreScope switch ($StoreScope) { "LocalMachine" { $StoreScopeTemp = "local machine"} "CurrentUser" { $StoreScopeTemp = "current user"} default {} } # Translation table for StoreName switch ($StoreName) { "CA" { $StoreNameTemp = "Intermediate Certificates Authorities (= CA)"} "My" { $StoreNameTemp = "Personal (= My)"} "Root" { $StoreNameTemp = "Trusted Root Certificates Authorities (= Root)"} "TrustedPublisher" { $StoreNameTemp = "Trusted Publishers (= TrustedPublisher)"} default {} } DS_WriteLog "I" "Import the certificate '$CertFile' in the $StoreScopeTemp store $StoreNameTemp" $LogFile # Check if the certificate file exists. if ( !(Test-Path $CertFile) ) { DS_WriteLog "E" "The file '$CertFile' does not exist. This script will now quit" $LogFile Exit 1 } # Import the certificate to the store if (Test-Path "cert:\$StoreScope\$StoreName") { $Cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $CertFile,$CertPassword $Store = New-Object System.Security.Cryptography.X509Certificates.X509Store $StoreName,$StoreScope $Store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite) $Store.Add($Cert) $Store.Close() DS_WriteLog "S" "The certificate '$CertFile' was imported successfully in the $StoreScopeTemp store $StoreNameTemp" $LogFile } else { DS_WriteLog "E" "The store does not exist. This script will now quit" $LogFile } } end { DS_WriteLog "I" "END FUNCTION - $FunctionName" $LogFile } } #========================================================================== # Function DS_BindCertificateToIISPort #========================================================================== Function DS_BindCertificateToIISPort { # Reference: https://weblog.west-wind.com/posts/2016/Jun/23/Use-Powershell-to-bind-SSL-Certificates-to-an-IIS-Host-Header-Site#BindtheWebSitetotheHostHeaderIP <# .SYNOPSIS Bind a certificate to an IIS port .DESCRIPTION Bind a certificate to an IIS port .PARAMETER URL [Mandatory] This parameter contains the URL (e.g. apps.myurl.com) of the certificate If this parameter contains the prefix 'http://' or 'https://' or any suffixes, these are automatically deleted .PARAMETER Port [Optional] This parameter contains the port of the IIS site to which the certificate should be bound (e.g. 443) If this parameter is omitted, the value 443 is used .EXAMPLE DS_BindCertificateToIISSite -URL "myurl.com" -Port 443 Binds the certificate containing the URL 'myurl.com' to port 443. The function automatically determines the hash value of the certificate .EXAMPLE DS_BindCertificateToIISSite -URL "anotherurl.com" -Port 12345 Binds the certificate containing the URL 'anotherurl' to port 12345. The function automatically determines the hash value of the certificate #> [CmdletBinding()] param ( [Parameter(Mandatory=$True)] [string]$URL, [Parameter(Mandatory=$True)] [int]$Port = 443 ) begin { [string]$FunctionName = $PSCmdlet.MyInvocation.MyCommand.Name DS_WriteLog "I" "START FUNCTION - $FunctionName" $LogFile } process { # Import the PowerShell module 'WebAdministration' for IIS try { Import-Module WebAdministration } catch { DS_WriteLog "E" "An error occurred trying to import the PowerShell module 'WebAdministration' for IIS (error: $($Error[0]))" $LogFile Exit 1 } # Retrieve the domain name of the host base URL if ( $URL.StartsWith("http") ) { [string]$Domain = ([System.URI]$URL).host # Retrieve the domain name from the URL. For example: if the host base URL is "http://apps.mydomain.com/folder/", using the data type [System.URI] and the property "host", the resulting value would be "www.mydomain.com" } else { [string]$Domain = $URL # Retrieve the domain name from the URL. For example: if the host base URL is "http://apps.mydomain.com/folder/", using the data type [System.URI] and the property "host", the resulting value would be "www.mydomain.com" } # Retrieve the certificate hash value DS_WriteLog "I" "Retrieve the hash value of the certificate for the host base URL '$URL' (check for the domain name '$Domain')" $LogFile try { If ( $Domain.StartsWith("*") ) { $Hash = (Get-ChildItem cert:\LocalMachine\My | where-object { $_.Subject -match "\*.$($Domain)" } | Select-Object -First 1).Thumbprint } else { $Hash = (Get-ChildItem cert:\LocalMachine\My | where-object { $_.Subject -like "*$Domain*" } | Select-Object -First 1).Thumbprint } if ( !($Hash) ) { DS_WriteLog "I" "The hash value could not be retrieved. Check if a wildcard or SAN certificate is installed." $LogFile [string[]]$Domain = (([System.URI]$URL).host).Split(".") # Retrieve the domain name from the URL (e.g. apps.mydomain.com) and split it based on the dot (.) [string]$Domain = "$($Domain[-2]).$($Domain[-1])" # Read the last two items in the newly created array to retrieve the root level domain name (in our example this would be "mydomain.com") DS_WriteLog "I" "Retrieve the hash value of the certificate for the host base URL '$URL' (check for the domain name '*.$($Domain)')" $LogFile $Hash = (Get-ChildItem cert:\LocalMachine\My | where-object { $_.Subject -match "\*.$($Domain)" } | Select-Object -First 1).Thumbprint if ( !($Hash) ) { DS_WriteLog "E" "The hash value could not be retrieved. The most likely cause is that the certificate for the host base URL '$URL' is not installed." $LogFile Exit 1 } else { DS_WriteLog "S" "The hash value of the certificate for the host base URL '$URL' is $Hash" $LogFile } } else { DS_WriteLog "S" "The hash value of the certificate for the host base URL '$URL' is $Hash" $LogFile } } catch { DS_WriteLog "E" "An error occurred trying to retrieve the certificate hash value (error: $($Error[0]))" $LogFile Exit 1 } # Bind the certificate to the IIS site DS_WriteLog "I" "Bind the certificate to the IIS site" $LogFile try { Get-Item iis:\sslbindings\* | where { $_.Port -eq $Port } | Remove-Item Get-Item "cert:\LocalMachine\MY\$Hash" | New-Item "iis:\sslbindings\0.0.0.0!$Port" DS_WriteLog "S" "The certificate with hash $Hash was successfully bound to port $Port" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to bind the certificate with hash $Hash to port $Port (error: $($Error[0]))" $LogFile Exit 1 } } end { DS_WriteLog "I" "END FUNCTION - $FunctionName" $LogFile } } #========================================================================== # Function DS_CreateStoreFrontStore #========================================================================== # Note: this function is based on the example script "SimpleDeployment.ps1" located in the following StoreFront installation subdirectory: "C:\Program Files\Citrix\Receiver StoreFront\PowerShellSDK\Examples". Function DS_CreateStoreFrontStore { <# .SYNOPSIS Creates a single-site or multi-site StoreFront deployment, stores, farms and the Authentication, Receiver for Web and PNAgent services .DESCRIPTION Creates a single-site or multi-site StoreFront deployment, stores, farms and the Authentication, Receiver for Web and PNAgent services .PARAMETER FriendlyName [Optional] This parameter configures the friendly name of the store, for example "MyStore" or "Marketing" If this parameter is omitted, the script generates the friendly name automatically based on the farm name. For example, if the farm name is "MyFarm", the friendly name would be "Store - MyFarm" .PARAMETER HostBaseUrl [Mandatory] This parameter determines the URL of the IIS site (the StoreFront "deployment"), for example "https://mysite.com" or "http://mysite.com" .PARAMETER CertSubjectName [Optional] This parameter determines the Certificate Subject Name of the certificate you want to bind to the IIS SSL port on the local StoreFront server Possible values are: -Local machine name: $($env:ComputerName).mydomain.local -Wilcard / Subject Alternative Name (SAN) certificate: *.mydomain.local or portal.mydomain.local If this parameter is omitted, the Certificate Subject Name will be automatically extracted from the host base URL. .PARAMETER AddHostHeaderToIISSiteBinding [Optional] This parameter determines whether the host base URl (the host name) is added to IIS site binding If this parameter is omitted, the value is set to '$false' and the host base URl (the host name) is NOT added to IIS site binding .PARAMETER IISSiteDir [Optional] This parameter contains the directory path to the IIS site. This parameter is only used in multiple deployment configurations whereby multiple IIS sites are created. If this parameter is omitted, a directory will be automatically generated by the script .PARAMETER Farmtype [Optional] This parameter determines the farm type. Possible values are: XenDesktop | XenApp | AppController | VDIinaBox. If this parameter is omitted, the default value "XenDesktop" is used .PARAMETER FarmName [Mandatory] This parameter contains the name of the farm within the store. The farm name should be unique within a store. .PARAMETER FarmServers [Mandatory] This parameter, which data type is an array, contains a list of farm servers (XML brokers or Delivery Controller). Enter the list comma separated (e.g. -FarmServers "Server1","Server2","Server3") .PARAMETER StoreVirtualPath [Optional] This parameter contains the partial path of the StoreFront store, for example: -StoreVirtualPath "/Citrix/MyStore" or -StoreVirtualPath "/Citrix/Store1". If this parameter is omitted, the default value "/Citrix/Store" is used .PARAMETER ReceiverVirtualPath [Optional] This parameter contains the partial path of the Receiver for Web site in the StoreFront store, for example: -ReceiverVirtualPath "/Citrix/MyStoreWeb" or -ReceiverVirtualPath "/Citrix/Store1ReceiverWeb". If this parameter is omitted, the default value "/Citrix/StoreWeb" is used .PARAMETER SSLRelayPort [Mandatory] This parameter contains the SSL Relay port (XenApp 6.5 only) used for communicating with the XenApp servers. Default value is 443 (HTTPS). .PARAMETER LoadBalanceServers [Optional] This parameter determines whether to load balance the Delivery Controllers or to use them in failover order (if specifying more than one server) If this parameter is omitted, the default value "$false" is used, which means that failover is used instead of load balancing .PARAMETER XMLPort [Optional] This parameter contains the XML service port used for communicating with the XenApp\XenDesktop servers. Default values are 80 (HTTP) and 443 (HTTPS), but you can also use other ports (depending on how you configured your XenApp/XenDesktop servers). If this parameter is omitted, the default value 80 is used .PARAMETER HTTPPort [Optional] This parameter contains the port used for HTTP communication on the IIS site. The default value is 80, but you can also use other ports. If this parameter is omitted, the default value 80 is used. .PARAMETER HTTPSPort [Optional] This parameter contains the port used for HTTPS communication on the IIS site. If this value is not set, no HTTPS binding is created .PARAMETER TransportType [Optional] This parameter contains the type of transport to use for the XML service communication. Possible values are: HTTP | HTTPS | SSL If this parameter is omitted, the default value "HTTP" is used .PARAMETER EnablePNAgent [Mandatory] This parameter determines whether the PNAgent site is created and enabled. Possible values are: $True | $False If this parameter is omitted, the default value "$true" is used .PARAMETER PNAgentAllowUserPwdChange [Optional] This parameter determines whether the user is allowed to change their password on a PNAgent. Possible values are: $True | $False. Note: this parameter can only be used if the logon method for PNAgent is set to 'prompt' !! Only add this parameter when the parameter EnablePNAgent is set to $True If this parameter is omitted, the default value "$true" is used .PARAMETER PNAgentDefaultService [Optional] This parameter determines whether this PNAgent site is the default PNAgent site in the store. Possible values are: $True | $False. !! Only add this parameter when the parameter EnablePNAgent is set to $True If this parameter is omitted, the default value "$true" is used .PARAMETER LogonMethod [Optional] This parameter determines the logon method for the PNAgent site. Possible values are: Anonymous | Prompt | SSON | Smartcard_SSON | Smartcard_Prompt. Only one value can be used at a time. !! Only add this parameter when the parameter EnablePNAgent is set to $True If this parameter is omitted, the default value "SSON" (Single Sign-On) is used .EXAMPLE DS_CreateStoreFrontStore -FriendlyName "MyStore" -HostBaseUrl "https://myurl.com" -FarmName "MyFarm" -FarmServers "Server1","Server2" -EnablePNAgent $True Creates a basic StoreFront deployment, a XenDesktop store, farm, Receiver for Web site and a PNAgent site. The communication with the Delivery Controllers uses the default XML port 80 and transport type HTTP The above example uses only the 4 mandatory parameters and does not include any of the optional parameters In case the HostBaseUrl is different than an already configured one on the local StoreFront server, a new IIS site is automatically created .EXAMPLE DS_CreateStoreFrontStore -HostBaseUrl "https://myurl.com" -FarmName "MyFarm" -FarmServers "Server1","Server2" -StoreVirtualPath "/Citrix/MyStore" -ReceiverVirtualPath "/Citrix/MyStoreWeb" -XMLPort 443 -TransportType "HTTPS" -EnablePNAgent $True -PNAgentAllowUserPwdChange $False -PNAgentDefaultService $True -LogonMethod "Prompt" Creates a StoreFront deployment, a XenDesktop store, farm, Receiver for Web site and a PNAgent site. The communication with the Delivery Controllers uses port 443 and transport type HTTPS (please make sure that your Delivery Controller is listening on port 443!) PNAgent users are not allowed to change their passwords and the logon method is "prompt" instead of the default SSON (= Single Sign-On). In case the HostBaseUrl is different than an already configured one on the local StoreFront server, a new IIS site is automatically created .EXAMPLE DS_CreateStoreFrontStore -HostBaseUrl "https://anotherurl.com" -FarmName "MyFarm" -FarmServers "Server1","Server2" -EnablePNAgent $False Creates a StoreFront deployment, a XenDesktop store, farm and Receiver for Web site, but DOES NOT create and enabled a PNAgent site. The communication with the Delivery Controllers uses the default XML port 80 and transport type HTTP. In case the HostBaseUrl is different than an already configured one on the local StoreFront server, a new IIS site is automatically created #> [CmdletBinding()] param ( [Parameter(Mandatory=$false)] [string]$FriendlyName, [Parameter(Mandatory=$true)] [string]$HostBaseUrl, [Parameter(Mandatory=$false)] [string]$CertSubjectName, [Parameter(Mandatory=$false)] [string]$AddHostHeaderToIISSiteBinding = $false, [Parameter(Mandatory=$false)] [string]$IISSiteDir, [Parameter(Mandatory=$false)] [ValidateSet("XenDesktop","XenApp","AppController","VDIinaBox")] [string]$FarmType = "XenDesktop", [Parameter(Mandatory=$true)] [string]$FarmName, [Parameter(Mandatory=$true)] [string[]]$FarmServers, [Parameter(Mandatory=$false)] [string]$StoreVirtualPath = "/Citrix/Store", [Parameter(Mandatory=$false)] [string]$ReceiverVirtualPath = "/Citrix/StoreWeb", [Parameter(Mandatory=$false)] [int]$SSLRelayPort, [Parameter(Mandatory=$false)] [bool]$LoadBalanceServers = $false, [Parameter(Mandatory=$false)] [int]$XMLPort = 80, [Parameter(Mandatory=$false)] [int]$HTTPPort = 80, [Parameter(Mandatory=$false)] [int]$HTTPSPort, [Parameter(Mandatory=$false)] [ValidateSet("HTTP","HTTPS","SSL")] [string]$TransportType = "HTTP", [Parameter(Mandatory=$true)] [bool]$EnablePNAgent = $true, [Parameter(Mandatory=$false)] [bool]$PNAgentAllowUserPwdChange = $true, [Parameter(Mandatory=$false)] [bool]$PNAgentDefaultService = $true, [Parameter(Mandatory=$false)] [ValidateSet("Anonymous","Prompt","SSON","Smartcard_SSON","Smartcard_Prompt")] [string]$LogonMethod = "SSON" ) begin { [string]$FunctionName = $PSCmdlet.MyInvocation.MyCommand.Name DS_WriteLog "I" "START FUNCTION - $FunctionName" $LogFile } process { # Import StoreFront modules. Required for versions of PowerShell earlier than 3.0 that do not support autoloading. DS_WriteLog "I" "Import StoreFront PowerShell modules:" $LogFile [string[]] $Modules = "WebAdministration","Citrix.StoreFront","Citrix.StoreFront.Stores","Citrix.StoreFront.Authentication","Citrix.StoreFront.WebReceiver" # Four StoreFront modules are required in this function. They are listed here in an array and in the following 'foreach' statement each of these four is loaded Foreach ( $Module in $Modules) { try { DS_WriteLog "I" " -Import the StoreFront PowerShell module $Module" $LogFile Import-Module $Module DS_WriteLog "S" " The StoreFront PowerShell module $Module was imported successfully" $LogFile } catch { DS_WriteLog "E" " An error occurred trying to import the StoreFront PowerShell module $Module (error: $($Error[0]))" $LogFile Exit 1 } } DS_WriteLog "-" "" $LogFile # Modify variables and/or create new ones for logging [int]$SiteId = 1 [string]$HostName = ([System.URI]$HostBaseUrl).host # Get the hostname (required for the IIS site) without prefixes such as "http://" or "https:// and no suffixes such as trailing slashes (/). The contents of the variable '$HostBaseURL' will look like this: portal.mydomain.com [string]$HostBaseUrl = "$(([System.URI]$HostBaseUrl).scheme)://$(([System.URI]$HostBaseUrl).host)" # Retrieve the 'clean' URL (e.g. in case the host base URL contains trailing slashes or more). The contents of the variable '$HostBaseURL' will look like this: https://portal.mydomain.com. If ( !($CertSubjectName) ) { $CertSubjectNameTemp = "<use the host base URL>" } else { $CertSubjectNameTemp = $CertSubjectName } If ( $CertSubjectName.StartsWith("*") ) { $CertSubjectName.Replace("*", "\*") | Out-Null } # In case the certificate subject name starts with a *, place a backslash in front of it (this is required for the regular e If ( $AddHostHeaderToIISSiteBinding -eq $true ) { $AddHostHeaderToIISSiteBindingTemp = "yes" } If ( $AddHostHeaderToIISSiteBinding -eq $false ) { $AddHostHeaderToIISSiteBindingTemp = "no" } [string]$IISSiteDirTemp = $IISSiteDir If ( [string]::IsNullOrEmpty($FriendlyName) ) { $FriendlyName = "Store - $($FarmName)" } If ( [string]::IsNullOrEmpty($IISSiteDir) ) { $IISSiteDirTemp = "Default: $env:SystemDrive\inetpub\wwwroot" } If ( $LoadBalanceServers -eq $true ) { $LoadBalanceServersTemp = "yes" } If ( $LoadBalanceServers -eq $false ) { $LoadBalanceServersTemp = "no (fail-over)" } If ( !($HTTPSPort) ) { $HTTPSPortTemp = "<no IIS HTTPS/SSL>" } else { $HTTPSPortTemp = $HTTPSPort} If ( $EnablePNAgent -eq $true ) { $EnablePNAgentTemp = "yes" } If ( $EnablePNAgent -eq $false ) { $EnablePNAgentTemp = "no" } If ( $PNAgentAllowUserPwdChange -eq $true ) { $PNAgentAllowUserPwdChangeTemp = "yes" } If ( $PNAgentAllowUserPwdChange -eq $false ) { $PNAgentAllowUserPwdChangeTemp = "no" } If ( $PNAgentDefaultService -eq $true ) { $PNAgentDefaultServiceTemp = "yes" } If ( $PNAgentDefaultService -eq $false ) { $PNAgentDefaultServiceTemp = "no" } # Start logging DS_WriteLog "I" "Create the StoreFront store with the following parameters:" $LogFile DS_WriteLog "I" " -Friendly name : $FriendlyName" $LogFile DS_WriteLog "I" " -Host base URL : $HostBaseUrl" $LogFile DS_WriteLog "I" " -Certificate subject name : $CertSubjectNameTemp" $LogFile DS_WriteLog "I" " -Add host name to IIS site binding : $AddHostHeaderToIISSiteBindingTemp" $LogFile DS_WriteLog "I" " -IIS site directory : $IISSiteDirTemp" $LogFile DS_WriteLog "I" " -Farm type : $FarmType" $LogFile DS_WriteLog "I" " -Farm name : $FarmName" $LogFile DS_WriteLog "I" " -Farm servers : $FarmServers" $LogFile DS_WriteLog "I" " -Store virtual path : $StoreVirtualPath" $LogFile DS_WriteLog "I" " -Receiver virtual path : $receiverVirtualPath" $LogFile If ( $FarmType -eq "XenApp" ) { DS_WriteLog "I" " -SSL relay port (XenApp 6.5 only) : $SSLRelayPort" $LogFile } DS_WriteLog "I" " -Load Balancing : $LoadBalanceServersTemp" $LogFile DS_WriteLog "I" " -XML Port : $XMLPort" $LogFile DS_WriteLog "I" " -HTTP Port : $HTTPPort" $LogFile DS_WriteLog "I" " -HTTPS Port : $HTTPSPortTemp" $LogFile DS_WriteLog "I" " -Transport type : $TransportType" $LogFile DS_WriteLog "I" " -Enable PNAgent : $EnablePNAgentTemp" $LogFile DS_WriteLog "I" " -PNAgent allow user change password : $PNAgentAllowUserPwdChangeTemp" $LogFile DS_WriteLog "I" " -PNAgent set to default : $PNAgentDefaultServiceTemp" $LogFile DS_WriteLog "I" " -PNAgent logon method : $LogonMethod" $LogFile DS_WriteLog "-" "" $LogFile # Check if the parameters match If ( ( $TransportType -eq "HTTPS" ) -And ( $XMLPort -eq 80 ) ) { DS_WriteLog "W" "The transport type is set to HTTPS, but the XML port was set to 80. Changing the port to 443" $LogFile Exit 0 } If ( ( $TransportType -eq "HTTP" ) -And ( $XMLPort -eq 443 ) ) { DS_WriteLog "W" "The transport type is set to HTTP, but the XML port was set to 443. Changing the port to 80" $LogFile Exit 0 } ############################################################################# # Create a new deployment with the host base URL set in the variable $HostBaseUrl ############################################################################# DS_WriteLog "I" "Create a new StoreFront deployment (URL: $($HostBaseUrl)) unless one already exists" $LogFile # Port bindings If ( !($HTTPSPort) ) { if ( $AddHostHeaderToIISSiteBinding -eq $false ) { $Bindings = @( @{protocol="http";bindingInformation="*:$($HTTPPort):"} ) } else { $Bindings = @( @{protocol="http";bindingInformation="*:$($HTTPPort):$($HostName)"} ) } } else { if ( $AddHostHeaderToIISSiteBinding -eq $false ) { $Bindings = @( @{protocol="http";bindingInformation="*:$($HTTPPort):"}, @{protocol="https";bindingInformation="*:$($HTTPSPort):"} ) } else { $Bindings = @( @{protocol="http";bindingInformation="*:$($HTTPPort):$($HostName)"}, @{protocol="https";bindingInformation="*:$($HTTPSPort):$($HostName)"} ) } } # Determine if the deployment already exists $ExistingDeployments = Get-STFDeployment if( !($ExistingDeployments) ) { DS_WriteLog "I" "No StoreFront deployment exists. Prepare IIS for the first deployment" $LogFile # Delete the bindings on the Default Web Site DS_WriteLog "I" "Delete the bindings on the Default Web Site" $LogFile try { Clear-ItemProperty "IIS:\Sites\Default Web Site" -Name bindings DS_WriteLog "S" "The bindings on the Default Web Site have been successfully deleted" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to delte the bindings on the Default Web Site (error: $($Error[0]))" $LogFile Exit 1 } # Create the bindings on the Default Web Site DS_WriteLog "I" "Create the bindings on the Default Web Site" $LogFile try { Set-ItemProperty "IIS:\Sites\Default Web Site" -Name bindings -Value $Bindings DS_WriteLog "S" "The bindings on the Default Web Site have been successfully created" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to create the bindings the Default Web Site (error: $($Error[0]))" $LogFile Exit 1 } DS_WriteLog "-" "" $LogFile # Bind the certificate to the IIS Site (only when an HTTPS port has been defined in the variable $HTTPSPort) If ( $HTTPSPort ) { if ( !($CertSubjectName) ) { DS_BindCertificateToIISPort -URL $HostBaseUrl -Port $HTTPSPort } else { DS_BindCertificateToIISPort -URL $CertSubjectName -Port $HTTPSPort } DS_WriteLog "-" "" $LogFile } # Create the first StoreFront deployment DS_WriteLog "I" "Create the first StoreFront deployment (this may take a couple of minutes)" $LogFile try { Add-STFDeployment -HostBaseUrl $HostbaseUrl -SiteId 1 -Confirm:$false # Create the first deployment on this server using the IIS default website (= site ID 1) DS_WriteLog "S" "The StoreFront deployment '$HostbaseUrl' on IIS site ID $SiteId has been successfully created" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to create the StoreFront deployment '$HostBaseUrl' (error: $($Error[0]))" $LogFile Exit 1 } } else { # One or more deployments exists $ExistingDeploymentFound = $False Foreach ( $Deployment in $ExistingDeployments ) { # Loop through each deployment and check if the URL matches the one defined in the variable $HostbaseUrl if ($Deployment.HostbaseUrl -eq $HostBaseUrl) { # The deployment URL is the same as the one we want to add, so nothing to do $SiteId = $Deployment.SiteId # Set the value of the variable $SiteId to the correct IIS site ID $ExistingDeploymentFound = $True # The deployment exists and it is configured to the desired hostbase URL DS_WriteLog "I" "A deployment has already been created with the hostbase URL '$HostBaseUrl' on this server and will be used (IIS site ID is $SiteId)" $LogFile } } # Create a new IIS site and StoreFront deployment in case existing deployments were found, but none matching the hostbase URL defined in the variable $HostbaseUrl If ( $ExistingDeploymentFound -eq $False ) { DS_WriteLog "I" "One or more deployments exist on this server, but all with a different host base URL" $LogFile DS_WriteLog "I" "A new IIS site will now be created which will host the host base URL '$HostBaseUrl' defined in variable `$HostbaseUrl" $LogFile # Generate a random, new directory for the new IIS site in case the directory was not specified in the variable $IISSiteDir If ( [string]::IsNullOrEmpty($IISSiteDir) ) { # In case no directory for the new IIS site was specified in the variable $IISSiteDir, a new, random one must be created DS_WriteLog "I" "No directory for the new IIS site was defined in variable `$HostbaseUrl. A new one will now be generated" $LogFile DS_WriteLog "I" "Retrieve the list of existing IIS sites, identify the highest site ID, add 1 and use this number to generate a new IIS directory" $LogFile $NewIISSiteNumber = ((((Get-ChildItem -Path IIS:\Sites).ID) | measure -Maximum).Maximum)+1 # Retrieve all existing IIS sites ('Get-ChildItem -Path IIS:\Sites'); list only one property, namely the site ID (ID); than use the Measure-Object (measure -Maximum) to get the highest site ID and add 1 to determine the new site ID DS_WriteLog "S" "The new site ID is: $NewIISSiteNumber" $LogFile $IISSiteDir = "$env:SystemDrive\inetpub\wwwroot$($NewIISSiteNumber)" # Set the directory for the new IIS site to "C:\inetpub\wwwroot#\" (# = the number of the new site ID retrieved in the previous line), for example "C:\inetpub\wwwroot2" } DS_WriteLog "I" "The directory for the new IIS site is: $IISSiteDir" $LogFile # Create the directory for the new IIS site DS_WriteLog "I" "Create the directory for the new IIS site" $LogFile DS_CreateDirectory -Directory $IISSiteDir # Create the new IIS site DS_WriteLog "I" "Create the new IIS site" $LogFile try { New-Item "iis:\Sites\$($FarmName)" -bindings $Bindings -physicalPath $IISSiteDir DS_WriteLog "S" "The new IIS site for the URL '$HostbaseUrl' with site ID $SiteId has been successfully created" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to create the IIS site for the URL '$HostbaseUrl' with site ID $SiteId (error: $($Error[0]))" $LogFile Exit 1 } # Retrieve the site ID of the new site DS_WriteLog "I" "Retrieve the site ID of the new site" $LogFile $SiteId = (Get-ChildItem -Path IIS:\Sites | Where-Object { $_.Name -like "*$FarmName*" }).ID DS_WriteLog "S" "The new site ID is: $SiteId" $LogFile DS_WriteLog "-" "" $LogFile # Bind the certificate to the IIS Site (only when an HTTPS port has been defined in the variable $HTTPSPort) If ( $HTTPSPort ) { if ( !($CertSubjectName) ) { DS_BindCertificateToIISPort -URL $HostBaseUrl -Port $HTTPSPort } else { DS_BindCertificateToIISPort -URL $CertSubjectName -Port $HTTPSPort } DS_WriteLog "-" "" $LogFile } # Create the StoreFront deployment try { Add-STFDeployment -HostBaseUrl $HostBaseUrl -SiteId $SiteId -Confirm:$false # Create a new deployment on this server using the new IIS default website (= site ID #) DS_WriteLog "S" "The StoreFront deployment '$HostBaseUrl' on IIS site ID $SiteId has been successfully created" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to create the StoreFront deployment '$HostBaseUrl' (error: $($Error[0]))" $LogFile Exit 1 } } } DS_WriteLog "-" "" $LogFile ############################################################################# # Determine the Authentication and Receiver virtual path to use based on the virtual path of the store defined in the variable '$StoreVirtualPath' # The variable '$StoreVirtualPath' is not mandatory. In case it is not defined, the default value '/Citrix/Store' is used. ############################################################################# DS_WriteLog "I" "Set the virtual path for Authentication and Receiver:" $LogFile $authenticationVirtualPath = "$($StoreVirtualPath.TrimEnd('/'))Auth" DS_WriteLog "I" " -Authentication virtual path is: $authenticationVirtualPath" $LogFile If ( [string]::IsNullOrEmpty($ReceiverVirtualPath) ) { # In case no directory for the Receiver for Web site was specified in the variable $ReceiverVirtualPath, a directory name will be automatically generated $receiverVirtualPath = "$($StoreVirtualPath.TrimEnd('/'))Web" } DS_WriteLog "I" " -Receiver virtual path is: $receiverVirtualPath" $LogFile DS_WriteLog "-" "" $LogFile ############################################################################# # Determine if the authentication service at the specified virtual path in the specific IIS site exists ############################################################################# DS_WriteLog "I" "Determine if the authentication service at the path $authenticationVirtualPath in the IIS site $SiteId exists" $LogFile $authentication = Get-STFAuthenticationService -siteID $SiteId -VirtualPath $authenticationVirtualPath if ( !($authentication) ) { DS_WriteLog "I" "No authentication service exists at the path $authenticationVirtualPath in the IIS site $SiteId" $LogFile # Add an authentication service using the IIS path of the store appended with Auth DS_WriteLog "I" "Add the authentication service at the path $authenticationVirtualPath in the IIS site $SiteId" $LogFile try { $authentication = Add-STFAuthenticationService -siteID $SiteId -VirtualPath $authenticationVirtualPath DS_WriteLog "S" "The authentication service at the path $authenticationVirtualPath in the IIS site $SiteId was created successfully" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to create the authentication service at the path $authenticationVirtualPath in the IIS site $SiteId (error: $($Error[0]))" $LogFile Exit 1 } } else { DS_WriteLog "I" "An authentication service already exists at the path $authenticationVirtualPath in the IIS site $SiteID and will be used" $LogFile } DS_WriteLog "-" "" $LogFile ############################################################################# # Create store and farm ############################################################################# DS_WriteLog "I" "Determine if the store service at the path $StoreVirtualPath in the IIS site $SiteId exists" $LogFile $store = Get-STFStoreService -siteID $SiteId -VirtualPath $StoreVirtualPath if ( !($store) ) { DS_WriteLog "I" "No store service exists at the path $StoreVirtualPath in the IIS site $SiteId" $LogFile DS_WriteLog "I" "Add a store that uses the new authentication service configured to publish resources from the supplied servers" $LogFile # Add a store that uses the new authentication service configured to publish resources from the supplied servers try { #If ( $FarmType -eq "XenApp" ) { $store = Add-STFStoreService -FriendlyName $FriendlyName -siteID $SiteId -VirtualPath $StoreVirtualPath -AuthenticationService $authentication -FarmName $FarmName -FarmType $FarmType -Servers $FarmServers -SSLRelayPort $SSLRelayPort -LoadBalance $LoadbalanceServers -Port $XMLPort -TransportType $TransportType #} else { # $store = Add-STFStoreService -FriendlyName $FriendlyName -siteID $SiteId -VirtualPath $StoreVirtualPath -AuthenticationService $authentication -FarmName $FarmName -FarmType $FarmType -Servers $FarmServers -LoadBalance $LoadbalanceServers -Port $XMLPort -TransportType $TransportType #} DS_WriteLog "S" "The store service with the following configuration was created successfully:" $LogFile DS_WriteLog "I" " -FriendlyName : $FriendlyName" $LogFile DS_WriteLog "I" " -siteId : $SiteId" $LogFile DS_WriteLog "I" " -VirtualPath : $StoreVirtualPath" $LogFile DS_WriteLog "I" " -AuthService : $authenticationVirtualPath" $LogFile DS_WriteLog "I" " -FarmName : $FarmName" $LogFile DS_WriteLog "I" " -FarmType : $FarmType" $LogFile DS_WriteLog "I" " -Servers : $FarmServers" $LogFile If ( $FarmType -eq "XenApp" ) { DS_WriteLog "I" " -SSL relay port (XenApp 6.5 only) : $SSLRelayPort" $LogFile } DS_WriteLog "I" " -LoadBalance : $LoadBalanceServersTemp" $LogFile DS_WriteLog "I" " -XML Port : $XMLPort" $LogFile DS_WriteLog "I" " -TransportType: $TransportType" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to create the store service with the following configuration (error: $($Error[0])):" $LogFile DS_WriteLog "I" " -FriendlyName : $FriendlyName" $LogFile DS_WriteLog "I" " -siteId : $SiteId" $LogFile DS_WriteLog "I" " -VirtualPath : $StoreVirtualPath" $LogFile DS_WriteLog "I" " -AuthService : $authenticationVirtualPath" $LogFile DS_WriteLog "I" " -FarmName : $FarmName" $LogFile DS_WriteLog "I" " -FarmType : $FarmType" $LogFile DS_WriteLog "I" " -Servers : $FarmServers" $LogFile If ( $FarmType -eq "XenApp" ) { DS_WriteLog "I" " -SSL relay port (XenApp 6.5 only) : $SSLRelayPort" $LogFile } DS_WriteLog "I" " -LoadBalance : $LoadBalanceServersTemp" $LogFile DS_WriteLog "I" " -XML Port : $XMLPort" $LogFile DS_WriteLog "I" " -TransportType: $TransportType" $LogFile Exit 1 } } else { # During the creation of the store at least one farm is defined, so there must at the very least be one farm present in the store DS_WriteLog "I" "A store service called $($Store.Name) already exists at the path $StoreVirtualPath in the IIS site $SiteId" $LogFile DS_WriteLog "I" "Retrieve the available farms in the store $($Store.Name)." $LogFile $ExistingFarms = (Get-STFStoreFarmConfiguration $Store).Farms.FarmName $TotalFarmsFound = $ExistingFarms.Count DS_WriteLog "I" "Total farms found: $TotalFarmsFound" $LogFile Foreach ( $Farm in $ExistingFarms ) { DS_WriteLog "I" " -Farm name: $Farm" $LogFile } # Loop through each farm, check if the farm name is the same as the one defined in the variable $FarmName. If not, create/add a new farm to the store $ExistingFarmFound = $False DS_WriteLog "I" "Check if the farm $FarmName already exists" $LogFile Foreach ( $Farm in $ExistingFarms ) { if ( $Farm -eq $FarmName ) { $ExistingFarmFound = $True # The farm exists. Nothing to do. This script will now end. DS_WriteLog "I" "The farm $FarmName exists" $LogFile } } # Create a new farm in case existing farms were found, but none matching the farm name defined in the variable $HostbaseUrl If ( $ExistingFarmFound -eq $False ) { DS_WriteLog "I" "The farm $FarmName does not exist" $LogFile DS_WriteLog "I" "Create the new farm $FarmName" $LogFile # Create the new farm try { Add-STFStoreFarm -StoreService $store -FarmName $FarmName -FarmType $FarmType -Servers $FarmServers -SSLRelayPort $SSLRelayPort -LoadBalance $LoadBalanceServers -Port $XMLPort -TransportType $TransportType DS_WriteLog "S" "The farm $FarmName with the following configuration was created successfully:" $LogFile DS_WriteLog "I" " -siteId : $SiteId" $LogFile DS_WriteLog "I" " -FarmName : $FarmName" $LogFile DS_WriteLog "I" " -FarmType : $FarmType" $LogFile DS_WriteLog "I" " -Servers : $FarmServers" $LogFile If ( $FarmType -eq "XenApp" ) { DS_WriteLog "I" " -SSL relay port (XenApp 6.5 only) : $SSLRelayPort" $LogFile } DS_WriteLog "I" " -LoadBalance : $LoadBalanceServersTemp" $LogFile DS_WriteLog "I" " -XML Port : $XMLPort" $LogFile DS_WriteLog "I" " -TransportType: $TransportType" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to create the farm $FarmName with the following configuration (error: $($Error[0])):" $LogFile DS_WriteLog "I" " -siteId : $SiteId" $LogFile DS_WriteLog "I" " -FarmName : $FarmName" $LogFile DS_WriteLog "I" " -FarmType : $FarmType" $LogFile DS_WriteLog "I" " -Servers : $FarmServers" $LogFile If ( $FarmType -eq "XenApp" ) { DS_WriteLog "I" " -SSL relay port (XenApp 6.5 only) : $SSLRelayPort" $LogFile } DS_WriteLog "I" " -LoadBalance : $LoadBalanceServersTemp" $LogFile DS_WriteLog "I" " -XML Port : $XMLPort" $LogFile DS_WriteLog "I" " -TransportType: $TransportType" $LogFile Exit 1 } } } DS_WriteLog "-" "" $LogFile ############################################################################################## # Determine if the Receiver for Web service at the specified virtual path and IIS site exists ############################################################################################## DS_WriteLog "I" "Determine if the Receiver for Web service at the path $receiverVirtualPath in the IIS site $SiteId exists" $LogFile try { $receiver = Get-STFWebReceiverService -siteID $SiteID -VirtualPath $receiverVirtualPath } catch { DS_WriteLog "E" "An error occurred trying to determine if the Receiver for Web service at the path $receiverVirtualPath in the IIS site $SiteId exists (error: $($Error[0]))" $LogFil Exit 1 } # Create the receiver server if it does not exist if ( !($receiver) ) { DS_WriteLog "I" "No Receiver for Web service exists at the path $receiverVirtualPath in the IIS site $SiteId" $LogFile DS_WriteLog "I" "Add the Receiver for Web service at the path $receiverVirtualPath in the IIS site $SiteId" $LogFile # Add a Receiver for Web site so users can access the applications and desktops in the published in the Store try { $receiver = Add-STFWebReceiverService -siteID $SiteId -VirtualPath $receiverVirtualPath -StoreService $Store DS_WriteLog "S" "The Receiver for Web service at the path $receiverVirtualPath in the IIS site $SiteId was created successfully" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to create the Receiver for Web service at the path $receiverVirtualPath in the IIS site $SiteId (error: $($Error[0]))" $LogFile Exit 1 } } else { DS_WriteLog "I" "A Receiver for Web service already exists at the path $receiverVirtualPath in the IIS site $SiteId" $LogFile } DS_WriteLog "-" "" $LogFile ############################################################################################## # Determine if the PNAgent service at the specified virtual path and IIS site exists ############################################################################################## $StoreName = $Store.Name DS_WriteLog "I" "Determine if the PNAgent on the store '$StoreName' in the IIS site $SiteId is enabled" $LogFile try { $storePnaSettings = Get-STFStorePna -StoreService $Store } catch { DS_WriteLog "E" "An error occurred trying to determine if the PNAgent on the store '$StoreName' is enabled" $LogFil Exit 1 } # Enable the PNAgent if required if ( $EnablePNAgent -eq $True ) { if ( !($storePnaSettings.PnaEnabled) ) { DS_WriteLog "I" "The PNAgent is not enabled on the store '$StoreName'" $LogFile DS_WriteLog "I" "Enable the PNAgent on the store '$StoreName'" $LogFile # Check for the following potential error: AllowUserPasswordChange is only compatible with logon method 'Prompt' authentication if ( ($PNAgentAllowUserPwdChange -eq $True) -and ( !($LogonMethod -eq "Prompt")) ) { DS_WriteLog "I" "Important: AllowUserPasswordChange is only compatible with LogonMethod Prompt authentication" $LogFile DS_WriteLog "I" " The logon method is set to $LogonMethod, therefore AllowUserPasswordChange has been set to '`$false'" $LogFile $PNAgentAllowUserPwdChange = $False } # Enable the PNAgent if ( ($PNAgentAllowUserPwdChange -eq $True) -and ($PNAgentDefaultService -eq $True) ) { try { Enable-STFStorePna -StoreService $store -AllowUserPasswordChange -DefaultPnaService -LogonMethod $LogonMethod DS_WriteLog "S" "The PNAgent was enabled successfully on the store '$StoreName'" $LogFile DS_WriteLog "S" " -Allow user change password: yes" $LogFile DS_WriteLog "S" " -Default PNAgent service: yes" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to enable the PNAgent on the store '$StoreName' (error: $($Error[0]))" $LogFile Exit 1 } } if ( ($PNAgentAllowUserPwdChange -eq $False) -and ($PNAgentDefaultService -eq $True) ) { try { Enable-STFStorePna -StoreService $store -DefaultPnaService -LogonMethod $LogonMethod DS_WriteLog "S" "The PNAgent was enabled successfully on the store '$StoreName'" $LogFile DS_WriteLog "S" " -Allow user change password: no" $LogFile DS_WriteLog "S" " -Default PNAgent service: yes" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to enable the PNAgent on the store '$StoreName' (error: $($Error[0]))" $LogFile Exit 1 } } if ( ($PNAgentAllowUserPwdChange -eq $True) -and ($PNAgentDefaultService -eq $False) ) { try { Enable-STFStorePna -StoreService $store -AllowUserPasswordChange -LogonMethod $LogonMethod DS_WriteLog "S" "The PNAgent was enabled successfully on the store '$StoreName'" $LogFile DS_WriteLog "S" " -Allow user change password: yes" $LogFile DS_WriteLog "S" " -Default PNAgent service: no" $LogFile } catch { DS_WriteLog "E" "An error occurred trying to enable the PNAgent on the store '$StoreName' (error: $($Error[0]))" $LogFile Exit 1 } } } else { DS_WriteLog "I" "The PNAgent is already enabled on the store '$StoreName' in the IIS site $SiteId" $LogFile } } else { DS_WriteLog "I" "The PNAgent should not be enabled on the store '$StoreName' in the IIS site $SiteId" $LogFile } DS_WriteLog "-" "" $LogFile } end { DS_WriteLog "I" "END FUNCTION - $FunctionName" $LogFile } } #========================================================================== ################ # 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 StoreFront (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 # --------------------------------------------------------------------------------------------------------------------------- # Install/import a root certificate to the local machine "Trusted Root Certificates Authorities" certificate store $CertFile = Join-Path $StartDir "Files\MyRootCert.cer" DS_InstallCertificate -StoreScope "LocalMachine" -StoreName "Root" -CertFile $CertFile DS_WriteLog "-" "" $LogFile # Install/import a password protected personal certificate including private key to the local machine "Personal" store $CertFile = Join-Path $StartDir "Files\MyPersonalCert.pfx" DS_InstallCertificate -StoreScope "LocalMachine" -StoreName "My" -CertFile $CertFile -CertPassword "123456" DS_WriteLog "-" "" $LogFile # Create the StoreFront deployment, store and farm with SSL communication to the Delivery Controllers and port 80 and 554 enabled on the IIS site and PNAgent enabled DS_CreateStoreFrontStore -FriendlyName "MyStore" -HostbaseUrl "https://portal.mydomain.local" -CertSubjectName "$($env:ComputerName).mydomain.local" -AddHostHeaderToIISSiteBinding $False -FarmName "MyFarm" -FarmServers "Server1.mydomain.com","Server2.mydomain.com" -StoreVirtualPath "/Citrix/MyStore" -ReceiverVirtualPath "/Citrix/MyStoreWeb" -LoadBalanceServers $True -XMLPort 443 -HTTPPort 80 -HTTPSPort 443 -TransportType "HTTPS" -EnablePNAgent $True -PNAgentDefaultService $True -LogonMethod "SSON" DS_WriteLog "-" "" $LogFile # Disable Customer Experience Improvement Program (CEIP) DS_WriteLog "I" "Disable Customer Experience Improvement Program (CEIP)" $LogFile DS_SetRegistryValue -RegKeyPath "hklm:\SOFTWARE\Citrix\Telemetry\CEIP" -RegValueName "Enabled" -RegValue "0" -Type "DWORD" # --------------------------------------------------------------------------------------------------------------------------- # 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 -executionpolicy bypass -file C:\Temp\Citrix\StoreFront\Configure_CitrixStoreFront.ps1
Log files are created in the directory C:\Logs\Citrix_StoreFront_(configure), but you can change this to any directory you want (see lines 1066 and 1067).
This concludes this article on the Citrix StoreFront unattended installation. You can customize the scripts in this article in any way you see fit.
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: Scripting the complete list of Citrix components with PowerShell - Dennis Span
Pingback: Detailed Change Log – Carl Stalhood
Pingback: StoreFront 3.5 through 3.13 – Basic Configuration – Carl Stalhood
Pingback: Citrix StoreFront Multi-Site Aggregation with PowerShell - Dennis Span
Pingback: Translating the Citrix StoreFront console to PowerShell - Dennis Span
Hallo Herr Span,
vielleicht können sie mir einen Tip geben.
Ich hbae folgendes Problem.
Ich starte die Storefront.exe und diese läd und läd aber zeigt nichts an.
Können wie mir hierzu ein tip geben?
Vielen Dank
Mfg
S.Casper
Hallo Casper,
Meine Entschuldigung für die späte Antwort, aber ich war auf Urlaub. Was genau meinst du mit der StoreFront.exe? Du meinst nicht zufälligerweise Director, oder? Bitte schicke mir mehr Information. Danke!
Grüße,
Dennis
Pingback: Site Updates – January 2018 – Carl Stalhood