Jeremy Davis
Jeremy Davis
Sitecore, C# and web development
Article printed from: https://blog.jermdavis.dev/posts/2017/solr-installs-with-sif

Solr installs with SIF

Published 13 November 2017
PowerShell SIF Solr ~5 min. read

Last time out I was looking at scripting installs of Solr using plain old PowerShell. Since the Sitecore world is getting to grips with a new PowerShell based install approach with the Sitecore Install Framework (SIF), it seemed like a sensible idea to try porting my ideas to SIF so see how that would work...

So what is SIF? url copied!

It's Sitecore's new install framework built on top of PowerShell. It takes `.json` files which describe the setup you need performed, and it executes them against your servers. The `.json` files have a syntax for defining the parameters you need to pass in to the install, a mechanism for how to declare computed configuration values based on the parameters, and a way of declaring the set of steps to perform during an install. So extending it to install your own stuff involves creating any PowerShell commandlets you need to implemnent Tasks it doesn't already know about, and then constructing the right json for the process you need.

Declaring extra stuff url copied!

Since SIF is based on PowerShell, declaring extensions is as simple as creating a new PowerShell Module containing the code you want to be able to run. A module is just a file with the `.psm1` extension, which you place in one of the well-known locations for module files. You can find these by typing `$Env:PSModulePath` into a PowerShell window. On my laptop, that gets:
C:\Users\<user>\Documents\WindowsPowerShell\Modules;C:\Program Files\WindowsPowerShell\Modules;C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules;C:\Program Files (x86)\Microsoft SQL Server\130\Tools\PowerShell\Modules\

					

For development purposes, I went for %UserProfile%\Documents\WindowsPowerShell\Modules. I named my module "SolrInstal-SIF-Extensions", so I created a folder with that name in the module directory, and then put the module file into that:

Module Folder

Inside that file, you need three things:

  • The PowerShell commandlet functions you want to be able to call from SIF.
  • Any private functions necessary for the commandlets to work.
  • The registration calls that tell SIF you have a function it should know about.

The commandlets are just normal PowerShell functions with the following structure:

function AllowedVerb-YourChosenNameTask
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        # Whatever set of mandartory and optional parameters you need
    )

    PROCESS
    {
        # Your code...
    }
}

					

The CmdletBinding attribute is important - that allows the code to support the -WhatIf parameter - which is very useful for verifying what a script will do without changing any state.

Initially, I got the registration stuff a bit wrong, but it turns out that as long as you put these statements into your module file, it's pretty simple. These statements just map the full name of your commandlets into a shortened names to use in the .json file later on:

Register-SitecoreInstallExtension -Command AllowedVerb-YourChosenNameTask -As YourChosenName -Type Task

					

In terms of the overall commandlet code, it's mostly just refactoring last week's work into a set of commandlets that seemed to make sense from the SIF perspective. The only significant change is that now the code uses SIF's Write-TaskInfo commandlet to report its status, and it's been modified a bit to support -WhatIf. All that means is that statements which actually change the state of your system have been wrapped in a block of code like this:

if($pscmdlet.ShouldProcess("RelevantData", "DescriptionOfWhatWillBeDoneWithTheData"))
{
    # Code goes here...
}

					

If the user specifies the -WhatIf flag then the statements inside this block are not run, but a log message is generated from the data and description passed it. So you can see what it would do, without it actually doing stuff:

WhatIf

The test for "is the session elevated?" that last week's script contained isn't relevant any more, since SIF needs to run in an elevated PowerShell window. However you do get a slightly confusing error message if you forget to click "Run as administrator" to start it up:

Not Elevated

If you follow the instructions, you'll get a message about how the scripts require elevation – but it's not entirely clear from the message you see initially...

The full script for the module is available in a Gist.

The json data url copied!

This file needs to define how SIF makes use of your module at runtime. Initially it looks a bit complicated, but it's actually fairly easy to get to grips with. It can be a bit tricky diagnosing what's wrong if you make a typo here, as the errors from the scripts aren't that great. I'd recommend using a json-aware editor (like Visual Studio) so that you get warnings for silly typos like missing commas – otherwise it can be hard to track down your mistake. One trick I've found helpful here, is that if you get an odd "don't like that json" error from SIF, try manually converting your json to an object and see what errors that reports:
Get-Content YourFileNameHere.json | ConvertFrom-Json

					

SIF's errors sometimes hide some useful bits of the parsing error detail from you – but this command will let you see what PowerShell thinks of your json file. You'll either see a PowerShell object (which implies successful parsing) or a parser error message. The errors aren't always helpful about the location of the problem in your file, but they generally tell you give you a hint about what sort of parsing error occurred.

So what needs to go in the json file?

The first section is for declaring parameters. These are the bits of data that might vary between each install. This is pretty similar to the set of script parameters declared for the plain PowerShell last time. For each one you declare a name, a type, a default value and a description:

"Parameters" : {
    "JREVersion": {
        "Type": "string",
        "DefaultValue": "1.8.0_151",
        "Description": "What version of the Java Runtime should "
    }
}

					

One thing that caught me out here was that you're declaring json data – so if you want a boolean value, you use the Javascript true rather than the PowerShell $true.

Next, you need to say if you're using any extra modules to extend SIF:

"Modules" : [
    "SolrInstall-SIF-Extensions.psm1"
]

					

You don't need to specify a path, as SIF assumes your extensions are stored in a module folder, as described above.

You can then declare variables. These are bits of data your commandlets need which are computed from parameters or other values that SIF has access to. Hence they're declared as a map from variable name to an expression that can be used to calculate them:

"Variables" : {
    "NSSMSourcePackage": "[concat('https://nssm.cc/release/nssm-', parameter('NSSMVersion'), '.zip')]"
}

					

These are most useful where more than one commandlet requires access to a computed value – rather than having to pass lots of parameters and do the computation on multiple places, you can do it once and pass the result...

Finally there's the task declaration. This sets out the steps your SIF script needs to take, what commandlet will perform the step, and what data to pass in. For example:

"Tasks" : {
    "Ensure NSSM is installed": {
        "Type": "EnsureNSSM",
        "Params": {
            "downloadFolder":    "[parameter('DownloadFolder')]",
            "nssmVersion":       "[parameter('NSSMVersion')]",
            "installFolder":     "[parameter('InstallFolder')]",
            "nssmSourcePackage": "[variable('NSSMSourcePackage')]"
        }
    }
}

					

The type referrs back to the -As parameter you gave to the Register-SitecoreInstallExtension statements in your module declaration. The params are the names of the commandlet parameters, mapped to the value you need to pass in.

SIF will then run each of your tasks in turn, and display the result for you:

Install Running

There's plenty more detail about what all of this means in Sitecore's documentation for SIF – it's well worth a read to familiarise yourself with everything that's going on.

The complete data is available in the Gist for this.

Using this in the real world url copied!

What I've put together here is a stand-alone install for Solr. You could run this in a sequence of "Solr install, XConnect install, Sitecore Install" with another bit of script. Or you might want to merge the json data for Solr into a single json file for your entire installation. I've not tried that, but it should work fine in that scenario... ↑ Back to top