Applying configuration transforms

Abstract

Explains how you apply XDT-based configuration transforms.

Sitecore implementations often require modifications of configuration files that you cannot change with Sitecore configuration patching, such as Web.config, ConnectionStrings.config, Domains.config, Layers.config, and others. In these cases, you use an XDT transform file instead.

This topic shows how you apply these XDT-based configuration transforms when you build your Sitecore Docker images. The transform files can be located in your solution or the can be local to the Dockerfile for a particular Sitecore role. The example uses a Sitecore Experience Management (XM1) instance.

If you have not already done so, clone the Docker Examples repository to a location on your machine, such as C:\sitecore\docker-examples\ (the example uses this folder). You use the custom-images folder in the example.

The custom-images example requires some preparation before you can run it. If you have not already done so, either follow the preparation steps or run the included init.ps1 script to perform these preparation steps automatically:

  • Open a PowerShell administrator prompt, navigate to the custom-images folder, and run this command, replacing the -LicenseXmlPath with the location of your Sitecore license file:

    .\init.ps1 -LicenseXmlPath C:\License\license.xml

The Docker Examples solution contains two XDT configuration transform files for the Web.config where a custom Docker-Examples HTTP header is manipulated. Navigate to the custom-images folder, and look at the following transform files:

  • \src\DockerExamples.Website\Web.config.xdt

    Because this transform file is located in the solution, it is applied to all core Sitecore roles (CM and CD in an XM1 topology). This is an example of a solution transform. Here, the Docker-Examples HTTP header is added and it is set to Solution transform:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
      <system.webServer>
        <httpProtocol>
          <customHeaders>
            <add name="Docker-Examples" xdt:Locator="Match(name)" value="Solution transform" xdt:Transform="InsertIfMissing" />
          </customHeaders>
        </httpProtocol>
      </system.webServer>
    </configuration>
  • \docker\build\cm\transforms\Web.config.xdt

    This transform file is located in the docker\build folder, local to the cm service. This is an example of a role transform. It changes the Docker-Examples HTTP header to Role transform:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
      <system.webServer>
        <httpProtocol>
          <customHeaders>
            <add name="Docker-Examples" xdt:Locator="Match(name)" value="Role transform" xdt:Transform="SetAttributes(value)" />
          </customHeaders>
        </httpProtocol>
      </system.webServer>
    </configuration>

Tip

It is best practice to take order of operation into consideration when you apply multiple transforms. However, because transforms are applied to a fresh config file every time, there is no need to ensure they are idempotent.

The example applies solution transforms first, then role transforms.

You store configuration transforms that apply to all core Sitecore roles directly in the solution structure.

The following example uses a dedicated solution build artifact to collect and apply solution transforms. The advantage of this approach is that it supports multiple transforms for the same config file, and this is often used in Sitecore Helix solutions.

For example, you can have multiple Web.config transforms among the layers:

  • \src\Foundation\[Module Name]\website\Web.config.xdt

  • \src\Feature\[Module Name]\website\Web.config.xdt

  • \src\Project\[Module Name]\website\Web.config.xdt

In this setup, if you include all these in the build output (thats is: Build Action is set to Content), only one ends up in the output and ultimately gets applied. Instead, each of these files can be excluded (that is Build Action is set to None), and they are collected separately in the solution build image.

You can also to keep the transform files in the main build output and perform the transforms in-place in the web root. However, this does not support multiple transforms for the same config file.

Configure in solution build

Navigate to the custom-images folder, and look at the Dockerfile that is there.

You' can see that the transform files (.xdt extension) are collected within the builder stage and dropped at C:\out\transforms. The use of robocopy (along with the /s flag) is important here because it preserves the folder structure:

RUN Invoke-Expression 'robocopy C:\build\src C:\out\transforms /s /ndl /njh /njs *.xdt'

Note this is done before the msbuild so you do not have to pick up extra .xdt files that have not been excluded from build output. Then these files are copied in from the builder stage to the final image with this structure: \artifacts\transforms:

COPY --from=builder C:\out\transforms .\transforms\

Apply to Sitecore runtime images

To apply transforms to runtime images:

  1. Open the Sitecore runtime Dockerfile for the cm service. You can see the solution transforms just collected copied in, landing at \transforms\solution\:

    COPY --from=solution \artifacts\transforms\ \transforms\solution\
  2. Development tools are copied in from the tooling image (to C:\tools), and the Invoke-XdtTransform.ps1 script is used to apply the transforms:

    COPY --from=tooling \tools\ \tools\
    RUN C:\tools\scripts\Invoke-XdtTransform.ps1 -Path .\ -XdtPath C:\transforms\solution\DockerExamples.Website

The Invoke-XdtTransform.ps1 script accepts two folders for the -Path and -XdtPath parameters. When using folders:

  • the folder structure of the -XdtPath must match -Path

  • transform files located in -XdtPath must be named to match the config, with an added .xdt file extension

In this case, -Path is the current WORKDIR of C:\inetpub\wwwroot, and -XdtPath is the root of our single Visual Studio Website project.

The Helix solution example

The Docker Examples solution is a simple example with a single Website project. In a real-world solution that follow the Sitecore Helix practices, the transform commands would need to be adjusted to reflect the nested folder structure and layer priority (such as Project, Feature, Foundation):

RUN Get-ChildItem C:\transforms\solution\Foundation\*\website | ForEach-Object { & C:\tools\scripts\Invoke-XdtTransform.ps1 -Path .\ -XdtPath $_.FullName }; `
    Get-ChildItem C:\transforms\solution\Feature\*\website | ForEach-Object { & C:\tools\scripts\Invoke-XdtTransform.ps1 -Path .\ -XdtPath $_.FullName }; `
    Get-ChildItem C:\transforms\solution\Project\*\website | ForEach-Object { & C:\tools\scripts\Invoke-XdtTransform.ps1 -Path .\ -XdtPath $_.FullName };

Alternative: Use transform files in main build output

You can keep the transform files with all other files in the main build output. The disadvantage to this approach is that it does not support multiple transforms for the same config file, but it can be your only option if you do not have a solution build Dockerfile and image and instead rely on a more traditional build.

The following is an example:

RUN $xdts = [System.Collections.ArrayList]@(); `
    $xdts.AddRange(@(Get-ChildItem -Path .\*.xdt)); `
    $xdts.AddRange(@(Get-ChildItem -Path .\App_Config\*.xdt -Recurse)); `
    $xdts | ForEach-Object { & C:\tools\scripts\Invoke-XdtTransform.ps1 -Path $_.FullName.Replace('.xdt', '') -XdtPath $_.FullName }; `
    $xdts | ForEach-Object { Remove-Item -Path $_.FullName };

The Invoke-XdtTransform.ps1 script also accepts matching configuration and transform files as parameters.

This example looks for .xdt files in the web root and App_Config folder, passes them through the Invoke-XdtTransform.ps1 script, and then deletes the .xdt files.

You can store configuration transforms that only apply to a specific Sitecore role inside the dedicated docker\build folder of that role.

Add to docker\build folder

Navigate to the docker\build folder for the cm service. Note there is an extra transforms folder for this role:

build
    cm
        transforms
            Web.config.xdt
        Dockerfile

There is a single Web.config.xdt transform, but this transform can contain any other transforms necessary for the cm role. As is the case with solution transforms, you structure the transform files to match the folder structure of your target.

You then apply these transforms in the Sitecore runtime Dockerfile for the role.

Apply to Sitecore runtime image

You can see in the Sitecore runtime Dockerfile for the cm service that the transforms folder contents are copied in from the Docker build context, landing at \transforms\role\:

COPY .\transforms\ \transforms\role\

The transforms are then applied after solution transforms, using the same Invoke-XdtTransform.ps1 script:

RUN C:\tools\scripts\Invoke-XdtTransform.ps1 -Path .\ -XdtPath C:\transforms\role

To run the Docker Examples solution:

  1. Open the Sitecore runtime Dockerfile for the cd service (for example C:\sitecore\docker-examples\custom-images\docker\build\cd\Dockerfile). You can see that the cd service has the solution transforms, but does not have any role transforms.

  2. Open a PowerShell prompt and navigate to the custom-images folder. Run the Docker Examples with the Docker Compose up command:

    docker-compose -f docker-compose.xm1.yml -f docker-compose.xm1.override.yml up -d

    Note

    Note the docker-compose -f docker-compose.xm1.yml -f docker-compose.xm1.override.yml command. Because this example uses the Sitecore Experience Management (XM1) instance, the docker-compose commands are explicitly referencing the xm1 Compose fileswith the -f flag. The default Compose files are XP0.

    You can access Sitecore Experience Management (XM1) containers with the following:

  3. When the instance is up and running, you can use developer tools in your browser to inspect the HTTP headers. You can see the Docker-Examples custom header that is in the transform examples.

    The cm site displays Role transform, while the cd site displays Solution transform.

To apply updates to a running container:

  1. Change to one of the example Web.config.xdt files. For example, change the solution transform value to My transform.

  2. Run the following command to see the change applied in the running containers:

    docker-compose -f docker-compose.xm1.yml -f docker-compose.xm1.override.yml up --build -d

    You can also be more selective and only build the containers that are impacted:

    docker-compose -f docker-compose.xm1.yml -f docker-compose.xm1.override.yml build solution cm cd
    docker-compose -f docker-compose.xm1.yml -f docker-compose.xm1.override.yml up -d

    In both cases, when the up command is called, Docker recreates only the cm and cd containers. The rest of the roles continue running.

  3. When you are finished, stop and remove the containers using the down command:

    docker-compose -f docker-compose.xm1.yml -f docker-compose.xm1.override.yml down

Tip

When you are actively developing configuration transforms, it is helpful to use a transform test tool such as https://webconfigtransformationtester.apphb.com/ to shorten the feedback loop.