OPTEN, das einzige Umbraco-zertifizierte Unternehmen der Schweiz

Our Octopus Odyssey: Part 5. web.config

This blog is part 5 of a series

Part 1. The Basics
Part 2. Build Script
Part 3. Variables and Powershell
Part 4. Database
Part 6. NuGet Repository
Part 7. less & CSS

The Problem

Octopus Deploy has a great built in feature named “Configuration variables” which can be used to replace the connection string or any app settings in any app.config or web.config files. This is a great feature and I explained how we used it to replace the connection string in the app.config file in our DbScripts project as I explained in the last blog. But in our web.config files we had custom sections with settings which also needed replacing which this feature could not handle.

Substitute variables in files

The solution to this problem was in another Octopus Deploy feature named “Substitute variables in files” This feature can be added to any project step in Octopus Deploy and is amazingly powerfull. It lets you specify a file or files and in each of these files any variables will be found and replaced by the correct Octopus Deploy variables, following the scoping rules for the environment, roles and steps. Using this feature made it easy to handle all our web.config settings.

Octopus.config

The first thing that I did was to create a new file in the root of each of my web applications named Octopus.config. This file was an exact copy of the web.Config. Then I opened this file and wherever there was a string which needed to be replaced at deploy, I removed the string and replaced it with the appropriate Octopus variable. The syntax for calling the Octopus variable is #{variableName}. It is very simple and very effective. So we had a custom section in our web.config. This is how it looked with the Octopus variables:

<mainSettings TitleSuffix="#{opten.commerce.mainSettings.titleSuffix}"
        AbsolutePathToMediaFolder="#{commonRoot}#{adminSiteName}\
                #{opten.commerce.mainSettings.absolutePathToMediaFolder}"
        AbsolutePathToEmailTemplatesFolder="#{commonRoot}
                #{websiteFolder}#{deployRoot}
                #{opten.commerce.mainSettings.absolutePathToEmailTemplatesFolder}"
        AbsolutePathToPdfFolder="#{commonRoot}#{adminSiteName}\
                #{opten.commerce.mainSettings.absolutePathToPdfFolder}"
        IsDatatransInLiveMode="#{opten.commerce.mainSettings.isDatatransInLiveMode}"
        ForceFrontSiteUrl="#{opten.commerce.mainSettings.forceFrontSiteUrl}"
        ForceSsl="#{opten.commerce.mainSettings.forceSsl}" />

We also used the same method to replace appSettings:

<appSettings>
    <add key="ThemeName" value="#{appSettings.themeName}" />
</appSettings>

To replace connection strings:

<connectionStrings>
    <add name="SHOPTEN_DB1" connectionString="#{SHOPTEN_DB1}"
        providerName="System.Data.SqlClient" />
</connectionStrings>

And it we even used it to set the customErrors mode, we wanted to have this set to Off for staging, so that we could see any errors that occurred, but On for live, we did not want the users to see a yellow screen of death.

<customErrors mode="#{customErrors}" />

Variable sets

Once I had gone through the Octopus.config file and set all the variables, then I had to create the variables in the Octopus Deploy itself. I found that when there are quite a few variables, creating them for each project means that you repeat yourself and also it becomes difficult to manage when there are so many variables. Here is where the “Variable sets” section of Octopus Deploy is really useful. I could create a set for each category of variable to organise them, then these variables can be scoped to the appropriate environment and role. 

Variablesets

It is only the variables which need to be scoped at the step level which need to be created under each project. The rest can be created here and imported into each project. The way to do this is to go to the project, then select the variables tab, then at the top there is a section “Also includes” here you can select “configure…” and then select whichever libraries you need to import to this project.

Configure step

Once the variables were created I added the “Substitute variables in files” feature for the relevant step and then entered Octopus.config as the target file:

Substitutevariables

That is all there is to do, it is really easy to configure. Now when I deployed all the variables were replaced in the Octopus.config file

Powershell

The only step left was to add some code to my PostDeploy.ps1 Powershell script which would delete the web.config, and rename octopus.config to web.config:

# replace web.config with octopus.config (this has the appropriate variables set)
rm Web.config
mv Octopus.config Web.config
Write-Host "replaced web.config"

Summary

This is an easy way to exactly configure the web.config file upon deploy. It also meant that by keeping the original web.config in the project when debugging on my local machine there was no problem. The web.config was there as it always was, but then when the website was deployed it was replaced by the octopus.config. The one thing that you have to keep in mind when using this method is that when making a change to the web.config, the same change must be made to the octopus.config file to keep the two in step. But this is a small thing in comparison to the convenience of not having to worry about web.config files when deploying. The great thing about Octopus Deploy is when everything is set up it just works, again and again and the worry is removed. The “Substitute variables in files” feature can be used for any files, not just the web.config. It is a very powerful tool in the Octopus Deploy armoury.

Next: private nuget repository

With the web.config files handled I was happy that Octopus deploy could do everything we wanted and decided to deploy it to our internal server. So that the other developers could also use it and we could put it into production. However it was at this point that I ran into a serious performance issue. The way I eventually solved this was to create our own custom nuget repository and this is the topic of the next blog.


kommentieren


2 Kommentar(e):

Daniel
Dezember 9, 2015 10:20

Very smart and helpful idea!


Tamoj
Februar 18, 2016 05:52

I am experiencing .ps1 is denied when I have script in post-deployment.