Complex Web.config variable replacement

Hi,

We have some custom sections in some of our Web.config files, like so:

@@@















@@@

What I would like is to have some of these be replaced with OD variables too. Of course it is complex because it is not a 1-to-1 any more.

I’ve looked into the “XML transformations” feature, but from what I understand I cannot use a OD variable anywhere in the XSLT code, and so I will not be able to get the OD variables that way.

I’ve also looked into creating a powershell that could apply XSLT from a OD variable, or replace OD variables into a static XSLT code before applying it to the config file. I’ve not been able to complete it and to be honest that way feels like reimplementing some stuff that OD is already doing.

I was just wondering if anyone else has experience around this?
…or how does OD recommend dealing with config values that need to be replaced but are nested into the config?
…f.ex. SOAP endpoints / URLs defined in Web.config that should be replaced by OD must be a common problem?

Hi,

Thanks for getting in touch!

The best way to work with Octopus Deploy in this situation would be to do the substitutions in a transform file.
There are some examples and detail in the following blog post:

Hope that helps!

Vanessa

Thank you for your reply, Vanessa.

What if I need to replace/transform the config to contain a value from
Octopus Deploys variables?
If I understood correctly I will not be able to do this with the config
transforms. The best I could do was to create separate transforms with
the same names as my environments, right? That would mean I cannot keep
my config administrable in the OD webinterface any longer… I want that.

Hi Moh,

The trick to achieving this is to use the transform file as the target of variable substitution. This makes it possible to ‘for-each’ over a set of values, if I understand correctly that this is your goal.

(You can use your Web.config file itself as the target of variable substitution, but this will mean it is no longer valid XML, which is inconvenient during development.)

Assuming you have one or more transform files targeting the Web.config file, in the transform file you can use expressions like:

#{each server in Servers}
  <Server Name="#{server.Name}" Host="#{server.Host}"  ... />
#{/each}

Your Octopus variables can then contain entries like:

Server[Cache].Name | RedisCache
Server[Cache].Host | redis.example.com
Server[Carts].Name | RedisCarts
Server[Carts].Host | carts.example.com

This will evaluate in the transform file to:

  <Server Name="RedisCache" Host="redis.example.com"  ... />
  <Server Name="RedisCarts" Host="carts.example.com"  ... />

When the transform is subsequently applied to the Web.config file these values can be copied in.

If this isn’t what you’re looking for, can you please provide a few more details showing:

  • What (ideally) would the variables look like?
  • What should the resulting Web.config look like?

Hope this helps,
Nick

Thanks Vanessa, Nicholas!

I got this working using your described method. Summary of changes I had to do:

  • Rename my config transform file to: MyApp.exe.Release.config
    (not App.Release.config, including the “exe” :slight_smile: )
  • Add the scripting sections to the config transform file.
  • OD / Variables: Add new values.
  • OD / Steps: Add feature: Variable substitutions on MyApp.exe.Release.config
  • OD / Steps: Add feature: Configuration tranforms.

A follow-up question, in the #{each}-loop, is there a way I can get the key of the iterator?

I.e. it would be nice if config variables like this:
@@@
Server[Cache].Host = redis.example.com
@@@

Substituted into something like this:
@@@
#{each server in Servers}
<Server Name="#{…KEY…}" Host="#{server.Host}" … />
#{/each}
@@@

Would result in :
@@@
<Server Name=“Cache” Host=“redis.example.com” … />
@@@

Tried to google for more on #{each}, #{unless} etc, but didn’t find anything. Is it still too new to be in the documentation?

Great! Glad to hear you’re up and running.

#{server}

…will give you the key. You need a fairly recent Octopus version for this to work IIRC.

We’re intending some updates to variable substitution in the near future so should have a chance to revisit the docs then.

Cheers!

I am hoping this helps me get to what I need here:

http://help.octopusdeploy.com/discussions/questions/3481-every-website-in-the-deploy-has-a-different-apppool-and-website-name-how-to-deal-no-other-differences

Moh,

It took me some time to get this working. Here are some tips for people that read this:

In your example you use #{each server in Servers}. The variables then need to be called Servers[Cache].Name (plural) instead of Server[Cache].Name (singular).

Also, I wanted to have this substitution take place on my Web.config. However, I had setup my Web.Release.config to paste in the OD #{each...} statement. I found that the variable substitution runs before the config transformation, hence I had to perform the substitution on Web.Release.config instead of Web.config.

Hi Pascal,

Yes, I see mr. Blumhardt had a small typo in his example, you are right,
it should have been said Servers in the config variables. It makes sense
when thinking of a foreach-loop that one is reading from a var Servers
into a local var server.
I can confirm my experience matches yours, substitution happens before
transformation. I will typical have Web.Release.config as the
substitution target. That way when transformation happens
Web.Release.config already has the desired substituted content.

Thanks for the input!

On 11.12.2014 16:24, Pascal wrote:

Bit of a newbie question this one.

We want to fill a target config file AppSettings.Config with items sourced from variable sets to produce a bunch of appSettings e.g.

...

However we want the originating file to be empty, so rather than replacement we want to do insertion.

Is this possible. Were currently on Octo 2.6.5.1010

Hi Stuart,

Replacement would definitely be easier on this case, but if that’s not an option in your scenario:

You can add attributes to your configuration files. Now the configuration files cant be 100% empty, as they need to have at least the skeleton of a .config file (i.e it needs to have )

This site shows an example of how to add an attribute to a config file: https://webconfigtransformationtester.apphb.com/

In your case you wont have a transform-config-file from which you will add the settings to you main config file. So you will need to add a step on your deployment process previous to the transformation/deployment, on which you will create this transform-config-file based on the values of your variables. The easiest way for this will be to use Powershell

Hope that helps,

Dalmiro