Overview

Installing Sitecore 9.1.1 in a scaled production environment can be tricky.  This post gives a step by step guide so that you can install Sitecore 9.1.1 on multiple servers all from one single PowerShell script!

Step 1 – Plan Your Environment

List the servers that you are going to have in your production environment and decide which Sitecore roles are going to be on which servers.  For the purposes of this guide here are the servers and roles. However, you can break out the servers and roles differently and add more servers if you like.

Server Name

Sitecore Roles

SitecoreCD1

Content Delivery

SitecoreCD2

Content Delivery

SitecoreCMS

(All other roles)

SitecoreProcessing

Processing

SitecoreSolr

Solr

Step 2 – Install SSL Certificates on all servers

Sitecore 9.1.1 uses HTTPS for serving content for some sites and also uses HTTPS for internal communications between servers and services. The simplest solution is to create a wildcard SSL cert that covers all of the different domains so that one SSL cert can be used for everything.

Another option is to create a single SSL cert with many Subject Alternative Names that cover all of the different domains. Basically you need to install certificates to cover all of the different domains that are listed in Step 7 below.

Step 3 – Install Solr on the Solr Server 

Whenever you are installing Solr for use with Sitecore make sure that you are installing the correct version.  You can see the Sitecore – Solr compatibility table here - https://kb.sitecore.net/articles/227897.  For Sitecore 9.1.1 you should use Solr v7.2.1.

  • Make sure that you have Java JRE 1.8.* installed and you know where it is.  If you don’t have it installed you can download it here - https://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html.
  • Make sure that you have the JAVA_HOME environment variable set up on the Solr server so that it points to the JRE ROOT location (not bin).
  • Export the SSL certificate for Solr to a file
    • Open Manage Computer Certificates
    • Go to Trusted Root Certification > Certificates > [Solr Cert] (the cert that you created for Solr in Step 1)
    • Right click on that cert and select All Tasks > Export
    • Check “Yes, export the private key”.  Click Next
    • Select “Include all certificates in the certification path if possible”.  Click Next
    • Check the Password toggle and give it a password.  Make note of the password.  For example “Open1234”.  Click Next
    • Set the path and filename of the cert and click Next and finish the wizard.  For example store the cert at C:\certs\solr_ssl_cert.pfx
  • Install Non-Sucking Service Manager (NSSM) to run Solr as a service
    • Download latest version of NSSM from https://nssm.cc/ and simply extract it to a folder (C:\NSSM)
  • Download and install Solr
  • Configure Solr for SSL
    • Locate and edit “bin\solr.in.cmd” file under the Solr root directory
    • Find this section and uncomment all of the SET lines:
      • REM Uncomment to set SSL-related system properties
      • REM Be sure to update the paths to the correct keystore for your environment
      • set SOLR_SSL_KEY_STORE=etc/solr-ssl.keystore.jks
      • set SOLR_SSL_KEY_STORE_PASSWORD=secret
      • set SOLR_SSL_KEY_STORE_TYPE=JKS
      • set SOLR_SSL_TRUST_STORE=etc/solr-ssl.keystore.jks
      • set SOLR_SSL_TRUST_STORE_PASSWORD=secret
      • set SOLR_SSL_TRUST_STORE_TYPE=JKS
      • set SOLR_SSL_NEED_CLIENT_AUTH=false
      • set SOLR_SSL_WANT_CLIENT_AUTH=false
    • Edit the SET lines to point to your cert file with the password and type of PKCS12.  Using the examples in this post the lines would now look like this:
      • REM Uncomment to set SSL-related system properties
      • REM Be sure to update the paths to the correct keystore for your environment
      • set SOLR_SSL_KEY_STORE=c:\certs\solr_ssl_cert.pfx
      • set SOLR_SSL_KEY_STORE_PASSWORD=Open1234
      • set SOLR_SSL_KEY_STORE_TYPE=PKCS12
      • set SOLR_SSL_TRUST_STORE=c:\certs\solr_ssl_cert.pfx
      • set SOLR_SSL_TRUST_STORE_PASSWORD=Open1234
      • set SOLR_SSL_TRUST_STORE_TYPE=PKCS12
      • set SOLR_SSL_NEED_CLIENT_AUTH=false
      • set SOLR_SSL_WANT_CLIENT_AUTH=false
    • Test Solr by opening a Command Prompt.  Navigate to the [Solr Path]\bin folder.  Run “solr.cmd start -f -p 8983”.  You should see a bunch of text scroll in the window showing you that Solr started successfully.  Close the window to shut Solr down.
  • Setup Solr as a Service
    • To use NSSM do the following.  Open a Command Prompt as an administrator and navigate to the proper NSSM folder.  Most likely it is [NSSMPATH]\win64\nssm
    • Decide on the name of your service.  For example – SOLR
    • Type the following command – “install SOLR”.  NSSM will open so that you can fill out the details
    • On the Application Tab enter the following:
      • Path – the path to the solr.cmd – C:\solr\bin\solr.cmd
      • Startup directory – C:\solr\bin
      • Arguments – start -f -p 8983
    • Move to the Details Tab
      • Enter a display name and a description
      • The startup type should be automatic
    • Click “Install Service” button.  You should receive a confirmation that it installed properly.
    • Test it by opening a browser and going to https://localhost:8983/solr.  You may get an error since the cert that you used was specifically for a company domain.  You might need to test it by going to https://solr.mycompany.com:8983/solr (or whatever the fully qualified domain name is for your Solr server)

Step 4 – Install SIF and Sitecore Fundamentals on all servers

The Sitecore Install Framework and Sitecore Fundamentals Powershell modules must be installed on all servers.

  • Open a PowerShell ISE window as an Administrator
  • In the PowerShell command line run the following cmdlet:
  • Set the repository as trusted by running the following cmdlet:
    • Set-PSRepository -Name SitecoreGallery -InstallationPolicy Trusted
  • Install the Sitecore Install Framework by running the following cmdlet:
    • Install-Module -Name SitecoreInstallFramework -Repository SitecoreGallery
  • Install the Sitecore Fundamentals by running the following cmdlet:
    • Install-Module -Name SitecoreFundamentals -Repository SitecoreGallery
  • Repeat this on all servers.

NOTE: the above steps will install the latest version of the Sitecore Install Framework (SIF) on every computer.  However the latest version of SIF is not always the version that you want.  It is important to make sure that you are installing the correct version of SIF.  Every version of Sitecore may use a different version of SIF.  Sitecore 9.1.1 uses SIF 2.1.0.  There is a way in PowerShell to force the install of the correct version of SIF.  Also you can have multiple versions of SIF installed on one computer and you can switch back and forth between them.  Please see this excellent post that describes how to determine which versions you have installed, which version is current and how to install a specific version - https://www.koenheye.be/multiple-versions-of-the-sitecore-install-framework/.

Step 5 – Enable PowerShell Remoting on all servers

  • Open a PowerShell ISE window as an Adminstrator
  • Run the following cmdlet:  Enable-PSRemoting

Repeat this on all servers.

Step 6 – Install Prerequisites on all servers

To automatically install the prerequisites for any of the topologies on a single server:

  • Download the following file from the Sitecore Downloads Page – http://dev.sitecore.net
    • Prerequisites.json
    • This file is stored in the XP0 Configuration files 9.1.1 rev. 002459.zip file.
  • To install the prerequisites, run the following cmdlet:
    • Install-SitecoreConfiguration -Path .\Prerequisites.json

Repeat this on all servers.

Reboot every server after the prerequisites have been installed.

Step 7 – Allow port traffic for HTTPS Remote PowerShell and Solr

The Sitecore PowerShell script uses HTTPS. This script also uses Windows Remote Management (WinRM) to all remote configuration and setup. The normal port for WinRM is 5985. However the port for WinRM HTTPS is 5986.

  • Create a firewall rule to allow WinRM HTTPS port 5986
  • WinRM needs to be configured to respond to HTTPS requests. Create a HTTPS WinRM Listener on all servers.
  • The WinRM listener should be able to use the existing certs.

Additionally we need to make sure that the Solr server can be accessed over port 8983

  • Create a Windows Firewall rule to allow access to port 8983 on the Solr server

Step 8 – Set up DNS entries for all sites and microservices

In order for the distributed install script to work correctly, DNS entries must be set up properly. Below is a table that shows an example. In this example we will have the following 5 servers:

  • SitecoreCD1
  • SitecoreCD2
  • SitecoreCMS
  • SitecoreProcessing
  • SolrServer

The SitecoreProcessing server will only have the Processing Role. All other roles will be installed on the SitecoreCMS server. This is where you can decide to add more servers and split out roles however you like. Just make sure that the DNS entries match up with your intended roles for each server.  This table below is just an example of the DNS entries.  You are free to name these whatever you want.

Site / Services

DNS Entry

Server

Content Delivery Server 1

Sitecorecd1.company.com

SitecoreCD1

Content Delivery Server 2

Sitecorecd2.company.com

SitecoreCD2

Content Management Server

Sitecorecms.company.com

SitecoreCMS

Processing Server

Scprocessing.company.com

SitecoreProcessing

Solr Server

Solr.company.com

SitecoreSolr

Identity Server

Scidentity.company.com

SitecoreCMS

Reporting Server

Screporting.company.com

SitecoreCMS

Collection Service

Sccollection.company.com

SitecoreCMS

Collection Search Service

Sccollectionsearch.company.com

SitecoreCMS

Reference Data Service

Screference.company.com

SitecoreCMS

Marketing Automation Service

Scmarketing.company.com

SitecoreCMS

Marekting Automation Reporting Service

Scmarketingreporting.company.com

SitecoreCMS

Cortex Processing Service

Sccortexprocessing.company.com

SitecoreCMS

Cortex Reporting Service

Sccortexreporting.company.com

SitecoreCMS

 

Step 9 – Copy all resource files to each server

  • Create a folder on each server called "C:\resourcefiles"
  • Download and save the following WDP packages and SIF resource files in this folder:
    • Sitecore 9.1.1 rev. 002459 (OnPrem)_cd.scwdp.zip
    • Sitecore 9.1.1 rev. 002459 (OnPrem)_cm.scwdp.zip
    • Sitecore 9.1.1 rev. 002459 (OnPrem)_prc.scwdp.zip
    • Sitecore 9.1.1 rev. 002459 (OnPrem)_rep.scwdp.zip
    • Sitecore 9.1.1 rev. 002459 (OnPrem)_xp1collection.scwdp.zip
    • Sitecore 9.1.1 rev. 002459 (OnPrem)_xp1collectionsearch.scwdp.zip
    • Sitecore 9.1.1 rev. 002459 (OnPrem)_xp1marketingautomation.scwdp.zip
    • Sitecore 9.1.1 rev. 002459 (OnPrem)_xp1marketingautomationreporting.scwdp.zip
    • Sitecore 9.1.1 rev. 002459 (OnPrem)_xp1cortexprocessing.scwdp.zip
    • Sitecore 9.1.1 rev. 002459 (OnPrem)_xp1referencedata.scwdp.zip
    • Sitecore 9.1.1 rev. 002459 (OnPrem)_xp1cortexreporting.scwdp.zip
    • Sitecore.IdentityServer 2.0.1 r00166(OnPrem)_identityserver.scwdp.zip
    • IdentityServer.json
    • Sitecore-solr.json
    • Sitecore-XP1-cd.json
    • Sitecore-XP1-cm.json
    • Sitecore-XP1-prc.json
    • Sitecore-XP1-rep.json
    • createcert.json
    • importcert.json
    • xconnect-solr.json
    • xconnect-xp1-collection.json
    • xconnect-xp1-collectionsearch.json
    • xconnect-xp1-MarketingAutomation.json
    • xconnect-xp1-MarketingAutomationReporting.json
    • xconnect-xp1-CortexProcessing.json
    • xconnect-xp1-ReferenceData.json
    • xconnect-xp1-CortexReporting.json
    • Role-Remote.json
    • XP1-Distributed.json

Step 10 – Create and edit XP1-Distributed.ps1 file

An example XP1 Distributed PowerShell script is in section 7.6.2 of the official Sitecore 9.1.1 installation guide. Here is a link to a slightly modified example that was used to install 2 CD servers instead of only 1. [INSERT LINK TO PS1 FILE HERE]

Create this file and call it XP1-Distributed.ps1 and save it in the C:\resourcefiles folder of your installation server.

Step 11 (optional) – Edit JSON files to specify alternate install path

By default the JSON files that you download from Sitecore will install everything in the C:\inetpub\wwwroot folder on each server. If you wish to install in a different folder you should edit each individual JSON file that corresponds to the role that you want to install in a different location.

For example if you want to install the Identity Server in a different location then you should edit the IdentityServer.json file. If you wish to install the CD server in a different location then you should edit the sitecore-XP1-cd.json file. And so on.

In each file that you want to change look for the following line:

"Site.PhysicalPath": "[joinpath(environment('SystemDrive'), 'inetpub', 'wwwroot', parameter('SiteName'))]",

Change it to something like this:

"Site.PhysicalPath": "[joinpath('L:\\Websites', parameter('SiteName'))]",

That will make the installation script install it in the L:\websites folder.

Step 12 (optional) – Edit JSON files to support installation of 2 CD servers

By default the scripts that you download from Sitecore only support the installation of 1 CD server. The above PowerShell script has been modified to support 2 CD servers. Additionally you will need to edit the XP1-Distributed.json file to support 2 CD servers.

In the parameters section it should look like this:

"CD1ComputerName": {
"Type": "String",
"Description": "The name of the computer to deploy content delivery role to.",
"DefaultValue": ""
},
"CD2ComputerName": {
"Type": "String",
"Description": "The name of the computer to deploy content delivery role to.",
"DefaultValue": ""
},

And this is also in the parameters section:

"CD1Sitename": {
"Type": "string",
"DefaultValue": "SitecoreCD",
"Description": "The name of the Sitecore Content Delivery site to be deployed."
},
"CD2Sitename": {
"Type": "string",
"DefaultValue": "SitecoreCD",
"Description": "The name of the Sitecore Content Delivery site to be deployed."
},

And this is also in the parameters section:

"CD1:ComputerName": {
"Type": "String",
"Reference": "CD1ComputerName",
"Description": "Override to pass SitecoreCDComputerName value to SitecoreCD config."
},
"CD1:UserName": {
"Type": "String",
"Reference": "CDUserName",
"Description": "Override to pass SitecoreCDUserName value to SitecoreCD config."
},
"CD1:Password": {
"Type": "String",
"Reference": "CDPassword",
"Description": "Override to pass SitecoreCDPassword value to SitecoreCD config."
},
"CD2:ComputerName": {
"Type": "String",
"Reference": "CD2ComputerName",
"Description": "Override to pass SitecoreCDComputerName value to SitecoreCD config."
},
"CD2:UserName": {
"Type": "String",
"Reference": "CDUserName",
"Description": "Override to pass SitecoreCDUserName value to SitecoreCD config."
},
"CD2:Password": {
"Type": "String",
"Reference": "CDPassword",
"Description": "Override to pass SitecoreCDPassword value to SitecoreCD config."
},

       Then later in the file replace the single CD setup with this:

"CD1Config": ".
Sitecore-xp1-cd.json",
"CD1ResourceFiles": [ "[variable('CD1Config')]" , "[parameter('CDPackage')]", "[parameter('LicenseFile')]" ],
"CD1:ResourceFiles": "[concat(variable('CD1ResourceFiles'), variable('XConnectCertImportResourceFiles'))]",
"CD1:RemoteResourceFolder": "[variable('RemoteResourceFolder')]",
"CD1:ImportCertificatesParameters": "[variable('XConnectCertImportConfigurationParameters')]",
"CD1:ConfigurationParameters": {
"Path": "[JoinPath(variable('RemoteResourceFolder'), SplitPath(Path:variable('CD1Config'),Leaf:true))]",
"Package": "[JoinPath(variable('RemoteResourceFolder'), SplitPath(Path:parameter('CDPackage'),Leaf:true))]",
"SiteName": "[parameter('CD1Sitename')]",
"XConnectCert": "[parameter('CertificateName')]",
"XConnectCollectionService": "[parameter('XConnectCollectionService')]",
"XConnectReferenceDataService": "[parameter('XConnectReferenceDataService')]",
"MarketingAutomationOperationsService": "[parameter('MarketingAutomationOperationsService')]",
"MarketingAutomationReportingService": "[parameter('MarketingAutomationReportingService')]",
"SitecoreIdentityAuthority": "[parameter('SitecoreIdentityAuthority')]",
"SolrUrl": "[parameter('SolrUrl')]",
"SolrCorePrefix": "[parameter('Prefix')]",
"SqlDbPrefix": "[parameter('Prefix')]",
"SqlServer": "[parameter('SqlServer')]",
"LicenseFile": "[JoinPath(variable('RemoteResourceFolder'), SplitPath(Path:parameter('LicenseFile'),Leaf:true))]",
"HostMappingName": "",
"DNSName": "[parameter('CD1ComputerName')]"
},
"CD2Config": ".
Sitecore-xp1-cd.json",
"CD2ResourceFiles": [ "[variable('CD2Config')]" , "[parameter('CDPackage')]", "[parameter('LicenseFile')]" ],
"CD2:ResourceFiles": "[concat(variable('CD2ResourceFiles'), variable('XConnectCertImportResourceFiles'))]",
"CD2:RemoteResourceFolder": "[variable('RemoteResourceFolder')]",
"CD2:ImportCertificatesParameters": "[variable('XConnectCertImportConfigurationParameters')]",
"CD2:ConfigurationParameters": {
"Path": "[JoinPath(variable('RemoteResourceFolder'), SplitPath(Path:variable('CD2Config'),Leaf:true))]",
"Package": "[JoinPath(variable('RemoteResourceFolder'), SplitPath(Path:parameter('CDPackage'),Leaf:true))]",
"SiteName": "[parameter('CD2Sitename')]",
"XConnectCert": "[parameter('CertificateName')]",
"XConnectCollectionService": "[parameter('XConnectCollectionService')]",
"XConnectReferenceDataService": "[parameter('XConnectReferenceDataService')]",
"MarketingAutomationOperationsService": "[parameter('MarketingAutomationOperationsService')]",
"MarketingAutomationReportingService": "[parameter('MarketingAutomationReportingService')]",
"SitecoreIdentityAuthority": "[parameter('SitecoreIdentityAuthority')]",
"SolrUrl": "[parameter('SolrUrl')]",
"SolrCorePrefix": "[parameter('Prefix')]",
"SqlDbPrefix": "[parameter('Prefix')]",
"SqlServer": "[parameter('SqlServer')]",
"LicenseFile": "[JoinPath(variable('RemoteResourceFolder'), SplitPath(Path:parameter('LicenseFile'),Leaf:true))]",
"HostMappingName": "",
"DNSName": "[parameter('CD2ComputerName')]"
},

And finally at the bottom of the file in the Includes section it should look like this:

"CD1": {
"Source": ".
Role-Remote.json"
},
"CD2": {
"Source": ".
Role-Remote.json"
},

 

Step 13 – Fix Bug in XP1-Distributed.json

There is a bug in the XP1-Distributed.json file that you download from Sitecore. At about line 818 in the original file you will see the following:

"XConnectCollectionService": "[parameter('XConnectCollectionService')]",

It should be changed to this:

"XConnectCollectionSearchService": "[parameter('XConnectCollectionSearchService')]",

Step 14 – Run PS1 script from a separate server

The distributed SC9.1.1 installation has to be run from a separate server. It can not be run from one of the target servers for your installation. If you haven't done so already create a C:\resourcefiles folder on your installation folder and copy all of the assets and scripts to that server.

Open PowerShell as an administrator and execute the script as follows:

.\XP1-Distributed.ps1

The script should take about 30 minutes to complete.

Step 15 – Verify Sitecore is running and modify SSL certificates as needed

Verify that Sitecore is running properly by signing in to the CMS site. The default Sitecore admin password is "SIF-Default".

It is possible that the SSL certificates were not applied correctly during the automated install. Sitecore is set to create self-signed certs if it can't find any certs that match. Therefore it is a good idea to go through all of the sites and review the HTTPS bindings to make sure that the SSL certs are being used properly.

Step 16 – Configure API Authentication Keys

Follow the directions on this page - https://doc.sitecore.com/developers/91/platform-administration-and-architecture/en/configure-api-authentication-keys-in-a-scaled-environment.html - to set up the proper API authentication keys.