Joe
Joe

Reputation: 5497

App.Exe Config Transform with Azure Devops and Release Variables

I am currently using Azure Devops with an local build agent to deploy a windows service to an internal server and am looking for information on how to transform my MyApp.Exe.config file using the variables defined in my release task.

With web applications, this seems fairly straightforward. I have a parameters.xml file that is used in conjunction with the SetParameters File in my IIS Web Deploy task. This pulls the variables from the release task and updates the web.config accordingly.

However, I can't find a definitive answer on how to do this with a config file for an executable. This document suggests that it should be possible, but looks like I'd need to provide a transform file with the variables already set.

In summary, what I am looking to do is use a Parameters.xml file to transform the config file of an executable using my release variables. How can this be accomplished?

Upvotes: 1

Views: 3430

Answers (1)

Josh Gust
Josh Gust

Reputation: 4445

It's unclear in your question which values you're trying to replace in your target file, so it might be important to note:

". . . Variable substitution takes effect only on the applicationSettings, appSettings, connectionStrings, and configSections elements of configuration files. If you are looking to substitute values outside of these elements you can use a (parameters.xml) file, however you will need to use a 3rd party pipeline task to handle the variable substitution. . . ."

If you can't find a 3rd party task

Of course the Document doesn't point us to any favored 3rd party task. So if you can't find a task that operates directly on the parameters.xml file, then you can use the File Transform task to add/replace values in your .config with tokens, and then use the Replace Tokens task to insert your variable values.

Example:

In my sandbox I can replace the xml values in the specified sections in the quoted text above by simply adding the appropriate variable in my pipeline.

Given this is my config file:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSection>
        <section name="entityFramework" />
    </configSection>
    <connectionStrings>
        <add name="DefaultConnection"
             connectionString="Data Source=(LocalDB)\LocalDB;FileName=Local.mdf" />
    </connectionStrings>
    <appSettings>
        <add key="ClientValidationEnabled" value="true" />
        <add key="UnobstructiveJavascriptEnabled" value="true" />
        <add key="AdminUserName" value="__AdminUserName__" />
        

<!-- Change AdminPassword in this line: --> 
        <add key="AdminPassword" value="__AdminPassword__" />


    </appSettings>
    <entityFramework>
        <defaultConnectionFactory type="System.Data.Entity.LocalDbConnectionFactory">
            <parameters></parameters>
        </defaultConnectionFactory>
        <providers>
            <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer" />
        </providers>
    </entityFramework>    
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <staticContent>
      <remove fileExtension=".woff" />
      <remove fileExtension=".woff2" />
      <mimeMap fileExtension=".woff" mimeType="application/font-woff" />
      <mimeMap fileExtension=".woff2" mimeType="application/font-woff" />
    </staticContent>
    <security>
      <requestFiltering>


<!-- change this value -->
        <requestLimits maxAllowedContentLength="1073741824" />


      </requestFiltering>
    </security>
  </system.webServer>
</configuration>

And GIVEN these variables in yaml:

variables:
  solution: '**/*.sln'
  buildPlatform: 'Any CPU'
  buildConfiguration: 'Release'
  AdminPassword: 'fake password'
  maxAllowedContentLength: '25'

And GIVEN this is the yaml task:

- task: FileTransform@1
  inputs:
    folderPath: '$(System.DefaultWorkingDirectory)/src'
    enableXmlTransform: false
    fileType: 'xml'
    targetFiles: '*.config'

The result is this:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSection>
        <section name="entityFramework"/>
    </configSection>
    <connectionStrings>
        <add name="DefaultConnection" connectionString="Data Source=(LocalDB)\LocalDB;FileName=Local.mdf"/>
    </connectionStrings>
    <appSettings>
        <add key="ClientValidationEnabled" value="true"/>
        <add key="UnobstructiveJavascriptEnabled" value="true"/>
        <add key="AdminUserName" value="__AdminUserName__"/>


        <add key="AdminPassword" value="fake password"/>


    </appSettings>
    <entityFramework>
        <defaultConnectionFactory type="System.Data.Entity.LocalDbConnectionFactory">
            <parameters/>
        </defaultConnectionFactory>
        <providers>
            <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer"/>
        </providers>
    </entityFramework>    
    <system.webServer>        
        <security>
            <requestFiltering>


                <requestLimits maxAllowedContentLength="1073741824"/>


            </requestFiltering>
        </security>
    </system.webServer>

Notice the un-substituted value!!

As stated in the documentation, the value I wanted to change <requestLimits maxAllowedContentLength="1073741824"/> is unchanged b/c it is not defined by a configSection and is not part of the predefined nodes.

Using the 3rd party to substitute outside the defaults

Take the same config file but change the yaml task to include a transformation to the following transform.config

Given the same config file

transform.config

<?xml version="1.0" encoding="utf-8"?>

<!-- For more information on using web.config transformation visit https://go.microsoft.com/fwlink/?LinkId=125889 -->

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<system.webServer>    
    <security>
      <requestFiltering>


        <!-- change this value -->
        <requestLimits maxAllowedContentLength="${maxAllowedContentLength}$" xdt:Transform="Replace" />


      </requestFiltering>
    </security>
</system.webServer>
</configuration>

New task settings

- task: FileTransform@1
  inputs:
    folderPath: '$(System.DefaultWorkingDirectory)/src'
    enableXmlTransform: true
    xmlTransformationRules: '-transform transform.config -xml app.config'
    fileType: 'xml'
    targetFiles: '*.config'

- task: replacetokens@3
  inputs:
    rootDirectory: '$(build.sourcesdirectory)/src'
    targetFiles: 'app.config'
    encoding: 'auto'
    writeBOM: false
    actionOnMissing: 'warn'
    keepToken: false
    tokenPrefix: '${'
    tokenSuffix: '}$'

And the results are:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSection>
        <section name="entityFramework"/>
    </configSection>
    <connectionStrings>
        <add name="DefaultConnection" connectionString="Data Source=(LocalDB)\LocalDB;FileName=Local.mdf"/>
    </connectionStrings>
    <appSettings>
        <add key="ClientValidationEnabled" value="true"/>
        <add key="UnobstructiveJavascriptEnabled" value="true"/>
        <add key="AdminUserName" value="__AdminUserName__"/>


        <add key="AdminPassword" value="fake password"/>


    </appSettings>
    <entityFramework>
        <defaultConnectionFactory type="System.Data.Entity.LocalDbConnectionFactory">
            <parameters/>
        </defaultConnectionFactory>
        <providers>
            <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer"/>
        </providers>
    </entityFramework>    
    <system.webServer>        
        <security>
            <requestFiltering>


                <requestLimits maxAllowedContentLength="25"/>


            </requestFiltering>
        </security>
    </system.webServer>

Upvotes: 3

Related Questions