Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
How to enable transformations on build with Visual Studio

#Transform web.config on build

  1. Unload the project
  2. Edit .csproj
  3. Append figure 1 to the end of the file just before </Project>; v12.0 my change depending on your version of Visual Studio
  4. Save .csproj and reload
  5. Open configuration manager
  6. Add a new Configuration Name: Base. Copy settings from: Release
  7. Copy the contents of your web.config
  8. Right click Web.Config > Add Config Transformation
  9. Overwrite the web.base.config with the contents of your clipboard
  10. From now on Web.Config will be overwritten using transformations.

For settings that apply to all cofigurations use Base
For settings that apply only to Release use Release
For settings that apply only to Debug use Debug

A helpful note to add to your projects [base|debug|release].config

<!-- WEB CONFIG IN THIS PROJECT SHOULD ONLY BE MODIFIED BY -->
<!-- web.base.config -->
<!-- web.debug.config -->
<!-- web.release.config -->
<!-- CHANGES MADE DIRECTLY TO THE web.config WILL BE OVERWRITTEN -->

####Figure 1 Copy and paste:

<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v12.0\WebApplications\Microsoft.WebApplication.targets" />
<Target Name="BeforeBuild">
    <TransformXml Source="Web.Base.config" Transform="Web.$(Configuration).config" Destination="Web.config" />
</Target>     
@stimpy77

This comment has been minimized.

Show comment Hide comment
@stimpy77

stimpy77 Mar 25, 2015

Found this from the Googlz. Pithy, brief, concise, well-formed, .. bravo.

Found this from the Googlz. Pithy, brief, concise, well-formed, .. bravo.

@stimpy77

This comment has been minimized.

Show comment Hide comment
@stimpy77

stimpy77 Mar 25, 2015

FYI someone said elsewhere (I haven't validated) that in VS2013 (and/or perhaps .NET 4.5.x) you don't need the <Import ..> tag addition.

FYI someone said elsewhere (I haven't validated) that in VS2013 (and/or perhaps .NET 4.5.x) you don't need the <Import ..> tag addition.

@andriy-f

This comment has been minimized.

Show comment Hide comment
@andriy-f

andriy-f May 29, 2015

Threre are some NuGet packages that are modifying Web.config directly. In this case it's important not to forget to copy those changes to Web.base.config

Threre are some NuGet packages that are modifying Web.config directly. In this case it's important not to forget to copy those changes to Web.base.config

@stimpy77

This comment has been minimized.

Show comment Hide comment
@stimpy77

stimpy77 Jul 14, 2015

Automating and putting into nested folder. Thanks again. https://github.com/stimpy77/FastKoala

Automating and putting into nested folder. Thanks again. https://github.com/stimpy77/FastKoala

@educit

This comment has been minimized.

Show comment Hide comment
@educit

educit Feb 23, 2016

I used Visual Studio 2015 and as stimpy77 said we don't need the <Import ...> tag addition.
Instead it will likely be located under this:

educit commented Feb 23, 2016

I used Visual Studio 2015 and as stimpy77 said we don't need the <Import ...> tag addition.
Instead it will likely be located under this:

@JohnLBevan

This comment has been minimized.

Show comment Hide comment
@JohnLBevan

JohnLBevan Jul 16, 2016

FYI: To have this applied during the build rather than deploy phase, use the Slow Cheetah plugin; more info here: http://stackoverflow.com/a/8841094/361842

FYI: To have this applied during the build rather than deploy phase, use the Slow Cheetah plugin; more info here: http://stackoverflow.com/a/8841094/361842

@nordquist

This comment has been minimized.

Show comment Hide comment
@nordquist

nordquist Jul 26, 2016

Why use base? The following should work just fine:
<TransformXml Source="Web.config" Transform="Web.$(Configuration).config" Destination="Web.config" />

Why use base? The following should work just fine:
<TransformXml Source="Web.config" Transform="Web.$(Configuration).config" Destination="Web.config" />

@glcheetham

This comment has been minimized.

Show comment Hide comment
@glcheetham

glcheetham Aug 8, 2016

@nordquist you should use Web.config.base so that you're not committing the Web.config with the (potentially sensitive) transforms into source control

@nordquist you should use Web.config.base so that you're not committing the Web.config with the (potentially sensitive) transforms into source control

@wilson0x4d

This comment has been minimized.

Show comment Hide comment
@wilson0x4d

wilson0x4d Sep 17, 2016

@nordquist yeah it's a nifty trick, but i'd ditch the web.base.config too -- it's abnormal, obtuse, and a solves a pebkac problem by introducing something abnormal, obtuse.. "be a rebel, conform;" i say. the existing industry convention is to have a default, and then transform the default for a particular environment/config/purpose. the default configs live in source control, don't require a build step to exist or be valid, and typically meet the needs of local hosting/debugging (e.g. valid for all developers that would need to punch F5 "out of the box" and expect everything to work.)

@nordquist yeah it's a nifty trick, but i'd ditch the web.base.config too -- it's abnormal, obtuse, and a solves a pebkac problem by introducing something abnormal, obtuse.. "be a rebel, conform;" i say. the existing industry convention is to have a default, and then transform the default for a particular environment/config/purpose. the default configs live in source control, don't require a build step to exist or be valid, and typically meet the needs of local hosting/debugging (e.g. valid for all developers that would need to punch F5 "out of the box" and expect everything to work.)

@zleao

This comment has been minimized.

Show comment Hide comment
@zleao

zleao Oct 20, 2016

Fast and helpful help to achieve web.config transformation on build! Thank you!

zleao commented Oct 20, 2016

Fast and helpful help to achieve web.config transformation on build! Thank you!

@ivanpointer

This comment has been minimized.

Show comment Hide comment
@ivanpointer

ivanpointer Nov 11, 2016

I think the main problem with transforming the Web.config file directly, is that the file handle is still open by the TransformXml task for read, when it tries to write it. The solution, is to add an extra copy (and delete) step in, to avoid the file handle collision:

  <!-- First, we extend the "BuildDependsOn" property with our custom target for applying the transform.
This is a cleaner/safer alternative to overloading the "AfterBuild" target: -->
  <PropertyGroup>
    <BuildDependsOn>
      $(BuildDependsOn);
      _VisualStudioApplyTransform;
    </BuildDependsOn>
  </PropertyGroup>

  <!-- Now, down to business: this is our target for applying the config transform.
I've included some conditions, to help avoid the build from blowing up when a transform
doesn't exist for the current configuration: -->

  <Target Name="_VisualStudioApplyTransform">
    <!-- Transform the file out to a temp file, sourcing from our Web.config file: -->
    <TransformXml Source="Web.config" Transform="Web.$(Configuration).config" Destination="Web.config.temp"
                  Condition="Exists('Web.$(Configuration).config')" />

    <!-- Copy the temp file, back over the top of Web.config, the file handle opened by TransformXml is closed now: -->
    <Copy SourceFiles="Web.config.temp" DestinationFiles="Web.config"
          Condition="Exists('Web.config.temp')" />

    <!-- Cleanup after ourselves: -->
    <Delete Files="Web.config.temp" Condition="Exists('Web.config.temp')" />
  </Target>

I think the main problem with transforming the Web.config file directly, is that the file handle is still open by the TransformXml task for read, when it tries to write it. The solution, is to add an extra copy (and delete) step in, to avoid the file handle collision:

  <!-- First, we extend the "BuildDependsOn" property with our custom target for applying the transform.
This is a cleaner/safer alternative to overloading the "AfterBuild" target: -->
  <PropertyGroup>
    <BuildDependsOn>
      $(BuildDependsOn);
      _VisualStudioApplyTransform;
    </BuildDependsOn>
  </PropertyGroup>

  <!-- Now, down to business: this is our target for applying the config transform.
I've included some conditions, to help avoid the build from blowing up when a transform
doesn't exist for the current configuration: -->

  <Target Name="_VisualStudioApplyTransform">
    <!-- Transform the file out to a temp file, sourcing from our Web.config file: -->
    <TransformXml Source="Web.config" Transform="Web.$(Configuration).config" Destination="Web.config.temp"
                  Condition="Exists('Web.$(Configuration).config')" />

    <!-- Copy the temp file, back over the top of Web.config, the file handle opened by TransformXml is closed now: -->
    <Copy SourceFiles="Web.config.temp" DestinationFiles="Web.config"
          Condition="Exists('Web.config.temp')" />

    <!-- Cleanup after ourselves: -->
    <Delete Files="Web.config.temp" Condition="Exists('Web.config.temp')" />
  </Target>
@urig

This comment has been minimized.

Show comment Hide comment
@urig

urig Feb 13, 2017

First off, thanks for sharing this. It's awesome.
Does this "play nice" with "publish time" config transforms? Having applied @stimpy77's https://github.com/stimpy77/FastKoala to a project, it looks to me that the transforms are not running when right-clicking the project and publishing it to Azure. Is there something I'm missing?

urig commented Feb 13, 2017

First off, thanks for sharing this. It's awesome.
Does this "play nice" with "publish time" config transforms? Having applied @stimpy77's https://github.com/stimpy77/FastKoala to a project, it looks to me that the transforms are not running when right-clicking the project and publishing it to Azure. Is there something I'm missing?

@cssmonauts

This comment has been minimized.

Show comment Hide comment
@cssmonauts

cssmonauts Mar 3, 2017

In VS Community Update 3, I get an "invalid child element" for TransformXml when using this verbatim... I also tried:

<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\WebApplications\Microsoft.WebApplication.targets" />

to see if that made a difference, but it didn't. I can't seem to find any answers online! Anyone getting the same issue?

In VS Community Update 3, I get an "invalid child element" for TransformXml when using this verbatim... I also tried:

<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\WebApplications\Microsoft.WebApplication.targets" />

to see if that made a difference, but it didn't. I can't seem to find any answers online! Anyone getting the same issue?

@resnyanskiy

This comment has been minimized.

Show comment Hide comment
@resnyanskiy

resnyanskiy Mar 21, 2017

FYI, https://marketplace.visualstudio.com/items?itemName=GolanAvraham.ConfigurationTransform
Based on <UsingTask> element, could be used with .xproj file.

FYI, https://marketplace.visualstudio.com/items?itemName=GolanAvraham.ConfigurationTransform
Based on <UsingTask> element, could be used with .xproj file.

@cemerson

This comment has been minimized.

Show comment Hide comment
@cemerson

cemerson Mar 19, 2018

Awesome thanks for sharing. My VS 2015 version of this (I changed Source to just "Web.config" as I didn't have a "base" version:
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v14.0\WebApplications\Microsoft.WebApplication.targets" /> <Target Name="BeforeBuild"> <TransformXml Source="Web.config" Transform="Web.$(Configuration).config" Destination="Web.config" /> </Target>

Awesome thanks for sharing. My VS 2015 version of this (I changed Source to just "Web.config" as I didn't have a "base" version:
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v14.0\WebApplications\Microsoft.WebApplication.targets" /> <Target Name="BeforeBuild"> <TransformXml Source="Web.config" Transform="Web.$(Configuration).config" Destination="Web.config" /> </Target>

@valbers

This comment has been minimized.

Show comment Hide comment
@valbers

valbers Apr 5, 2018

Thank you. Small correction:
"might change"
https://gist.github.com/valbers/2635267ef83cf976c7fc4b4beb516087

valbers commented Apr 5, 2018

Thank you. Small correction:
"might change"
https://gist.github.com/valbers/2635267ef83cf976c7fc4b4beb516087

@mrpmorris

This comment has been minimized.

Show comment Hide comment
@mrpmorris

mrpmorris May 11, 2018

This is great, thanks!

This is great, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment