Unattended Citrix StoreFront Installation and Configuration – UPDATE 2025

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 PowerShell 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

PowerShell
# 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

PowerShell
# 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

PowerShell
# 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
# -------------------------------

Loading

Leave a Reply

Your email address will not be published. Required fields are marked *