ASP.NET July 15, 2009

Handling multiple ASP.NET configuration files with build events

In the project that I’m currently working on at work most of the developers are using Windows Vista and thereby IIS 7 while a few, including myself is using Windows XP and thereby IIS 5.1. This means that we need to have two separate web.config files. Then there is Kalle Hoppe who uses the 64 bit edition of Vista and me who is running the 64 bit edition of Windows 7 at home (where I usually put in a few hours as well). Since we are using EPiServer CMS which admin interface is by default located in C:\Program Files\EPiServer and referenced in web.config and the 64 bit OS’s program files folder is located at C:\Program Files (x86)\EPiServer we actually need three versions of web.config.

In other projects that I’ve worked in there has been a number of solutions to this problem, such as not checking in web.config into the source code repository or checking in multiple versions and then each developer has had to manually copy and rename the version that fits him or her. This time around we (me and Kalle) decided to do something about this. We wanted a solution that would be as simple as possible and inspired by a talk by Ted Nyberg at a EPiServer developer meetup a few months ago where he described a simple way to solve a similar problem using build events in Visual Studio we created a simple Powershell script that automatically copies the files for us. In order to minimize the effort each time someone makes a change to web.config we also broke out most of the sections that are common to both versions of IIS to separate files. Here’s what we did.

Breaking out sections of web.config

The first thing we did was to break out many of the sections in web.config that are common to both IIS 7 and IIS 5.1/6 and put all of those files in a folder below the web root called Configuration. That is, we copied a section such as profile

<system.web>
  <profile enabled="true" defaultProvider="SqlProfile" automaticSaveEnabled="true">
    <properties>
      <add name="Address" type="System.String" 
        provider="SqlProfile" />
      <add name="ZipCode" type="System.String" 
        provider="SqlProfile" />
    </properties>
  </profile>
</system.web>


in web.config to a separate file and replaced it in web.config with a pointer to that file

<system.web>
  <membership configSource="Configuration\membership.config" />
</system.web>

Screendump of the configuration folderHaving done this we created two separate web.config files, IIS6.web.config and IIS7.web.config, as well as a file named episerver.x64.config which contained the EPiServer configuration section which needed to be slightly different for 64 bit OS’. While all other configSource attributes where set to point to files in the Configuration folder the EPiServer section’s attribute pointed to the web root. You’ll see why very soon.

We placed the above mentioned files in the Configuration folder as well. These operations left us with a configuration folder looking like the image to the right and no configuration files in the web root.

Creating a Powershell script for copying the correct files

The next step was to create a Powershell script that copies one of the two web.config files and one of the two EPiServer config files to the web root depending on which version of Windows it is run on. Our script looks like this:

$projectDir = $args[0]

write-output "Copying web.config"
$webConfigPath = join-path $projectDir "web.config";
del -force $webConfigPath;
if(((gwmi Win32_OperatingSystem | select Version) -match "5.1"))
{ 
  copy (join-path $projectDir "Configuration\IIS6.web.config") $webConfigPath
} 
else
{
  copy (join-path $projectDir "Configuration\IIS7.web.config") $webConfigPath
}

write-output "Copying episerver.config"
$episerverConfigPath = join-path $projectDir "episerver.config";
del -force $episerverConfigPath;
if (((gwmi Win32_OperatingSystem | Select OSArchitecture) 
  -match "64"))
{
  copy (join-path $projectDir "Configuration\episerver.x64.config") $episerverConfigPath
}
else 
{
  copy-item (join-path $projectDir "Configuration\episerver.config") $episerverConfigPath
}


The script needs a parameter which specifies the path to the project (such as C:\Projects\Customer\Customer.web). Given that it begins by deleting the existing web.config file in the projects root folder. Then it figures out whether the computer it is executed on runs Windows XP or not and copies a renamed version of either IIS6.web.config or IIS7.web.config to the projects root folder.

The same procedure is repeated for episerver.config but here the OS’ architecture is checked instead of the version.

Executing the script when compiling

imageThe final step was to set it up so that the script runs after each build. To do so we entered the below into the Post build event command line field (which is located under project properties, Build events):

powershell.exe set-executionpolicy unrestricted
powershell.exe $(ProjectDir)CopyConfig.ps1 '$(ProjectDir)'
powershell.exe set-executionpolicy default
 

The first line tells Powershell that it’s OK to run unsigned scripts, the second line runs the script with the projects path as parameter and the third line resets Powershells policy regarding unsigned scripts.

Conclusion

While there are a lot more advanced ways to solve this problem we found this to be a pretty quick and easy solution that works well for us. I’m quite sure that there are a more elegant way to write the Powershell script. Don’t hesitate to let me know if you have any suggestions :)

Thanks to Ted Nyberg for inspiring this solution.

PS. For updates about new posts, sites I find useful and the occasional rant you can follow me on Twitter. You are also most welcome to subscribe to the RSS-feed.

Joel Abrahamsson

Joel Abrahamsson

I'm a passionate web developer and systems architect living in Stockholm, Sweden. I work as CTO for a large media site and enjoy developing with all technologies, especially .NET, Node.js, and ElasticSearch. Read more

Comments

comments powered by Disqus

More about ASP.NET