In the past I wrote a couple of blog posts about the unattended installation and configuration of Citrix StoreFront. I had a version with PowerShell and a building block for Ivanti Automation.
Since it’s been a while and some PowerShell cmdlets for the configuration have changed I have created new versions. In this blog I will describe how you can use these in your own environment.
These scripts have been tested with Citrix StoreFront 2503 on Windows Server 2022.
NOTE: Although I also provide an Ivanti Automation building block, this post will focus on the PowerShell version. The Ivanti Automation version is not really that much different.
As before I have divided it into three PowerShell scripts which perform the following actions:
(contents and download link further down the page)
SFScript-1-Install-StoreFront.ps1
* Should be run on all StoreFront servers.
– Add required Windows Features (reboot no longer needed)
– Installation of .NET runtime (prevents error during StoreFront install)
– Install StoreFront (with recommended reboot)
– Copy StoreFront shortcut to desktop
SFScript-2-Setup-Primary-StoreFront-Instance.ps1
* Should be run on the first StoreFront server.
– Create store
– Create (self-signed or domain CA) certificate
– Bind certificate to site
– Create redirect to Receiver for Web Site
– Configure dedicated port for propagating (optional)
– Define trusted Active Directory domain (optional)
– Query base URL to check if it already exists
SFScript-3-Join-Existing-StoreFront-Instance.ps1
* Should be run on all subsequent StoreFront servers.
– Get passcode from primary StoreFront instance
– Join StoreFront server group with passcode
– Propagate changes from primary StoreFront instance
– Create (self-signed or domain CA) certificate
– Bind certificate to site
– Copy redirect from primary StoreFront instance
To use them please follow these steps:
1. Change the variable in script 1 that has the ‘EDIT’-comment behind them to a value that matches your environment.
2. Run script 1 on all (planned) StoreFront servers.
If a reboot is needed, you can run the same script after the reboot and it will simply skip the parts it has already done. But not performing a reboot after the Windows features installation didn’t cause any problems in my lab environment. I do recommend a reboot after the StoreFront installation. In my tests I encountered some issues with the server group not propagating correctly.
One addition in this new version is the .NET Runtime. When StoreFront is (silently) installed it gives an error:
Failed to open directory: C:\Program Files\dotnet\shares\Microsoft.NETCore.App
After installation that folder is present and StoreFront works accordingly, so it does not seem like an issue. However to suppress this error the download and installation of the .NET Runtime (the version that comes with StoreFront) will be performed before the StoreFront installation is started.
3. Change all variables in script 2 that have the ‘EDIT’-comment behind them to values that match your environment.
A new addition is the option to use the Active Directory Domain Certificate Authority to create SSL certificates. If the Domain CA FQDN and instance is not specified, the script will use certutil.exe to find it.
It also requires certificate information like Organizational Unit, Location, Country, etc. So make sure these are filled when using this type of certificate. If you set it to SelfSigned this information is not required.
Another addition is the option to specify the allowed authentication domains and if the domain dropdown box is displayed during StoreFront logon. Leave this empty if you do not want this configured.
You can define a specific port for propagating changes between the StoreFront instances. This is useful if these are in different subnets and the connection goes through a firewall. Otherwise you can leave it empty. It will then use a random port.
4. Run script 2 on the primary StoreFront server.
5. Change the variables in script 3 that has the ‘EDIT’-comment behind them.
This simply is the hostname, IP address or FQDN of the primary StoreFront server and the same SSL certificate information as in script 2.
6. Run script 3 on all subsequent StoreFront servers.
Run it on one server at a time and not all at once.
That’s it for the instructions. On to the actual downloads & scripts.
Download Ivanti Automation building blocks
DISCLAIMER: Once again: I’m in no way an expert PowerShell scripter, so it might not be the most efficient code, but it gets the job done. And, of course, feel free to use it/alter it/publish it as your own.
Script 1 – Install StoreFront
# SCRIPT INFO -------------------
# Script that installs Citrix StoreFront
# Run on designated StoreFront server
# By Chris Jeucken
# v2.0
# -------------------------------
# INSTALLATION VARIABLES --------
# User defined variables
$StoreFrontSoftwarePath = "" # <-- EDIT - Specify the folder where the CitrixStoreFront-x64.exe file is located (can also be a local folder)
$DotNetSoftwarePath = "" # <-- EDIT - OPTIONAL - Specify the folder where the windowsdesktop-runtime-8.0.11-win-x86.exe file is located (can also be a local folder)
# Pre-defined variables - Only edit if really required.
$StoreFrontInstaller = "CitrixStoreFront-x64.exe"
$StoreFrontInstallerPath = $StoreFrontSoftwarePath + "\" + $StoreFrontInstaller
$DotNetInstaller = "dotnet-hosting-8.0.10-win.exe"
$DotNetInstallerPath = $DotNetSoftwarePath + "\" + $DotNetInstaller
$DotNetInstallerUri = "https://builds.dotnet.microsoft.com/dotnet/Runtime/8.0.10/dotnet-runtime-8.0.10-win-x64.exe"
# -------------------------------
# PREREQUISITES -----------------
# Set Stop on error
$ErrorActionPreference = "Stop"
# Disable open file security warnings
$env:SEE_MASK_NOZONECHECKS = 1
# -------------------------------
# INSTALL FEATURES --------------
Import-Module ServerManager
$FeatureInstall = Install-WindowsFeature -Name Web-Default-Doc,Web-Http-Errors,Web-Static-Content,Web-Http-Redirect,Web-Http-Logging,Web-Filtering,Web-Basic-Auth,Web-Windows-Auth,Web-Net-Ext45,Web-AppInit,Web-Asp-Net45,Web-ISAPI-Ext,Web-ISAPI-Filter,Web-Mgmt-Console,Web-Scripting-Tools,NET-Framework-45-ASPNET -Restart:$false
if ($FeatureInstall.ExitCode -eq "NoChangeNeeded") {
Write-Output "INFO: Required IIS Windows features already installed"
} elseif ($FeatureInstall.ExitCode -eq "Success") {
Write-Host "INFO: IIS Windows features installed"
#Start-Sleep -Seconds 20
#Restart-Computer -Force
} else {
Write-Output "ERROR: IIS Windows features installation failed"
}
# -------------------------------
# SCRIPT ------------------------
# Check if StoreFront is already installed
if ((Get-WmiObject -Class Win32_Product | Where-Object {$_.Name -like "Citrix*"}).Name -contains "Citrix StoreFront") {
Write-Output "INFO: Citrix StoreFront already installed"
} else {
# Reload Path environment variable (needed if this script is run directly after the feature installation without reboot or restart of PowerShell instance)
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine")
# Install dotNET hosting runtime (if available) - Installer gives an error without it (and then installs it anyway (doh))
if (!($DotNetSoftwarePath)) {
Write-Output "INFO: No dotNET runtime location specified. Trying to download it."
$DotNetInstallerPath = ($env:TEMP + "\" + (Split-Path -Path $DotNetInstallerUri -Leaf))
Invoke-WebRequest -Uri $DotNetInstallerUri -OutFile $DotNetInstallerPath
}
if (Test-Path -Path $DotNetInstallerPath -ErrorAction SilentlyContinue) {
Start-Process -FilePath $DotNetInstallerPath -ArgumentList "/install /quiet /norestart" -Wait
}
# Install StoreFront
Write-Output "INFO: Installing Citrix StoreFront"
$StoreFrontInstallerArguments = "-silent"
Start-Process -FilePath $StoreFrontInstallerPath -ArgumentList $StoreFrontInstallerArguments -Wait -PassThru | Out-Null
if ((Get-WmiObject -Class Win32_Product | Where-Object {$_.Name -like "Citrix*"}).Name -contains "Citrix StoreFront") {
Write-Output "INFO: Citrix StoreFront installed"
} else {
Write-Error "Citrix StoreFront installation failed"
}
}
# Copy Citrix StoreFront shortcut to public desktop
Copy-Item -Path ($env:ProgramData + "\Microsoft\Windows\Start Menu\Programs\Citrix\Citrix StoreFront.lnk") -Destination ($env:PUBLIC + "\Desktop") -Force
# Perform post-installation reboot (recommended)
Start-Sleep -Seconds 10
Restart-Computer -Force
# -------------------------------
Script 2 – Setup primary StoreFront instance
# SCRIPT INFO -------------------
# Script that sets up the first StoreFront instance
# Run on designated (primary) StoreFront server
# By Chris Jeucken
# v2.0
# -------------------------------
# CONFIGURATION VARIABLES -------
# User defined variables
$SFBaseURL = "" # <-- EDIT - Define Base URL (e.g. https://storefront.local.lan)
$XDDeliveryController1 = "" # <-- EDIT - Define primary Citrix VADs Delivery Controller (e.g. xddc1.local.lan)
$XDDeliveryController2 = "" # <-- EDIT - Define secondary Citrix VADs Delivery Controller (leave empty if there is only one Delivery Controller)
$StoreFriendlyName = "" # <-- EDIT - Define StoreFront store name
$XDFarmName = "" # <-- EDIT - Define Citrix VADs farm name
$SFCustomP2PPort = "" # <-- EDIT - OPTIONAL - Define port for propagating changes (leave empty for random port (default))
$CertificateType = "None" # <-- EDIT - OPTIONAL - Specify type of SSL certificate (if any): None, SelfSigned or DomainCA
$CertificateAuthority = "" # <-- EDIT - OPTIONAL - Specify domain certificate authority hostname, FQDN or IP (if DomainCA)
$CertificateAuthorityInstance = "" # <-- EDIT - OPTIONAL - Specify domain certificate authority instance (if DomainCA)
$CertInfoOU = "" # <-- EDIT - OPTIONAL - Specify domainCA certificate information - Organizational Unit
$CertInfoOrg = "" # <-- EDIT - OPTIONAL - Specify domainCA certificate information - Organization
$CertInfoLoc = "" # <-- EDIT - OPTIONAL - Specify domainCA certificate information - Location
$CertInfoState = "" # <-- EDIT - OPTIONAL - Specify domainCA certificate information - State
$CertInfoCountry = "" # <-- EDIT - OPTIONAL - Specify domainCA certificate information - Country
$AuthenticationDomains = "" # <-- EDIT - OPTIONAL - Specify Active Directory authentication domains
$AuthenticationDomainsHide = "0" # <-- EDIT - OPTIONAL - Set to 1 to remove the Active Directory authentication domain selection box
# Pre-defined variables - Only edit if really required.
$SiteID = 1
$StoreFriendlyNameWithoutSpaces = $StoreFriendlyName -Replace "\s",""
$StoreFriendlyNameWithoutSpacesWeb = $StoreFriendlyNameWithoutSpaces + "Web"
$StoreFriendlyNameWithoutSpacesAuth = $StoreFriendlyNameWithoutSpaces + "Auth"
$StoreVirtualPath = "/Citrix/" + $StoreFriendlyName -Replace "\s",""
$StoreVirtualPathWeb = $StoreVirtualPath + "Web"
$StoreVirtualPathAuth = $StoreVirtualPath + "Auth"
$FarmType = "XenDesktop"
$TransportType = "HTTPS"
$ServicePort = "443"
$SslRelayPort = "443"
$RedirectFile = "SFRedirect.html"
$RedirectPath = "C:\inetpub\wwwroot\"
$RedirectPage = $RedirectPath + $Redirectfile
$SFConfigFiles = "$env:ProgramFiles\Citrix\Receiver StoreFront\Services\SubscriptionsStoreService\Citrix.DeliveryServices.SubscriptionsStore.ServiceHost.exe.config",
"$env:ProgramFiles\Citrix\Receiver StoreFront\Services\CredentialWallet\Citrix.DeliveryServices.CredentialWallet.ServiceHost.exe.config"
$CertTemplateFile = $env:TEMP + "\SFCertRequest.inf"
$CertRequestFile = $env:TEMP + "\SFCertRequest.req"
$CertFile = $env:TEMP + "\SFCertificate.cer"
# -------------------------------
# PREREQUISITES -----------------
# Set Stop on error
$ErrorActionPreference = "Stop"
# Reload PSModulePath environment variable (needed if this script is run directly after the SF installation without reboot or restart of PowerShell instance)
$env:PSModulePath = [System.Environment]::GetEnvironmentVariable("PSModulePath","Machine")
# Import StoreFront PS modules (not needed if using PoSH 3.0+, but just to be sure)
Import-Module Citrix.StoreFront
. "C:\Program Files\Citrix\Receiver StoreFront\Scripts\ImportModules.ps1" | Out-Null
Start-Sleep -Seconds 5
# -------------------------------
# SCRIPT ------------------------
# Create StoreFront deployment
Write-Output "INFO: Create StoreFront deployment"
Add-STFDeployment -SiteId $SiteID `
-HostBaseUrl $SFBaseURL `
-Confirm:$false
# Create StoreFront suthentication service
Write-Output "INFO: Create StoreFront authentication service"
Add-STFAuthenticationService -VirtualPath $StoreVirtualPathAuth `
-SiteId $SiteID `
-FriendlyName $StoreFriendlyNameWithoutSpacesAuth | Out-Null
# Setup Citrix VAD Delivery Controllers
if ($XDDeliveryController2) {
$XDDeliveryControllers = ($XDDeliveryController1 + "," + $XDDeliveryController2).Split(",")
} else {
$XDDeliveryControllers = $XDDeliveryController1
}
# Create StoreFront store service
Write-Output "INFO: Create StoreFront store service"
Add-STFStoreService -VirtualPath $StoreVirtualPath `
-FriendlyName $StoreFriendlyName `
-SiteId $SiteID `
-AuthenticationService (Get-STFAuthenticationService -SiteId $SiteID -VirtualPath $StoreVirtualPathAuth) `
-FarmName $XDFarmName `
-FarmType $FarmType `
-Servers @($XDDeliveryControllers) `
-TransportType $TransportType `
-Port $ServicePort `
-SSLRelayPort $SslRelayPort | Out-Null
# Create StoreFront Web Receiver service
Write-Output "INFO: Create StoreFront Web Receiver service"
Add-STFWebReceiverService -VirtualPath $StoreVirtualPathWeb `
-SiteId $SiteID `
-StoreService (Get-STFStoreService -SiteId $SiteID -VirtualPath $StoreVirtualPath) `
-FriendlyName $StoreFriendlyNameWithoutSpacesWeb `
-WebUIExperence Workspace | Out-Null
# Initial deployment done
Write-Output "INFO: StoreFront configured with first store"
# Setup certificates (if HTTPS in base URL)
if ($SFBaseURL -like "*https*") {
# Remove https from URL
$SFBaseURLShort = ($SFBaseURL -replace "https://","")
$LocalFQDN = ([System.Net.Dns]::GetHostByName($env:COMPUTER)).HostName
if ($CertificateType -eq "SelfSigned") {
# Create self signed certificate
$Certificate = New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname $SFBaseURLShort,$LocalFQDN
# Add certificate to Trusted Root Certification Authorities
$CertificateRootStore = New-Object System.Security.Cryptography.X509Certificates.X509Store -ArgumentList Root, LocalMachine
$CertificateRootStore.Open("MaxAllowed")
$CertificateRootStore.Add($Certificate)
$CertificateRootStore.Close()
# Get certificate thumbprint
$Thumbprint = $Certificate.Thumbprint
# Create new IIS binding
New-WebBinding -Name "Default Web Site" -IP "*" -Port 443 -Protocol https
# Add certiticate to IIS binding
Start-Sleep -Seconds 5
$Binding = Get-WebBinding -IPAddress "*" -Port 443 -Protocol https
$Binding.AddSslCertificate($Thumbprint, "my")
Write-Output "INFO: Created and bound self-signed certificate to StoreFront website"
} elseif ($CertificateType -eq "DomainCA") {
# Create domain CA certificate and bind it to site
if (!($CertificateAuthority)) {
Write-Output "INFO: No Domain Certificate Authority defined. Trying to find it with certutil."
$CertUtilConfig = (& $env:windir\System32\certutil.exe | Select-String -Pattern "Config:") -Split ("`"") `
| Where-Object {$_ -notlike ""} | Select-Object -Last 1
$CertificateAuthority = ([string]$CertUtilConfig).Split("\")[0]
$CertificateAuthorityInstance = ([string]$CertUtilConfig).Split("\")[1]
if (!($CertificateAuthority)) {
Write-Error "Could not find Domain Certificate Authority, please specify in the variables"
}
Write-Output "INFO: Found Domain Certificate Authority: $CertificateAuthority"
Write-Output "INFO: Found Domain Certificate Authority instance: $CertificateAuthorityInstance"
}
# Create certificate request file
$CertRequestTemplate = @"
[Version]
Signature="$Windows NT$"
[NewRequest]
Subject = "CN=$SFBaseURLShort, OU=$CertInfoOU, O=$CertInfoOrg, L=$CertInfoLoc, S=$CertInfoState, C=$CertInfoCountry"
KeySpec = 1
KeyLength = 4096
Exportable = TRUE
FriendlyName = "$SFBaseURLShort"
MachineKeySet = TRUE
SMIME = False
PrivateKeyArchive = FALSE
UserProtected = FALSE
UseExistingKeySet = FALSE
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
ProviderType = 12
RequestType = PKCS10
KeyUsage = 0xa0
[Extensions]
2.5.29.17 = "{text}"
_continue_ = "dns=$SFBaseURLShort&"
_continue_ = "dns=$LocalFQDN&"
[RequestAttributes]
CertificateTemplate=WebServer
"@
$CertRequestTemplate | Set-Content -Path $CertTemplateFile -Force
& certreq.exe -f -new "$CertTemplateFile" "$CertRequestFile"
# Sign certificate with defined certificate authority
$CertificateAuthorityFull = $CertificateAuthority + "\" + $CertificateAuthorityInstance
& certreq.exe -f -submit -config "$CertificateAuthorityFull" "$CertRequestFile" "$CertFile"
# Import certificate to local machine
if (Test-Path -Path $CertFile -ErrorAction SilentlyContinue) {
& certreq.exe -accept "$CertFile"
}
# Get certificate thumbprint
$Certificate = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.Subject -like "CN=$SFBaseURLShort*"} | Sort-Object -Property NotBefore | Select-Object -Last 1
$Thumbprint = $Certificate.Thumbprint
# Create new IIS binding
New-WebBinding -Name "Default Web Site" -IP "*" -Port 443 -Protocol https
# Add certiticate to IIS binding
Start-Sleep -Seconds 5
$Binding = Get-WebBinding -IPAddress "*" -Port 443 -Protocol https
$Binding.AddSslCertificate($Thumbprint, "my")
Write-Output "INFO: Created and bound domain certificate to StoreFront website"
}
}
# Create redirect to StoreFront site
Add-Content -Path $RedirectPage -Value "<script type=""text/javascript"">"
Add-Content -Path $RedirectPage -Value "<!--"
Add-Content -Path $RedirectPage -Value "window.location=""/Citrix/$StoreFriendlyNameWithoutSpacesWeb"";"
Add-Content -Path $RedirectPage -Value "// -->"
Add-Content -Path $RedirectPage -Value "</script>"
Add-WebConfiguration "system.webserver/defaultdocument/files" -atIndex 0 -Value $RedirectFile
Write-Output "INFO: Redirect to StoreFront website created"
# Insert custom P2P port for propagating changes (otherwise it's random)
if ($SFCustomP2PPort) {
foreach ($SFConfigFile in $SFConfigFiles) {
if (Test-Path -Path $SFConfigFile -ErrorAction SilentlyContinue) {
$SFConfigFileContent = Get-Content -Path $SFConfigFile
$SFConfigFileContent | ForEach-Object {
if ($_ -like "*net.p2p://*") {
$_ -replace "`">",":$SFCustomP2PPort`">"
} else {
$_
}
} | Set-Content -Path $SFConfigFile -Force
}
}
Restart-Service -Name CitrixSubscriptionsStore -Force
Restart-Service -Name CitrixCredentialWallet -Force
Write-Output "INFO: Configured custom P2P port for propagating changes"
}
# Setup trusted Active Directory authentication domain
if ($AuthenticationDomains) {
$StoreAuthService = (Get-STFAuthenticationService -SiteId $SiteID -VirtualPath $StoreVirtualPathAuth)
Set-STFExplicitCommonOptions -AuthenticationService $StoreAuthService -Domains $AuthenticationDomains -DefaultDomain ($AuthenticationDomains | Select-Object -First 1)
if ($AuthenticationDomainsHide -eq "1") {
Set-STFExplicitCommonOptions -AuthenticationService $StoreAuthService -HideDomainField $true
}
Write-Output "INFO: Configured specific domain(s) for username/password authentication"
}
# Query defined base URL
if (!(Resolve-DnsName -Name $SFBaseURLShort -ErrorAction SilentlyContinue)) {
Write-Warning "Defined base URL not found in DNS. This still needs to be created."
}
# -------------------------------
Script 3 – Join existing StoreFront instance
# SCRIPT INFO -------------------
# Script that adds a server to an existing StoreFront instance
# Run on designated (secondary) StoreFront server
# By Chris Jeucken
# v2.0
# -------------------------------
# CONFIGURATION VARIABLES -------
# User defined variables
$SFExistingServer = "" # <-- EDIT - Define hostname or FQDN of primary StoreFront server
$CertificateType = "None" # <-- EDIT - OPTIONAL - Specify type of SSL certificate (if any): None, SelfSigned or DomainCA
$CertificateAuthority = "" # <-- EDIT - OPTIONAL - Specify domain certificate authority hostname, FQDN or IP (if DomainCA)
$CertificateAuthorityInstance = "" # <-- EDIT - OPTIONAL - Specify domain certificate authority instance (if DomainCA)
$CertInfoOU = "" # <-- EDIT - OPTIONAL - Specify domainCA certificate information - Organizational Unit
$CertInfoOrg = "" # <-- EDIT - OPTIONAL - Specify domainCA certificate information - Organization
$CertInfoLoc = "" # <-- EDIT - OPTIONAL - Specify domainCA certificate information - Location
$CertInfoState = "" # <-- EDIT - OPTIONAL - Specify domainCA certificate information - State
$CertInfoCountry = "" # <-- EDIT - OPTIONAL - Specify domainCA certificate information - Country
# Pre-defined variables - Only edit if really required.
# Locations
$RemoteConfigPath = "\\" + $SFExistingServer + "\c$\_CONFIG"
$RemoteRedirectPath = "\\" + $SFExistingServer + "\c$\inetpub\wwwroot\"
$LocalConfigPath = "C:\_CONFIG"
$LocalRedirectPath = "C:\inetpub\wwwroot\"
# Files and scripts
$RedirectFile = "SFRedirect.html"
$RemoteRedirectPage = $RemoteRedirectPath + "\" + $RedirectFile
$RemotePasscodeScript = $RemoteConfigPath + "\SFPasscodeScript.ps1"
$LocalPasscodeScript = $LocalConfigPath + "\SFPasscodeScript.ps1"
$RemotePasscodeFile = $RemoteConfigPath + "\Passcode.txt"
$LocalPasscodeFile = $LocalConfigPath + "\Passcode.txt"
# Certificates
$CertTemplateFile = $env:TEMP + "\SFCertRequest.inf"
$CertRequestFile = $env:TEMP + "\SFCertRequest.req"
$CertFile = $env:TEMP + "\SFCertificate.cer"
# -------------------------------
# PREREQUISITES -----------------
# Set Stop on error
$ErrorActionPreference = "Stop"
# Reload PSModulePath environment variable (needed if this script is run directly after the SF installation without reboot or restart of PowerShell instance)
$env:PSModulePath = [System.Environment]::GetEnvironmentVariable("PSModulePath","Machine")
# Import StoreFront PS modules (not needed if using PoSH 3.0+, but just to be sure)
Import-Module Citrix.StoreFront
. "C:\Program Files\Citrix\Receiver StoreFront\Scripts\ImportModules.ps1" | Out-Null
Start-Sleep -Seconds 5
# -------------------------------
# SCRIPT ------------------------
# Create PowerShell Session object for primary StoreFront server
$PSSession = New-PSSession -ComputerName $SFExistingServer
# Check if folders and/or files already exist
if (!(Test-Path -Path $RemoteConfigPath -ErrorAction SilentlyContinue)) {
New-Item -Path $RemoteConfigPath -ItemType Directory -Force
}
if (Test-Path -Path $RemotePasscodeScript -ErrorAction SilentlyContinue) {
Remove-Item -Path $RemotePasscodeScript -Force
}
if (Test-Path -Path $RemotePasscodeFile -ErrorAction SilentlyContinue) {
Remove-Item -Path $RemotePasscodeFile -Force
}
# Create script on existing StoreFront server
Add-Content -Path $RemotePasscodeScript -Value ". ""C:\Program Files\Citrix\Receiver StoreFront\Scripts\ImportModules.ps1"""
Add-Content -Path $RemotePasscodeScript -Value "Start-DSClusterJoinService"
Add-Content -Path $RemotePasscodeScript -Value "`$Passcode = (Start-STFServerGroupJoin -IsAuthorizingServer -Confirm:`$false).Passcode"
Add-Content -Path $RemotePasscodeScript -Value "Set-Content -Path $LocalPasscodeFile -Value `$Passcode -Force"
# Run script on existing StoreFront server
schtasks /Create /F /TN SFPasscodeScript /S $SFExistingServer /RU "SYSTEM" /TR "powershell.exe -File $LocalPasscodeScript" /SC once /ST 23:30
schtasks /Run /TN SFPasscodeScript /S $SFExistingServer
While ((schtasks /Query /TN SFPasscodeScript /S $SFExistingServer /fo List)[5] -notlike "*Ready") {
Write-Output "INFO: Waiting for scheduled task to finish..."
Start-Sleep -Seconds 3
}
$SFPasscode = Get-Content -Path $RemotePasscodeFile
schtasks /Delete /TN SFPasscodeScript /S $SFExistingServer /F
Remove-Item -Path $RemotePasscodeFile -Force
Remove-Item -Path $RemotePasscodeScript -Force
Remove-Item -Path $RemoteConfigPath -Force
# Join new server to StoreFront group and wait a while for completion
# Start cluster join service on remote machine
Invoke-Command -Session $PSSession -Scriptblock {
. "C:\Program Files\Citrix\Receiver StoreFront\Scripts\ImportModules.ps1" | Out-Null
Start-DSClusterJoinService
}
# Start cluster join service on local machine
Start-DSClusterJoinService
# Start cluster join procedure
Start-STFServerGroupJoin -AuthorizerHostName $SFExistingServer -Passcode $SFPasscode -Confirm:$false
# Wait for cluster joining to complete
Wait-STFServerGroupJoin -Confirm:$false
# Stop cluster join service on remote machine
Invoke-Command -Session $PSSession -Scriptblock {
Stop-DSClusterJoinService
}
Start-Sleep -Seconds 30
# Stop cluster join service on local machine
Stop-DSClusterJoinService
# Propagate changes from existing StoreFront server
if (!($PSSession)) {
$PSSession = New-PSSession -computerName $SFExistingServer
}
Invoke-Command -Session $PSSession -Scriptblock {
Add-PSSnapin Citrix*
Start-DSConfigurationReplicationClusterUpdate -confirm:$FALSE
}
do {
Write-Output "INFO: Waiting for StoreFront replication..."
Start-Sleep -Seconds 60
} while ((Get-WmiObject -Class Win32_PerfFormattedData_PerfProc_Process -Property Name,PercentProcessorTime -Filter "Name LIKE 'Citrix.DeliveryServices.ConfigurationReplicationService%'").PercentProcessorTime -ne "0")
Remove-PSSession -Session $PSSession
# Setup certificates (if HTTPS in base URL)
$SFBaseUrlFull = Get-DSHostBaseUrl
$SFBaseUrl = $SFBaseURLFull.hostBaseUrl
if ($SFBaseURL -like "*https*") {
# Remove https from URL
$SFBaseURLShort = $SFBaseURL.Replace("https://","")
$SFBaseURLShort = $SFBaseURLShort.Replace("/","")
$LocalFQDN = ([System.Net.Dns]::GetHostByName($env:COMPUTER)).HostName
if ($CertificateType -eq "SelfSigned") {
# Create self signed certificate
$Certificate = New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname $SFBaseURLShort,$LocalFQDN
# Add certificate to Trusted Root Certification Authorities
$CertificateRootStore = New-Object System.Security.Cryptography.X509Certificates.X509Store -ArgumentList Root, LocalMachine
$CertificateRootStore.Open("MaxAllowed")
$CertificateRootStore.Add($Certificate)
$CertificateRootStore.Close()
# Get certificate thumbprint
$Thumbprint = $Certificate.Thumbprint
# Create new IIS binding
New-WebBinding -Name "Default Web Site" -IP "*" -Port 443 -Protocol https
# Add certiticate to IIS binding
Start-Sleep -Seconds 5
$Binding = Get-WebBinding -IPAddress "*" -Port 443 -Protocol https
$Binding.AddSslCertificate($Thumbprint, "my")
Write-Output "INFO: Created and bound self-signed certificate to StoreFront website"
} elseif ($CertificateType -eq "DomainCA") {
# Create domain CA certificate and bind it to site
if (!($CertificateAuthority)) {
Write-Output "INFO: No Domain Certificate Authority defined. Trying to find it with certutil."
$CertUtilConfig = (& $env:windir\System32\certutil.exe | Select-String -Pattern "Config:") -Split ("`"") `
| Where-Object {$_ -notlike ""} | Select-Object -Last 1
$CertificateAuthority = ([string]$CertUtilConfig).Split("\")[0]
$CertificateAuthorityInstance = ([string]$CertUtilConfig).Split("\")[1]
if (!($CertificateAuthority)) {
Write-Error "Could not find Domain Certificate Authority, please specify in the variables"
}
Write-Output "INFO: Found Domain Certificate Authority: $CertificateAuthority"
Write-Output "INFO: Found Domain Certificate Authority instance: $CertificateAuthorityInstance"
}
# Create certificate request file
$CertRequestTemplate = @"
[Version]
Signature="$Windows NT$"
[NewRequest]
Subject = "CN=$SFBaseURLShort, OU=$CertInfoOU, O=$CertInfoOrg, L=$CertInfoLoc, S=$CertInfoState, C=$CertInfoCountry"
KeySpec = 1
KeyLength = 4096
Exportable = TRUE
FriendlyName = "$SFBaseURLShort"
MachineKeySet = TRUE
SMIME = False
PrivateKeyArchive = FALSE
UserProtected = FALSE
UseExistingKeySet = FALSE
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
ProviderType = 12
RequestType = PKCS10
KeyUsage = 0xa0
[Extensions]
2.5.29.17 = "{text}"
_continue_ = "dns=$SFBaseURLShort&"
_continue_ = "dns=$LocalFQDN&"
[RequestAttributes]
CertificateTemplate=WebServer
"@
$CertRequestTemplate | Set-Content -Path $CertTemplateFile -Force
& certreq.exe -f -new "$CertTemplateFile" "$CertRequestFile"
# Sign certificate with defined certificate authority
$CertificateAuthorityFull = $CertificateAuthority + "\" + $CertificateAuthorityInstance
& certreq.exe -f -submit -config "$CertificateAuthorityFull" "$CertRequestFile" "$CertFile"
# Import certificate to local machine
if (Test-Path -Path $CertFile -ErrorAction SilentlyContinue) {
& certreq.exe -accept "$CertFile"
}
# Get certificate thumbprint
$Certificate = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.Subject -like "CN=$SFBaseURLShort*"} | Sort-Object -Property NotBefore | Select-Object -Last 1
$Thumbprint = $Certificate.Thumbprint
# Create new IIS binding
New-WebBinding -Name "Default Web Site" -IP "*" -Port 443 -Protocol https
# Add certiticate to IIS binding
Start-Sleep -Seconds 5
$Binding = Get-WebBinding -IPAddress "*" -Port 443 -Protocol https
$Binding.AddSslCertificate($Thumbprint, "my")
Write-Output "INFO: Created and bound domain certificate to StoreFront website"
}
}
# Create redirect to StoreFront site
Copy-Item -Path $RemoteRedirectPage -Destination $LocalRedirectPath
Add-WebConfiguration "system.webserver/defaultdocument/files" -atIndex 0 -Value $RedirectFile
# -------------------------------