﻿#### 

Working with Sitecore we usually have a multi environment infrastructure. For example, DEV, Acceptance and Production environments. Potentially, each of them require some environment specific values in web.config. In one of my projects, I had to extend Content-Security-Policy setting with additional URL:

| &lt;location path="sitecore"&gt;<br>
                &lt;system.webServer&gt;<br>
                  &lt;httpProtocol&gt;<br>
                    &lt;customHeaders&gt;<br>
                      &lt;add name="Content-Security-Policy"               value="default-src 'self' 'unsafe-inline' 'unsafe-eval' https://rendering-dev.sitename.com; img-src 'self' data:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' 'unsafe-inline' https://fonts.gstatic.com; upgrade-insecure-requests; block-all-mixed-content;"/&gt;<br>
                    &lt;/customHeaders&gt;<br>
                  &lt;/httpProtocol&gt;<br>
                &lt;/system.webServer&gt;<br>
              &lt;/location&gt; |
| --- |

Where **https://rendering-dev.sitename.com** is an environment specific hostname.

First of all I decided to use web XDT transforms like *web.config.DEV.xdt*, *web.config.ACC.xdt* and *web.config.PROD.xdt* and apply them on docker-compose build time. Where DEV, ACC and PROD is a value of an environment variable passed to the Dockerfile. And it works pretty fine when we build our solution locally. But when I started to prepare Build Pipelines in Azure DevOps, I noticed that I don't have an option to specify variables per environment. Because a Build Pipeline doesn’t have Stages and we are not able to link different variable libraries to corresponding stages. Once transformations are applied during a build time, not runtime, I can’t move this operation to Release Pipelines.

I found two ways to solve the issue I faced:

- Create a separate Build Pipelines for each environment (Stage) which is definitely an option. But I don’t like it because in this case I need to support three almost the same pipelines instead of one
- Use an environment variable in web.config instead of patching exact value.

By default, we can’t use environment variables in web.config, but since .NET Framework 4.7.1 we can utilize configuration builders which provide a modern and agile mechanism for ASP.NET apps to get configuration values from external sources. One of predefined config builders is used to read values from system environment variables. More info about config builders [here](https://docs.microsoft.com/en-us/aspnet/config-builder).

To align it with docker (k8s) build approach we should do the following:

- We need to add a new *configBuilder* to our *web.config*. The easiest way to do that is to utilize XDT transforms in one of our projects:

| &lt;configBuilders&gt;<br>
                &lt;builders&gt;<br>
                  &lt;add name="environment" mode="Expand" type="Microsoft.Configuration.ConfigurationBuilders.EnvironmentConfigBuilder, Microsoft.Configuration.ConfigurationBuilders.Environment" <br>
                       xdt:Transform="InsertIfMissing" xdt:Locator="Match(name)" /&gt;<br>
                &lt;/builders&gt;<br>
            &lt;/configBuilders&gt; |
| --- |

Code above adds (if missing) a basic key/value Configuration Builder, called “environment”,  for the .Net Framework that draws from environment variables.

- Update desired web.config property with currently introduced builder and variables. For example:

| &lt;location path="sitecore"&gt;<br>
                &lt;system.webServer&gt;<br>
                  &lt;httpProtocol&gt;<br>
                    &lt;customHeaders configBuilders="environment" xdt:Transform="SetAttributes(configBuilders)"&gt;<br>
                      &lt;add name="Content-Security-Policy" xdt:Transform="Replace" xdt:Locator="Match(name)"<br>
                           value="default-src 'self' 'unsafe-inline' 'unsafe-eval' ${RENDERING\_HOST\_PUBLIC\_URI}; img-src 'self' data:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' 'unsafe-inline' https://fonts.gstatic.com; upgrade-insecure-requests; block-all-mixed-content;"/&gt;<br>
                    &lt;/customHeaders&gt;<br>
                  &lt;/httpProtocol&gt;<br>
                &lt;/system.webServer&gt;<br>
              &lt;/location&gt; |
| --- |

In the code above we have set the environment config builder for the whole customHeaders. Which means the variables that this section has, will be read from:

- The values in the web.config file if the keys are not set in environment variables.
- The values of the environment variable, if set.

Finally, my web.config.xdt looks like below:

| &lt;?xml version="1.0"?&gt;<br>
            &lt;configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"&gt;<br>
              &lt;configBuilders&gt;<br>
                &lt;builders&gt;<br>
                  &lt;add name="environment" mode="Expand" type="Microsoft.Configuration.ConfigurationBuilders.EnvironmentConfigBuilder, Microsoft.Configuration.ConfigurationBuilders.Environment" <br>
                       xdt:Transform="InsertIfMissing" xdt:Locator="Match(name)" /&gt;<br>
                &lt;/builders&gt;<br>
              &lt;/configBuilders&gt;<br>
              &lt;location path="sitecore"&gt;<br>
                &lt;system.webServer&gt;<br>
                  &lt;httpProtocol&gt;<br>
                    &lt;customHeaders configBuilders="environment" xdt:Transform="SetAttributes(configBuilders)"&gt;<br>
                      &lt;add name="Content-Security-Policy" xdt:Transform="Replace" xdt:Locator="Match(name)"<br>
                           value="default-src 'self' 'unsafe-inline' 'unsafe-eval' ${RENDERING\_HOST\_PUBLIC\_URI}; img-src 'self' data:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' 'unsafe-inline' https://fonts.gstatic.com; upgrade-insecure-requests; block-all-mixed-content;"/&gt;<br>
                    &lt;/customHeaders&gt;<br>
                  &lt;/httpProtocol&gt;<br>
                &lt;/system.webServer&gt;<br>
              &lt;/location&gt;<br>
            &lt;/configuration&gt; |
| --- |

The last thing left is to apply the transform file to the actual web.config placed in a docker image. The code below needs to be added to the Docker file of CM and CD instances:

| RUN Get-ChildItem C:\transforms\solution\\*\\*\platform\web.config.xdt | ForEach-Object { & C:\tools\scripts\Invoke-XdtTransform.ps1 -Path .\web.config -XdtPath $\_.FullName } |
| --- |

Path to the web.config.xdt can be different in your solution. As well as we have to make sure that transform files are copied from solution to the solution image in its build stage.

Don’t forget to pass the newly added environment variables to the container with your  image.

####

###### More from author

[##### Deploy custom headless Sitecore solution in Sitecore XM Cloud
November 10, 2022](https://www.brimit.com/blog/deploy-custom-headless-sitecore-solution-in-sitecore-xm-cloud)[##### Running custom next.js editing host in Sitecore XM Cloud
September 29, 2022](https://www.brimit.com/blog/running-custom-next-js-editing-host-in-sitecore-xm-cloud)[##### Running node.js based rendering host in Sitecore Managed Cloud
August 3, 2022](https://www.brimit.com/blog/running-node-js-based-rendering-host-in-sitecore-managed-cloud)

###### Author

[!\[artsiom-photo\](https://www.brimit.com/-/jssmedia/feature/blogs/authors/artsiom-200.jpg?h=202&amp;iar=0&amp;w=200&amp;hash=41797D2540DF6EB6FDF558360F6F62B8)
Artsem Prashkovich
Sitecore MVP/ Solution Architect](https://www.brimit.com/blog/author?authors=Artsem%20Prashkovich)

###### More by category

[#Events](https://www.brimit.com/blog?categories=#Events)[#How-to](https://www.brimit.com/blog?categories=#How-to)[#News](https://www.brimit.com/blog?categories=#News)[#Guides](https://www.brimit.com/blog?categories=#Guides)

###### More by platform

[DXP](https://www.brimit.com/blog?platforms=DXP)[Sales and marketing automation](https://www.brimit.com/blog?platforms=Sales%20and%20marketing%20automation)[Application innovation](https://www.brimit.com/blog?platforms=Application%20innovation)

#### More on Sitecore

[!\[How Vercel Will Help You Save Effort When Deploying Sophisticated Sitecore Projects\](https://www.brimit.com/-/jssmedia/project/brimit/blog/2024/vercel_cover-image.png)
#Guides#How-toDXPE-commerce
##### How Vercel Will Help You Save Effort When Deploying Sophisticated Sitecore Projects
Optimize and accelerate the development and deployment of complex multisite Sitecore projects.
Alexei Vershalovich on July 17, 2024](https://www.brimit.com/blog/how-vercel-will-help-you-save-effort-when-deploying-sophisticated-sitecore-projects)

[!\[Training Up Tomorrow's Sitecore MVPs: a Mentoring Success Story\](https://www.brimit.com/-/jssmedia/project/brimit/blog/2023/sitecore-mentoring---cover-image.png)
#How-toDXP
##### Training Up Tomorrow's Sitecore MVPs: a Mentoring Success Story
How to participate in the Sitecore Mentor program and help younger colleagues jump-start a career in Sitecore development.
Sergey Baranov on October 2, 2023](https://www.brimit.com/blog/training-up-tomorrows-sitecore-mvps)

[!\[Going Headless. Part 2: When a Headless CMS Is Your Best Bet (if you have Sitecore)\](https://www.brimit.com/-/jssmedia/project/brimit/blog/2022/headless/adobestock_456986731.jpg)
#How-toDXPE-commerce
##### Going Headless. Part 2: When a Headless CMS Is Your Best Bet (if you have Sitecore)
Discover how a headless CMS can benefit organizations that use Sitecore.
Daniil Raschupkin, Palina Trokhautsava on September 15, 2022](https://www.brimit.com/blog/going-headless-part-2-when-a-headless-cms-is-your-best-bet-if-you-have-sitecore)

![](https://bat.bing.net/action/0?ti=187017043&amp;tm=gtm002&amp;Ver=2&amp;mid=b6471e6f-e7b3-45c2-8375-6958bd54214a&amp;bo=2&amp;gtm_tag_source=1&amp;pi=0&amp;lg=en-US&amp;sw=800&amp;sh=600&amp;sc=24&amp;nwd=1&amp;tl=Using%20environment%20variables%20in%20web.config%20instead%20xdt%20transforms&amp;kw=Kubernetes,AKS,XDT,Sitecore,Transfarms,Veriables&amp;p=https%3A%2F%2Fwww.brimit.com%2Fblog%2Fusing-environment-variables-in-web-config-instead-of-xdt&amp;r=&amp;lt=274&amp;evt=pageLoad&amp;sv=2&amp;asc=D&amp;cdb=AQAY&amp;rn=648074)