Recently Steve McGill asked me if I'd tried using SIF's certificate creation when automating Solr setup for Sitecore. I realised I'd not put any effort into how this might work – and that seemed like an excellent excuse for some research...
So what needs to change from my original attempt at installing Solr via SIF to make this work?
If you look at the standard SIF V2 scripts, then they make use of the
createcert.json
file to generate certificates. The standard setup passes in some parameters like the name for the certificate and where it should be saved, and the script deals with the generation work and ensuring it's trusted. So it could be called a third time to generate a certificate for Solr and write it out to disk. That would mean changing my original SIF script for Solr so that instead of generating the certificiate it needs, it can take a file that had already been generated, and install it.
That means making a few adjustments to the PowerShell...
The biggest logical change to the SIF extension for Solr is that we no longer need to generate a certificate. If we just need to copy a certificate that was already generated - hence the code for
Invoke-EnsureSSLCertificateTask
gets a lot simpler:
function Invoke-EnsureSSLCertificateTask { [CmdletBinding(SupportsShouldProcess=$true)] param( [parameter(Mandatory=$true)] [bool]$solrSSL, [parameter(Mandatory=$true)] [string]$solrName, [parameter(Mandatory=$true)] [string]$solrHost, [parameter(Mandatory=$true)] [string]$certificateStore, [parameter(Mandatory=$true)] [string]$sourceCert ) PROCESS { if($solrSSL) { Write-Information -Message "$sourceCert" -Tag "Copying the site cert to $certificateStore" copy $sourceCert $certificateStore } } }
A new parameter
$sourceCert
is added specifying the path to find the generated certificate. And the logic gets changed to get rid of all the original "generate and trust a certificate" code, and it's replaced with a simple copy operation from the new source location to the same place it went before. I could also get rid of the un-needed parameters that used to be used to generate a certificate, but I realise I didn't get around to that. A task for another day...
The location of the new certificate and its password also needs to be passed in to
Invoke-ConfigureSolrTask
that SIF calls to modify the default Solr config, so it can be passed down the chain...
function Invoke-ConfigureSolrTask { [CmdletBinding(SupportsShouldProcess=$true)] param( [parameter(Mandatory=$true)] [bool]$solrSSL, [parameter(Mandatory=$true)] [string]$solrHost, [parameter(Mandatory=$true)] [string]$solrRoot, [parameter(Mandatory=$true)] [string]$certificateStore, [parameter(Mandatory=$true)] [string]$CertificatePassword ) PROCESS { if($solrSSL) { Write-Information -Message "HTTPS" -Tag "Configuring Solr for HTTPS access" configureHTTPS $solrHost $solrRoot $certificateStore $CertificatePassword } else { Write-Information -Message "HTTP" -Tag "Configuring Solr for HTTP access" configureHTTP $solrHost $solrRoot } } }
...to the
configureHTTPS
method. It already knows how to point Solr at the certificate, but in my original code I'd hardcoded the certificate password for simplicity. SIF doesn't use a simple default password, so we need to be able to pass in the right password, and it needs to be put into the config:
function configureHTTPS { Param( [string]$solrHost, [string]$solrRoot, [string]$certStore, [string]$CertificatePassword ) $solrConfig = "$solrRoot\bin\solr.in.cmd" if(!(Test-Path -Path "$solrConfig.old")) { if($pscmdlet.ShouldProcess("$solrConfig", "Rewriting Solr config file for HTTPS")) { $cfg = Get-Content $solrConfig Rename-Item $solrConfig "$solrRoot\bin\solr.in.cmd.old" $newCfg = $cfg | % { $_ -replace "REM set SOLR_SSL_KEY_STORE=etc/solr-ssl.keystore.jks", "set SOLR_SSL_KEY_STORE=$certStore" } $newCfg = $newCfg | % { $_ -replace "REM set SOLR_SSL_KEY_STORE_PASSWORD=secret", "set SOLR_SSL_KEY_STORE_PASSWORD=$CertificatePassword" } $newCfg = $newCfg | % { $_ -replace "REM set SOLR_SSL_TRUST_STORE=etc/solr-ssl.keystore.jks", "set SOLR_SSL_TRUST_STORE=$certStore" } $newCfg = $newCfg | % { $_ -replace "REM set SOLR_SSL_TRUST_STORE_PASSWORD=secret", "set SOLR_SSL_TRUST_STORE_PASSWORD=$CertificatePassword" } $newCfg = $newCfg | % { $_ -replace "REM set SOLR_HOST=192.168.1.1", "set SOLR_HOST=$solrHost" } $newCfg | Set-Content $solrConfig } Write-Information -Message "$solrConfig" -Tag "Solr config updated for HTTPS access" } else { Write-Information -Message "$solrConfig" -Tag "Solr config already updated for HTTPS access - skipping" } }
So that's the code changes necessary for doing the install of Solr. I realise another tweak I could make here is to get rid of the "install Solr without HTTPS" code path from this file, as we're always using HTTPS. But again, that's a job for when I have some more free time.
So next is the configuration that binds this into SIF:
The version of Solr required by v9.1 is Solr 7.2.1. That gets specified in the json file that describes our Solr requirements to SIF. So lets update that parameter. I also have a newer version of Java running on the VM I tested this on, so that needs updating too:
{ "Parameters" : { "JREVersion": { "Type": "string", "DefaultValue": "1.8.0_202", "Description": "What version of the Java Runtime should " }, "SolrVersion": { "Type": "string", "DefaultValue": "7.2.1", "Description": "What version of Solr should be downloaded for install" } } }
I also decided I wanted a way around the issue of the path for the JRE. Depending on whether you're using 64 bit or 32 bit Java, you get your Java program files folder in a different place. So I decided to add a parameter for which Program Files folder it is using, and configured it for the 64-bit version of Java I was using:
{ "Parameters" : { "ProgFilesFolder": { "Type": "string", "DefaultValue": "C:\\Program Files", "Description": "Where certificates got exported to after generation" } } }
That parameter then gets used to help work out the correct JRE path variable from the original script:
{ "Variables" : { "JREPath": "[concat(parameter('ProgFilesFolder'), '\\Java\\jre', parameter('JREVersion'))]" }, }
Since we changed some method signatures in the PowerShell, the Tasks binding needs updating:
{ "Tasks" : { "Rewrite Solr config file": { "Type": "ConfigureSolr", "Params": { "solrSSL": "[parameter('SolrUseSSL')]", "solrHost": "[parameter('SolrHost')]", "solrRoot": "[variable('SolrInstallFolder')]", "certificateStore": "[variable('CertStoreFile')]", "CertificatePassword": "[parameter('CertificatePassword')]" } }, "Ensure trusted SSL certificate exists (if required)": { "Type": "EnsureSSLCertificate", "Params": { "solrSSL": "[parameter('SolrUseSSL')]", "solrName": "[variable('SolrName')]", "solrHost": "[parameter('SolrHost')]", "certificateStore": "[variable('CertStoreFile')]", "sourceCert": "[variable('SourceCert')]" } } } }
Those changes require some extra data to be passed in. The source certificate name is going to be a combination of a path, the host name we're using for Solr and the ".pfx" extension. Since that involves calculation, it means we need to set it up as a Variable:
"Variables" : { "SourceCert": "[concat(parameter('CertFilePath'), parameter('SolrHost'), '.pfx')]" }
And that means we also have two new parameters to the overall SIF script, in the folder we'll find the certificate in, and the password for the certificate file:
{ "Parameters" : { "CertFilePath": { "Type": "string", "DefaultValue": "c:\\certificates\\", "Description": "Where certificates got exported to after generation" }, "CertificatePassword": { "Type": "string", "DefaultValue": "SIF-Default", "Description": "The password set on the certificate" } }
And that's the changes necessary to my original SIF process. The json file and the PowerShell file are available in a gist.
The first change to make in
XP0-SingleDeveloper.json
is that we need to trigger the creation of a certificate for Solr and then to call the Solr install we created above. These need inserting at the top of the
Includes
section so that they happen first:
{ "Includes": { "SolrCertificates": { "Source": ".\\createcert.json" }, "Solr": { "Source": ".\\SolrServer.json" } } }
For those to work, some further parameters are required. First up, there are some parameters that define how the rest of the script can work:
{ "Parameters": { "SolrExportPassword": { "Type": "String", "DefaultValue": "testpassword", "Description": "Password to export the solr cert with." }, "CertPath": { "Type": "String", "DefaultValue": "c:\\certificates", "Description": "where exported certs go" }, "SolrHost": { "Type": "String", "DefaultValue": "localhost", "Description": "The Solr host name to use." } } }
We don't want SIF to generate a password for the Solr certificate, so we need to define a value for that. And we need to tell the Solr install script where it can find the generated certificate and what the host name for Solr should be.
Some of those values then need passing in to other bits of the script. SIF defines a special naming convention when you want a value to be passed to a specific included script. Where a parameter name is prefixed with the name of an include and a colon, it is specific to that include. Also, instead of defining a default value for these parameters, they define a reference to another parameter's name – effectively copying that value:
{ "Parameters": { "SolrCertificates:CertificateName": { "Type": "String", "Reference": "SolrHost", "Description": "Override to pass SolrCertificateName value to SolrCertificates config." }, "SolrCertificates:ExportPassword": { "Type": "String", "Reference": "SolrExportPassword", "Description": "Password to export the solr cert with." }, "Solr:CertificatePassword": { "Type": "String", "Reference": "SolrExportPassword", "Description": "Password the solr cert was exported with." } } }
Once these changes are made, you then need to tweak the PowerShell that invokes the JSON data. The change I made here was to pass in the
SolrHost
value, but you could pass in the password as well if you wanted to change the default above:
# Solr host name $SolrHost = "solr"; # Install XP0 via combined partials file. $singleDeveloperParams = @{ Path = "$SCInstallRoot\XP0-SingleDeveloper.json" SqlServer = $SqlServer SqlAdminUser = $SqlAdminUser SqlAdminPassword = $SqlAdminPassword SitecoreAdminPassword = $SitecoreAdminPassword SolrHost = $SolrHost SolrUrl = $SolrUrl SolrRoot = $SolrRoot SolrService = $SolrService Prefix = $Prefix XConnectCertificateName = $XConnectSiteName IdentityServerCertificateName = $IdentityServerSiteName IdentityServerSiteName = $IdentityServerSiteName LicenseFile = $LicenseFile XConnectPackage = $XConnectPackage SitecorePackage = $SitecorePackage IdentityServerPackage = $IdentityServerPackage XConnectSiteName = $XConnectSiteName SitecoreSitename = $SitecoreSiteName PasswordRecoveryUrl = $PasswordRecoveryUrl SitecoreIdentityAuthority = $SitecoreIdentityAuthority XConnectCollectionService = $XConnectCollectionService ClientSecret = $ClientSecret AllowedCorsOrigins = $AllowedCorsOrigins }
Again, these changes to the SIF json and the powershell are in the gist for this post.
I suspect there's a bit more tweaking that could be done here to make it prettier and more flexible, but this is a good start...
↑ Back to top