Web.Config transforms outside of Microsoft MSBuild?

Solution 1:

I created a small function to handle Microsoft's XML Document Transform in PowerShell.

I copied the Microsoft.Web.XmlTransform.dll file from Visual Studio build folder to my script's path, but you can reference it from the source folder if you'd like.

function XmlDocTransform($xml, $xdt)
{
    if (!$xml -or !(Test-Path -path $xml -PathType Leaf)) {
        throw "File not found. $xml";
    }
    if (!$xdt -or !(Test-Path -path $xdt -PathType Leaf)) {
        throw "File not found. $xdt";
    }

    $scriptPath = (Get-Variable MyInvocation -Scope 1).Value.InvocationName | split-path -parent
    Add-Type -LiteralPath "$scriptPath\Microsoft.Web.XmlTransform.dll"

    $xmldoc = New-Object Microsoft.Web.XmlTransform.XmlTransformableDocument;
    $xmldoc.PreserveWhitespace = $true
    $xmldoc.Load($xml);

    $transf = New-Object Microsoft.Web.XmlTransform.XmlTransformation($xdt);
    if ($transf.Apply($xmldoc) -eq $false)
    {
        throw "Transformation failed."
    }
    $xmldoc.Save($xml);
}

To transform web.config using web.release.config:

XmlDocTransform -xml "Web.config" -xdt "Web.Release.config"

Alternatively, you can use Sayed's self-bootstraping Xml Transform script, which will take care of getting the Microsoft.Xml.Xdt.dll for you:

https://gist.github.com/sayedihashimi/f1fdc4bfba74d398ec5b

Solution 2:

The logic of the transformation is contained inside of the TransformXml task itself. If you want to call it from code you would have to use the MSBuild API with a mock engine and execute it. I have some code for this if you want.

In your case since you mentioned PowerShell the best thing for you to do is to just create a wrapper MSBuild file to invoke the TransformXml task. I say this because PowerShell is configured to run under .NET 2.0, but the TransformXml task requires .NET 4.0. In order to call it from a dummy MSBuild file you can check my blog at http://sedodream.com/2010/04/26/ConfigTransformationsOutsideOfWebAppBuilds.aspx, but I've also pasted a sample from that link below.

<Project ToolsVersion="4.0" DefaultTargets="Demo" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <UsingTask TaskName="TransformXml"
             AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll"/>

    <Target Name="Demo">
        <TransformXml Source="app.config"
                      Transform="Transform.xml"
                      Destination="app.prod.config"/>
    </Target>
</Project>

For mono, this should work (tested on mono 6.4, macos, 2019) :

<Project DefaultTargets="TransformConfig" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <UsingTask TaskName="TransformXml"
    AssemblyFile="$(MSBuildSDKsPath)/Microsoft.NET.Sdk.Publish/tools/net46/Microsoft.NET.Sdk.Publish.Tasks.dll"/>
  <PropertyGroup>
    <TransformSource>Web.config</TransformSource>
    <Transformer>Web.Live.config</Transformer>
    <Destination>Output.Web.config</Destination>
  </PropertyGroup>
  <Target Name="TransformConfig">
    <Message Text="From TransformSource : $(TransformSource)" />
    <Message Text="Using Transform : $(Transformer)" />
    <Message Text="Output : $(Destination)" />
    <Message Text="MSBuildSDKsPath=$(MSBuildSDKsPath)" Condition="'$(MSBuildSDKsPath)' != ''" />
    <TransformXml Source="$(TransformSource)" Transform="$(Transformer)" Destination="$(Destination)"/>
  </Target>
</Project>

which you can run with just msbuild or supply parameters with

msbuild /p:TransformSource=... /p:Transformer=...