14

When configuring server instances in a distributed environment, a range of config files must be enabled or disabled based on the instance role (e.g. CD, CM, Processing, Aggregation), referencing the "Config Enable Disable Excel spreadsheet": https://doc.sitecore.net//~/media/F50C507B9BC04FEB9C9D933433A9C3C3.ashx?la=en

On top of the default configs I need to handle configs from 3rd party tools such as Unicorn and Synthesis (I can create a corresponding spreadsheet for each of this).

I would like to automate this process as a step in my continuous integration setup. Does anyone have any experience on this?

Note that each instance in the environment can have multiple roles (CM, Processing, Aggregation combined etc).

Update In the current setup we are using TeamCity to build and deploy the solution. I am looking for a general solution though (e.g. a powershell script), since the setup varies across customers (TeamCity, Jenkins, Octopus, TFS etc.).

  • Well the answer may depend on what tools are you using already for building and deploying your solution. – Szymon Kuzniak Oct 4 '16 at 7:03
  • Aren't we all waiting for Sitecore to provide us with config packages that are suited for each role instead of having to do that based on a spreadsheet.. should be possible to have patches for that. – Gatogordo Oct 4 '16 at 7:38
  • Sitecore 9 brings something like @Gatagordo wished for - it looks to get (almost) as easy as it gets doc.sitecore.net/sitecore_experience_platform/… – Jan Bühler Feb 20 '18 at 13:36
12

I've only ever seen one example of completely automating this process, based off the original Sitecore spreadsheet. It only works off the original; so you would need to expand it for Unicorn and other tools in use.

It relies on PowerShell. (not Sitecore PowerShell Extensions).

Essentially the script functions like this:

  1. Export the sheet into CSV format
  2. Read and parse it with PowerShell
  3. Renames config files to .config/.config.disabled as appropriate

See the Gist from Michael West here. Sitecore Role Configurator.ps1.

Main script reads like this:

<#
    .SYNOPSIS
        The following script prompts the user for a Sitecore server role, then applies the appropriate configurations as specified by the spreadsheet.

    .NOTES
        Michael West
        2016-06-17
#>
$VerbosePreference = "Silentlycontinue"

$configOptions = Import-Csv -Path C:\temp\Config_Enable-Disable_Sitecore_8.1_upd3.csv

$role = Read-Host -Prompt "Which server role would you like to use? (i.e. CD, CM, CMP, PRC, RPT)"
$apppath = "C:\temp\"

foreach($option in $configOptions) {
    $configPath = Join-Path -Path $apppath -ChildPath $option.Filepath

    switch($option.$role) {
        "Enable" {
            Write-Host "Enabling $($option.Filename)"
            $oldConfigPath = Join-Path -Path $configPath -ChildPath ($option.Filename + "*")
            $newConfigPath = Join-Path -Path $configPath -ChildPath $option.Filename
        }

        "Disable" {
            Write-Host "Disabling $($option.Filename)"
            $oldConfigPath = Join-Path -Path $configPath -ChildPath ($option.Filename + "*")
            $newConfigPath = Join-Path -Path $configPath -ChildPath $option.Filename.disabled
        }
        default {
            continue
        }
    }

    if(-not (Test-Path -Path $oldConfigPath)) {
        Write-Verbose "Skipping $($oldConfigPath) because the path does not exist."
        continue
    }

    Move-Item -Path $oldConfigPath -Destination $newConfigPath
}
| improve this answer | |
  • I could create a solution based on this. I will add CSV path as a parameter to support custom CSV's. I also need to support multiple roles, but this is a good start. – Kasper Gadensgaard Oct 4 '16 at 8:43
  • Using the above powershell script doesn't rename files which have 'example' on its extension and are not renamed to 'config'. Not sure if someone faced the same issue. – Vinicius Deschamps Jul 26 '17 at 17:35
6

This is another script, PowerShell Sitecore Role Configurator, that was based on the one from Michael West, but has several enhancements and modifications designed for improved CI-build support and simpler integration into systems like Bamboo, Octopus and TeamCity.

Like the script from Michael West, PowerShell Sitecore Role Configurator takes the Sitecore-provided "Config Enable/Disable Spreadsheet" as a input, but it also includes a tool for testing/debugging the spreadsheet to help you find and fix any inconsistencies that would otherwise break your configuration script.

There are three files that primarily make up PowerShell Sitecore Role Configurator:

I have copied the current version of each file below, for your convenience.

Invoke-SitecoreRoleConfigurator.ps1

This is the main configuration script. You would call this script directly or from your CI-build tool of choice, in order to configure your Sitecore instance.

function Invoke-SitecoreRoleConfigurator {
    Param(
        [Parameter(Mandatory=$True)] [string] $ApplicationFolderPath,
        [Parameter(Mandatory=$True)] [string] $RoleColumn,
        [Parameter(Mandatory=$False)] [string] $CsvSettingsFilePath = ".\ConfigurationCsv.Settings.xml",
        [Parameter(Mandatory=$False)] [string[]] $ExcludedSearchProviders = @(),
        [Parameter(Mandatory=$False)] [string] $CsvFilePath,
        [Parameter(Mandatory=$False)] [string] $FileNameColumn,
        [Parameter(Mandatory=$False)] [string] $FilePathColumn,
        [Parameter(Mandatory=$False)] [string] $DefaultExtensionColumn,
        [Parameter(Mandatory=$False)] [string] $SearchProviderColumn,
        [Parameter(Mandatory=$False)] [switch] $DryRun
    )
    $ErrorActionPreference = "Stop";
    $VerbosePreference = "Silentlycontinue"

    try {
        # make sure that the settings file exists
        if (-not (Test-Path -Path $CsvSettingsFilePath -PathType Leaf)) {
            throw [System.IO.FileNotFoundException] "Settings file '$CsvSettingsFilePath' suppied for parameter 'CsvSettingsFilePath' was not found. Settings file is required for script execution."
        }

        # get the xml
        [xml]$settings = Get-Content -Path $CsvSettingsFilePath

        # returns the value, if not null, or else the default value
        function Get-ValueOrDefault {
            Param([string] $Value, [string] $Default)
            if (!$Value) { return $Default } else { return $Value }
        }

        # apply default values from the XML settings, where needed
        $CsvFilePath = Get-ValueOrDefault $CsvFilePath $settings.Parameters.CsvFilePath;
        $FileNameColumn = Get-ValueOrDefault $FileNameColumn $settings.Parameters.FileNameColumnHeader
        $FilePathColumn = Get-ValueOrDefault $FilePathColumn $settings.Parameters.FilePathColumnHeader
        $DefaultExtensionColumn = Get-ValueOrDefault $DefaultExtensionColumn $settings.Parameters.DefaultExtensionColumnHeader
        $SearchProviderColumn = Get-ValueOrDefault $SearchProviderColumn $settings.Parameters.SearchProviderColumnHeader

        # trim values in excluded search providers
        $ExcludedSearchProviders = $ExcludedSearchProviders | ForEach-Object { $_.Trim() }

        # performs enabling/disabling of file by changing extension
        function Rename-ConfigFile {
            Param(
                [Parameter(Mandatory=$True)] [string] $ExistingName,
                [Parameter(Mandatory=$True)] [string] $TargetName,
                [Parameter(Mandatory=$False)] [string] $ContainerPath
            )

            # if a container path was supplied then join the path to the file names
            if ($ContainerPath) {
                $ExistingName = Join-Path -Path $ContainerPath -ChildPath $ExistingName
                $TargetName = Join-Path -Path $ContainerPath -ChildPath $TargetName
            }

            if(-not (Test-Path -Path $ExistingName)) {
                Write-Host "Skipping $ExistingName because the path does not exist."
                continue
            }

            # don't apply changes if this is DryRun mode
            if (-not $DryRun) {
                Move-Item -Path $ExistingName -Destination $TargetName
            }
        }

        # disables the file by changing the extension
        function Rename-DisableConfigFile {
            Param(
                [Parameter(Mandatory=$True)] [string] $ExistingName,
                [Parameter(Mandatory=$True)] [string] $ContainerPath
            )

            # change the extension the file
            $targetName = $fileName + ".disabled";

            # perform the renaming
            Rename-ConfigFile -ExistingName $ExistingName -TargetName $targetName -ContainerPath $ContainerPath
            Write-Host "`tFile successfully renamed to: $targetName"
        }

        # enables the file by changing the extension
        function Rename-EnableConfigFile {
            Param(
                [Parameter(Mandatory=$True)] [string] $ExistingName,
                [Parameter(Mandatory=$True)] [string] $DefaultExtension,
                [Parameter(Mandatory=$True)] [string] $ContainerPath
            )

            # remove the default extension
            $targetName = $ExistingName.Substring(0, $ExistingName.Length - $DefaultExtension.Length);

            # don't add '.config' if it's already there, e.g. if the file was originally 'Foo.config.disabled'
            if (!$targetName.EndsWith(".config")) {
                $targetName += ".config"
            }

            Rename-ConfigFile -ExistingName $ExistingName -TargetName $targetName -ContainerPath $ContainerPath
        }

        # import and parse the CSV
        $csvTable = Import-Csv -Path $CsvFilePath

        # loop through each row and enable/disable the file based on the given settings
        foreach($csvTableRow in $csvTable) {
            # get the full path to the config file's parent directory
            $configPath = Join-Path -Path $ApplicationFolderPath -ChildPath $csvTableRow.$FilePathColumn.Trim()
            $fileName = $csvTableRow.$FileNameColumn.Trim()
            $defaultExtension = ".$($csvTableRow.$DefaultExtensionColumn)".Trim()

            # enable, disable or skip based on the given role
            switch($csvTableRow.$RoleColumn.Trim()) {
                "Enable" {
                    Write-Host "Enable: $fileName"  

                    # before we enable the config, make sure that it is not for an excluded search provider
                    if ($ExcludedSearchProviders -contains $csvTableRow.$SearchProviderColumn.Trim()) {
                        if ($defaultExtension -eq ".config") {
                            Write-Host "`tDisabling file '$fileName'. File is for an excluded search provider." 
                            # disable the file
                            Rename-DisableConfigFile -ExistingName $fileName -ContainerPath $configPath
                        }
                        Write-Host "`tSkipping file '$fileName'. File is for an excluded search provider."
                    } else {
                        # there is an issue with the CSV - we could probably work around it, but better to have someone look at it
                        if (!$fileName.EndsWith($defaultExtension)) {
                            throw [System.Exception] "File '$fileName' does not have the expected extension '$defaultExtension'."
                        # file must have default extension - if it's not yet enabled then enable it
                        } elseif ($defaultExtension -ne ".config") {
                            Write-Host "`tRenaming $fileName to enable it"
                            Rename-EnableConfigFile -ExistingName $fileName -DefaultExtension $defaultExtension -ContainerPath $configPath
                        } else {
                            Write-Host "`tSkipping '$fileName' as it is already enabled"
                        }
                    }
                }
                "Disable" {
                    Write-Host "Disable: $fileName"

                    # there is an issue with the CSV - we could probably work around it, but better to have someone look at it
                    if (!$fileName.EndsWith($defaultExtension)) {
                        throw [System.Exception] "File '$fileName' does not have the expected extension '$defaultExtension'."
                    # if the file is enabled then disable it (we don't care about 'example' files, since they're already disabled)
                    } elseif ($defaultExtension -eq ".config") {
                        Write-Host "`tRenaming $fileName to disable it"
                        # disable the file
                        Rename-DisableConfigFile -ExistingName $fileName -ContainerPath $configPath
                    } else {                        
                        Write-Host "`tSkipping '$fileName' as it is already disabled"
                    }
                }
                default {
                }
            }
        }
    } catch {
        Write-Host "ERROR: An error was encountered and script execution was terminated. Details below:"

        $ErrorMessage = $_.Exception | Format-List -force
        Write-Output $ErrorMessage

        # if we are running in DryRun mode then we don't want to close the window
        if (-not $DryRun) {
            exit(1)
        }
    }
}

ConfiguationCsv.Settings.xml

This settings file is used for storing and sharing the information about the Sitecore-provided spreadsheet that the configuration script needs in order to correctly process the file and configure your instance(s). The file can be added to your repo and saved for repeated use. Each setting can also be overridden via invocation arguments of the configuration script.

<?xml version="1.0"?>
<!-- PARAMETERS 
       Defines settings to be used by the role configurator script and the
       included testing tool -->
<Parameters>
    <!-- CSV FILE Path
           Path to the .CSV file converted from Sitecore's Excel file that 
           should be used for configuring the base Sitecore instance
           Note: the original Excel file downloaded from Sitecore should 
                 be converted to CSV and any extranous rows or content 
                 that does not belong to the table data should be removed
                 (e.g. introductory titles or content, blank rows above 
                 table, etc.) -->
    <CsvFilePath>Config_Enable-Disable_Sitecore_8.1_upd3.csv</CsvFilePath>
    <!-- ROLE COLUMN HEADERS
           Defines the names of each column in the CSV that represents
           the settings for a role -->
    <RoleColumnHeaders>
        <RoleColumnHeader>Content Delivery (CD)</RoleColumnHeader>
        <RoleColumnHeader>Content Management (CM)</RoleColumnHeader>
        <RoleColumnHeader>Processing</RoleColumnHeader>
        <RoleColumnHeader>CM + Processing</RoleColumnHeader>
        <RoleColumnHeader>Reporting</RoleColumnHeader>
    </RoleColumnHeaders>
    <!-- FILE NAME COLUMN HEADER
           The name of the column in the CSV that contains the config 
           file name -->
    <FileNameColumnHeader>Config file name</FileNameColumnHeader>
    <!-- FILE PATH COLUMN HEADER
           The name of the column in the CSV that contains the config 
           file's directory path -->
    <FilePathColumnHeader>File path</FilePathColumnHeader>
    <!-- DEFAULT EXTENSION COLUMN HEADER
           The name of the column in the CSV that contains the config
           file's default extension (config, disabled, example) -->
    <DefaultExtensionColumnHeader>Config type</DefaultExtensionColumnHeader>
    <!-- SEARCH PROVIDER COLUMN HEADER
           The name of the column in the CSV that contains the search
           provider that the config file should be included for -->
    <SearchProviderColumnHeader>Search Provider Used</SearchProviderColumnHeader>
</Parameters>

Test-ConfigurationCsv.ps1

This is the configuration testing tool, which helps you to identify bugs/inconsistencies made by Sitecore in their provided configuration spreadsheet. This tool calls the configuration script in dryrun mode, meaning that the script runs through all of the logic but no changes are actually made.

[CmdletBinding()]
Param(
  [Parameter(Mandatory=$False)] [string] $CsvSettingsFilePath = ".\ConfigurationCsv.Settings.xml",
  [Parameter(Mandatory=$False)] [string] $ConfiguratorScriptFilePath = ".\Invoke-SitecoreRoleConfigurator.ps1"
)

# stop execution on all errors
$ErrorActionPreference = "Stop";
$VerbosePreference = "Silentlycontinue"

Write-Host "`r`nCONFIGURATION CSV TESTING TOOL`r`n`r`n"

Write-Host "To use this tool, ensure that you have configured the relevant settings in the $CsvSettingsFilePath file. The test will run once for each of the Roles that are added in the settings file. If any errors are encountered, resolve them in the CSV file and re-run the tool.`r`n" 
Write-Host "Note that the most common error occurs when a 'Config Type' and 'Config File Name' in the CSV does not match up for a particular file (i.e. the file is of type 'disabled' but its name ends in '.config' or '.example'). For these issues, find the referenced file in the unmodified Sitecore source and update the CSV according to what you find.`r`n"
Write-Host "This tool runs in DryRun mode, only. No actual file-system operations will be performed`r`n`r`n"

Read-Host "Press [ENTER] to execute the tool"

# get the xml
[xml]$settings = Get-Content -Path $CsvSettingsFilePath

Write-Host "Executing tests..."

if (-not (Test-Path -Path $ConfiguratorScriptFilePath -PathType Leaf)) {
  throw [System.IO.FileNotFoundException] "File '$ConfiguratorScriptFilePath' specified for parameter 'ConfiguratorScriptFilePath' was not found. This file is required and should be the file containing the Invoke-SitecoreRoleConfigurator commandlet."
}

# dot-source the script containing the Invoke-SitecoreRoleConfigurator commandlet so that we can call it
. $ConfiguratorScriptFilePath

# run the test for each role defined in the settings, 
foreach ($role in $settings.Parameters.RoleColumnHeaders.RoleColumnHeader)  {
  # invoke with the DryRun flag to ensure that no changes are actually made; ApplicationFolderPath can be fake since we are running in DryRun mode (no changes will actually be made)
  Invoke-SitecoreRoleConfigurator -ApplicationFolderPath "C:\fake" -RoleColumn "$role" -CsvSettingsFilePath "$CsvSettingsFilePath" -DryRun 
}

# script will stop execution on all errors, so if we get here then no errors were encountered
Write-Host "`r`nTEST COMPLETED SUCCESSFULLY!`r`n"
| improve this answer | |
  • Really nice script - please upvote some. One little gotcha: My Excel generated a ssv (semicolon seperated value) when I created the csv, so watch out for that. – Jan Bühler Feb 21 '18 at 10:17
3

If you can provide a lists of files which have to be enabled per environment the easiest way would be to have a scrip which would parse the file corresponding to the environment it runs on and enable the files needed.

  1. Have all the configs disabled
  2. Have a set of files with configs to be enabled. The files have to easily identifiable (eg. configs.CM.txt, configs.CD.txt, configs.Processing.txt, ...)
  3. Have a script which will accept parameter (CM, CD, Processing, ...) which will go through and enable all necessary configs
| improve this answer | |
  • 1
    Thanks for your comment. I would prefer a solution where the script can parse the official spreadsheet, so i do not have to maintain the files manually. This is just up to the implementation of course, but maybe someone has some hands on experience with a concrete tool. – Kasper Gadensgaard Oct 4 '16 at 7:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.