Configuration Variables Replace (custom binding?)

USING: 3.0.17.2462

When deploying code to a Azure CloudService/WorkerRole, the TFS build renames the app.config in source control to applicationName.Blah.Blah.Blah.dll.config; and it looks like there’s some problems with doing variable substitution in this file.

Inside one of our app.config files there is some “microsoft logic” that acts like a kind of switch statement.
I would like:

  1. To find a way to not have to use VariableSubstitution (because then developers cant actually use the app.config during development without locally modifying the file - which always Always ends up getting committed back to sourcecontrol)
    and
  2. not have to use some kind of manual Transform (since VS2013 doesnt support Transform on app.config for CloudServices projects).

Here’s the “Switch” statement that is causing my problem.

  <AzureConfiguration DefaultDomain="dev">
	<Domains>
	  <add Name="dev">
		<Service Namespace="dev001" KeyName="RootManageSharedAccessKey" KeyValue="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" />
		<Storage AccountName="dev001" AccountKey="XX/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==" />
	  </add>
	  <add Name="qa">
		<Service Namespace="qa001" KeyName="RootManageSharedAccessKey" KeyValue="BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=" />
		<Storage AccountName="qa001" AccountKey="XX/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==" />
	  </add>
	</Domains>
  </AzureConfiguration>

Ideally, the solution would be to have a way to specify custom key/value configuration variable replacement to fin/replace the

<Service Namespace="Some1ValueFromOctopusVariableSet" KeyName="Some2ValueFromOctopusVariableSet" KeyValue="Some3ValueFromOctopusVariableSet"/> 

and

<Storage AccountName="SomeValue1FromOctopusVariableSet" AccountKey="SomeValue2FromOctopusVariableSet"/> 

So the values be defaulted for developer testing and then use Octopus find/replace with VariableSets to inject the correct environment values at deploy time.

I see making custom Configuration Variable bindings is /possible/ but there’s no documentation for how to construct/format them to use this feature.

An example of how to use the custom Configuration Variable bindings to accomplish the above would be exemplary.

Hi Jon,

Thanks for getting in touch! Just to clarify, Octopus has a couple of configuration features:

  • ConfigurationVariables: replaces appSettings/connectionStrings by convention (not applicable here)
  • ConfigurationTranforms: runs Web/App.config transforms
  • VariableSubstitution: replaces #{....} tokens in files

VariableSubstitution, like you said, isn’t a good fit because then the files wouldn’t work for developers.

Configuration transforms should work, however. If you have:

applicationName.Blah.Blah.Blah.dll.config
applicationName.Blah.Blah.Blah.dll.Production.config

Octopus will apply the *.Production.config transform file to the *.config file.

(The trick here is to not use app.config in the naming, but the full file name that TFS build transforms it to)

The “configuration transforms” section on this page goes into some more detail and has examples:

http://docs.octopusdeploy.com/display/OD/Configuration+files

If that doesn’t work for you, you could look at using this library script which uses XPath queries to replace values in XML files:

http://library.octopusdeploy.com/#!/step-template/actiontemplate-xml-update

Hope this helps!

Paul

Thanks Paul, I did find this to be the best implementation case yesterday (using Application.blah.blah.dll.Release.config) but am waiting for end of sprint testing to validate the fix (bleh).

I simplified the Switch statement to have only one environment with substitution.

  <AzureConfiguration DefaultDomain="#{AzureDefaultDomain}">
       <Domains>
         <add Name="#{AzureDefaultDomain}">
              <Service Namespace="#{AzureDefaultDomain}" KeyName="RootManageSharedAccessKey" KeyValue="#{RootManageSharedAccessKey}" />
              <Storage AccountName="#{AzureDefaultDomain}" AccountKey="#{StorageAccountKey}" />
         </add>

Will respond with additional feedback once another build had been deployed.

The above worked, mostly.
However Configuration Transforms didnt replace the *.web.config with the *.Release.config file; so I ended up having to write a pre-release custom Powershell script to Find and rename *.Release.config to *.config.

Write-host "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=";
write-host "PRE-DEPLOYMENT SCRIPT START";
$releaseConfigs=$null;
$releaseConfigs = dir *.Release.Config -recurse;
if($releaseConfigs -ne $null){write-host "Found files matching *.Release.Config!";}
ForEach($file in $releaseConfigs){
    write-host "Processing file: $($file.Fullname)";
    $DestinationFilename=$null;
    $DestinationFilename="$($File.Directoryname)\$($file.Name.Replace("Release.",''))";
    Write-host "Checking for Existing DestinationFilename: $DestinationFilename";
    While(Test-Path($DestinationFilename)){
        write-host "Getting file to rename: $DestinationFilename";
        $destinationFile=gi $DestinationFilename;
        write-host "DestinationFilename Found, reanaming.";
        $ren=Rename-Item $destinationFile -NewName "$($DestinationFile.Name).Originial.ReplacedWithRelease" -Force -ea STOP;
        write-host "Looking for file: $($destinationFile.DirectoryName)\$($destinationFile.Name).Originial.ReplacedWithRelease";
        $renameSuccess=$null; 
        $renameSuccess=gi "$($destinationFile.DirectoryName)\$($destinationFile.Name).Originial.ReplacedWithRelease";
        if($renameSuccess -ne $null){
            write-host "DestinationFilename successfully renamed to: $($renameSuccess.FullName)";
        }else{
            write-host "DestinationFilename rename FAILED.";
            <# exit 1; #>
        }
    }
    write-host "Renaming Release.Config: $($file.fullname)";
    Rename-Item $file $DestinationFilename -Force -ea STOP;
    $renameSuccess=$null;
    $renameSuccess=gi $DestinationFilename;
    if($renameSuccess -ne $null){write-host "Rename Release.config file success: $DestinationFilename";}
}
write-host "PRE-DEPLOYMENT SCRIPT FINISH";
Write-host "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=";