---
title: "Applying configuration transforms"
slug: "applying-configuration-transforms"
abstract: "Explains how you apply XDT-based configuration transforms."
---
# Applying 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](https://docs.microsoft.com/en-us/previous-versions/aspnet/dd465326(v=vs.110)) 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.

<details>
<summary><h2>Clone the Docker Examples repository</h2></summary>

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.

</details>

<details>
<summary><h2>Example preparation</h2></summary>

The *custom-images* example requires some preparation before you can run it. If you have not already done so, either follow the [preparation steps](https://doc.sitecore.com/xp/en/developers/101/developer-tools/run-your-first-sitecore-instance.html "Run your first Sitecore instance") 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:

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

</details>

<details>
<summary><h2>The Docker Examples transform files</h2></summary>

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
    <?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
    <?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](https://en.wikipedia.org/wiki/Idempotence#Computer_science_meaning).
>
> The example applies solution transforms first, then role transforms.

</details>

<details>
<summary><h2>Solution transforms</h2></summary>

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](https://helix.sitecore.net/) 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:

```text
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`:

```text
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\`:

    ```text
    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](https://helix.sitecore.net/), 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.

</details>

<details>
<summary><h2>Role transforms</h2></summary>

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\`:

```text
COPY .\transforms\ \transforms\role\

```

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

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

```

</details>

<details>
<summary><h2>Run Docker Examples solution</h2></summary>

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:

    ```text
    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 *XP**0*.

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

    - Sitecore Content Management (cm): [https://cm.dockerexamples.localhost](https://cm.dockerexamples.localhost)
    - Sitecore Content Delivery (cd): [https://cd.dockerexamples.localhost](https://cd.dockerexamples.localhost)
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`.

</details>

<details>
<summary><h2>Apply updates to running containers</h2></summary>

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:

    ```text
    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:

    ```text
    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/](https://webconfigtransformationtester.apphb.com/) to shorten the feedback loop.

</details>

<details>
<summary><h2>Further reading</h2></summary>

- [Microsoft XDT Transformation Syntax](https://docs.microsoft.com/en-us/previous-versions/aspnet/dd465326(v=vs.110))
- [Dockerfile reference](https://docs.docker.com/engine/reference/builder/)

</details>