Jeremy Davis
Jeremy Davis
Sitecore, C# and web development
Article printed from: https://blog.jermdavis.dev/posts/2015/scripted-dms-installs

Scripted DMS Installs

Published 22 June 2015
Updated 26 June 2018

Continuing my current theme of installation automation via PowerShell, the next task I found myself looking at was how to enable Sitecore's analytics for a newly installed Sitecore instance. This is a task that the standard .exe installer doesn't do for the v6.6 sites I'm working with, but is relatively simple to automate.

The tasks we need to perform to get DMS up and running are as follows:

  • Grab the zip file containing the files to install for the DMS
  • Extract the files
  • Copy the datbase files to the right location
  • Attach the database files to your SQL server
  • Copy the DMS config files to the include folder of the website
  • Add the DMS connection string to the site's config

None of these tasks are particularly hard, but thinking about these while considering how to improve on the previous bits of script I'd created made me realise that there's lots of site configuration data required by these scripts. And that means the scripts need a sensible place to find this data in order to make it easy to reuse the PowerShell code for different Sitecore instances.

Getting the instance configuration

There are loads of possible ways of dealing with this config data, but the one that popped into my head was to create a simple XML document to represent the data. Thinking ahead to the other tasks I plan to automate, the data for a new instance needs to store:

  • General config properties like "what is the instance name" and "where is the license file"
  • The set of extra host names to bind
  • The set of modules and content packages to install

Some simple-to-read XML to describe that data might look like this:

 <config>
  <params>
    <param name="InstanceName">Test</param>
    <param name="LicenseFile">.\files\License-2014.xml</param>
    <param name="SitecoreInstaller">.\files\Sitecore 6.6.0 rev. 140410.exe</param>
    <param name="DMSFiles">.\files\DMS 6.6.0 rev. 140410.zip</param>
  </params>
  <hostnames>
    <hostname>www.test.dev</hostname>
  </hostnames>
  <modules>
    <module>.\files\MyPackage-1.0.zip</module>
  </modules>
</config>

					

PowerShell is pretty good at dealing with data in XML, so we can easily make the location of one of these config documents a required parameter of our "install an instance" script, and parse the file into an XML document. The individual config functions can then query this document.

Loading the data might look like:

param([Parameter(Mandatory=$true)][string]$configFile)

if( -not (Test-Path $configFile) ) {
    throw "Config file must exist"
}

[xml]$xml = Get-Content $configFile

#
# Validate the XML here
#

#
# Rest of script can use the XML data now
# 


					

By declaring a param() section at the top of our script file, we say that this parameter is coming from the command line. It can be marked as required with attributes - which will ensure that PowerShell receives a value from the user. And we can then also test that the value we received is a valid file.

The script can then use the Get-Content commandlet to return an XML document which we can query later. At this point I've not added any code to validate that the XML loaded has the correct schema – but that would be a useful thing to add in later.

And then the data can be read with some simple helper functions:

function Get-ConfigParam([string] $name) {
    $val = Select-Xml "/config/params/param[@name='$name']/text()" $xml
    
    if([string]::IsNullOrEmpty($val)) {
        throw "Parameter $name is missing from config"
    }
    
    return [string]$val
}

function Get-ConfigHostnames() {
    return Select-Xml "/config/hostnames/hostname/text()" $xml
}

function Get-ConfigModules() {
    return Select-Xml "/config/modules/module/text()" $xml
}

					

The Get-ConfigParam function looks up a named value from the set of general configuration data and returns it. The Get-ConfigHostnames and Get-ConfigModules return lists of strings which can be iterated over by other code.

With these functions in place, the basic script can now call the "install instance" script from last week's post, fetching the parameters to pass to the MSI installer from a config file.

Adding DMS to your instance

So with this in hand, the code for adding DMS might look like:

function Add-DMS() {
    # fetch the zip
    $dmsZip = Get-ConfigParam "DMSFiles"
    
    # decompress it
    Write-Host "Decompressing DMS files..."
    Expand-ZipFile $dmsZip '.\dms'
    
    $siteName = Get-ConfigParam "InstanceName"
    $sitecoreFolder = "C:\Inetpub\wwwroot\$($siteName)"
    
    # copy config files to right place
    Write-Host "Copying DMS config"
    Copy-Item ".\dms\*.config" "$($sitecoreFolder)\Website\App_Config\Include" -force
    
    # copy db files to right place
    Write-Host "Copying DMS DB Files"
    Copy-Item ".\dms\*.mdf" "$($sitecoreFolder)\Database\MDF" -force 
    Copy-Item ".\dms\*.ldf" "$($sitecoreFolder)\Database\LDF" -force
    
    # Attach databases
    Write-Host "Attaching DMS database"
    
    $osql = "OSQL.EXE"
    $dbFile = Get-Item ".\dms\*.mdf" | % { Join-Path "$($sitecoreFolder)\Database\MDF" $_.Name }
    $lgFile = Get-Item ".\dms\*.ldf" | % { Join-Path "$($sitecoreFolder)\Database\LDF" $_.Name }
    $dbName = "$($siteName)_Sitecore_Analytics"

    $query = "CREATE DATABASE [$dbName] ON ( FILENAME = N'$($dbFile)' ),( FILENAME = N'$($lgFile)' ) FOR ATTACH"

    &$osql -S localhost -E -Q "$($query)"
    
    # can't ignore connection string - it's needed for WFfM install
    Write-Host "Adding DMS connection string"
    $conStrFile = "$($sitecoreFolder)\Website\App_Config\ConnectionStrings.config"
    Add-DMSConnectionString $conStrFile $dbName  

    # tidy mess
    Write-Host "Removing DMS temp folder"
    rmdir ".\dms" -Recurse -Force
}

					

The code gets the location of the DMS zip file downloaded from SDN from the config code above, and then decompresses it to a temporary folder. It can then get the instance name of your Sitecore install, and work out the correct disk folder that our previous Sitecore install script used.

The DMS config include files can then be copied to the instance's config include folder, and the DMS database files can be copied to the Sitecore database folders.

Next the code works out the absolute path to the DMS database files that were just copied, and works out the correct database name. Then it uses the SQL Server command line tool to run a T-SQL command to attach the database correctly.

The code then calls another function to add the connection string, before tidying up the temporary folder.

The function to add the connection string might look like this:

function Add-DMSConnectionString([string] $conStrFile, [string] $dbName) {
    $sqlUser = Get-ConfigParam "sqlUser"
    $sqlPassword = Get-ConfigParam "sqlPassword"
    $sqlServer = Get-ConfigParam "sqlServer"

    # work out correct connection string
    $constr = "user id=$sqlUser;password=$sqlPassword;Data Source=$sqlServer;Database=$dbName"
    
    # Get connection file as XML Document
    [xml] $conStrXml = Get-Content $conStrFile
    
    # build the new entry
    $entry = $conStrXml.CreateElement("add")
    
    $nameAttr = $conStrXml.CreateAttribute("name")
    $nameAttr.Value = "analytics"
    $entry.Attributes.Append($nameAttr) | out-null

    $conAttr = $conStrXml.CreateAttribute("connectionString")
    $conAttr.Value = $constr
    $entry.Attributes.Append($conAttr) | out-null

    # Add the entry to the file
    $connections = $conStrXml.SelectSingleNode("/connectionStrings")
    $connections.AppendChild($entry) | Out-Null

    # Save the file
    $conStrXml.Save($conStrFile)
}

					

This bit of code takes the location of the connection string file and the name of the database to attach. It then uses the config functions to get the SQL server, user and password.

Then it can calculate the correct connection string, before adding the extra XML element and its attributes and then saving the changes.

Next week, my plan is to look at how you can automatically add modules or packages into your Sitecore instance after it's been installed.

↑ Back to top