I've been making some headway using Web Deploy packages that transform web.config files on deploy rather than build. The goal in this is "build once, deploy everywhere". The problem I'm having is when I need to add or change complex XML in a web.config.
For example, if my base web.config contains this:
<customSection>
</customSection>
I might want to the deployed web.config to look like this:
<customSection>
<someSettingKey>QA Setting</someSettingKey>
</customSection>
It seems to work to have my SetParameters.xml look something like:
<setParameter name="customSection" value="<someSettingKey>QA Setting</someSettingKey>" />
But that seems a bit cumbersome, especially when the XML gets more nested / complex.
Are there better ways of doing this?
I was able to break up the configuration a bit so that the base web.config (for local testing) has the simple XML, and have one transform for the Release configuration (i.e. what runs on the servers). This transform adds the complex XML, and only a few keys within it need to change via the SetParameters.xml for QA, Production, etc.
The complexity in the XML came from encrypting a web.config section. The encryption (and thus, complexity) is only needed on the server.
I suppose another way to do it might be breaking up the config files using the configSource attribute for certain sections... however I didn't really flesh that out.
Related
Most of my previous web applications have built using the pyramid (python) framework. With pyramid, it is possible to run the application in either development or production mode. This is achieved by providing the location of a production.ini or a development.ini settings file as one of the start up arguments to the application.
This file contains settings relating to logging levels, error reporting and database connections. The equivalent of these files in the .net world would appear to be web.config files. I assume i can create different execution environments, by simply creating a new web.config, renaming it to something appropriate, and restarting the application.
My question is, how can i get my app to target the correct *.config file (hypothetically call them production.config and development.config)?
You may want to look at MSDeploy. I use this for similar situations and it's helped me establish a single parameters file (of what is to be replaced, regardless of if it's XML, a filepath, text file, etc.) and multiple set parameter files [you can read about declareParam and setParam here.
Without knowing too much of your structure you are basically going to do something to the effect of this.
msdeploy -verb:sync -source:contentPath=c:\pathtosite\root -dest:package=c:\package.zip -declareParamFile=c:\packageparameters.xml"
Create an associated parameters file for each environment such as
development.xml
staging.xml
production.xml
(Here is a good explanation of parameter file setup)
then for your deploy you would have something (depending on the providers you want to use)
msdeploy -verb:sync -source:package=c:\package.zip -dest:contentPath=c:\inetpub\production -setParamFile=c:\<environment>.xml
The # of providers is pretty extensive. Content Path is just an easy example, but you can pull directly from IIS, to IIS, etc. Pretty robust.
There's no way that I know of to specify a different web.config file. Since .NET 4.0, however, you can use something called web.config transforms, which modify your base web.config on build. You can have a Web.Debug.Config and Web.Release.Config, which get applied depending on which build configuration you're using. Keep in mind these aren't normal web.config files, but rather transform files that modify the base web.config.
For more info and a walkthrough of the basic idea involved, see here on MSDN. The basic example given of a web.config transform file is this, which modifies a connection string to use a different connection string for the release build:
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<connectionStrings>
<add name="MyDB" connectionString="ReleaseSQLServer"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
</configuration>
So we have a build process that handles different development web.configs across different environments. We use the ConfigSource attribute and have Team City pick the appropriate file.
That's great, but what do I do when the developers have slightly different environments?
CI, can't help, because everyones getting it straight out of SVN (i.e. CI obviously doesn't build to each developers local machine).
I'll use the ConnectionStrings config section as an example:
<connectionStrings configSource=".\Config\ConnectionStrings.config">
</connectionStrings>
And we have:
configs\ConnectionStrings.config (the generic one)
But I might need to use:
configs\ConnectionStrings.dev1.config
configs\ConnectionStrings.dev2.config
configs\ConnectionStrings.dev3.config
depending on which developer is using the code at the moment.
Any ideas?
Have you tried looking into web.config transformations? They might be able to provide the functionality that you seek while still keeping everything in version control or needing any code changes. Plus it will work for more things than just connection strings, but also directories, etc.
One technique we've used in the past (I recall Ayende mentioning it on a web cast) is that each connection string is named with the developers machine name e.g.
<connectionStrings>
<add name="BobsPC" connectionString=""/>
<add name="JonsPC" connectionString=""/>
</connectionStrings>
When in debug we then look for a connection string name of the "current" machine name.
This saves having multiple connection string files. Instead we have one file with multiple connection strings.
We have a separate connection file just like you mentioned with the current machine name in it.
In the build event for the projects that need a connection string we've added a pre built event that deletes the connection.config file from the current project and in the post build event we copy connections.machine.config to connections.config in the current project folder.
In our web.config we have a so .NET will look in a separate file for the connection string information.
Web.config transformations can only be used when you actually deploy a project. The 'default' web.config will always be used on your local development machine so this is not an option for development environments.
You can extend this mechanism also to app settings and other config files by placing the machine specif ones in the solution folder and just copying them on build.
Simple task, but for some reason no simple solution just yet.
We've all got web.config files - and I haven't worked anywhere yet that doesn't have the problem where someone yells across the room "Sh*t, I've just uploaded the wrong web.config file".
Is there a simple way of being able to auto generate a web.config file that will contain the right things for copying to release? An example of these being:
Swap connection string over to use live database
Change
Switch over to use the live/release logging system, and live/release security settings
(in our case we need to change the SessionState mode to InProc from StateServer - this isn't normal)
If you have others, let me know and I'll update it here so it's easy for someone else to find
Maintaining 2 config files works, but is a royal pain, and is usually the reason something's gone wrong while you're pushing things live.
Visual Studio 2010 supports something like this. Check it out here.
How are you deploying your builds. In my environment, this used to be a pain point too, but now we use cruisecontrol.net and script our builds in nant. In our script, we detect the environment and have different versions of the config settings for each environment. See: http://www.mattwrock.com/post/2009/10/22/The-Perfect-Build-Part-3-Continuous-Integration-with-CruiseControlnet-and-NANT-for-Visual-Studio-Projects.aspx for my blogpost onthe subject of using cruisecontrol.net for build management. Skip to the end fora brief description of how we handle config versions.
In my most recent project I wrote a PowerShell script which loaded the web.config file, modified the necessary XML elements, and saved the file back out again. A bit like this:
param($mode, $src)
$ErrorActionPreference = "stop"
$config = [xml](Get-Content $src)
if ($mode -eq "Production")
{
$config.SelectSingleNode("/configuration/system.web/compilation").SetAttribute("debug", "false")
$config.SelectSingleNode("/configuration/system.web/customErrors").SetAttribute("mode", "off")
$config.SelectSingleNode("/configuration/system.net/mailSettings/smtp/network").SetAttribute("host", "live.mail.server")
$config.SelectSingleNode("/configuration/connectionStrings/add[#name='myConnectionString']").SetAttribute("connectionString", "Server=SQL; Database=Live")
}
elseif ($mode -eq "Testing")
{
# etc.
}
$config.Save($src)
This script overwrites the input file with the modifications, but it should be easy to modify it to save to a different file if needed. I have a build script that uses web deployment projects to build the web app, outputting the binaries minus the source code to a different folder - then the build script runs this script to rewrite web.config. The result is a folder containing all the files ready to be placed on the production server.
XSLT can be used to produce parameterized xml files. Web.config being xml file this approach works.
You can have one .xslt file(having xpath expressions).
Then there can be different xml files like
1. debug.config.xml
2. staging.config.xml
3. release.config.xml
Then in the postbuild event or using some msbuild tasks the xslt can be combined with appropriate xml files to having different web.config.
Sample debug.config.xml file can be
<Application.config>
<DatabaseServer></DatabaseServerName>
<ServiceIP></ServiceIP>
</Application.config>
.xslt can have xpaths referring to the xml given above.
Can have a look at the XSLT transformation This code can be used in some MSBuild tasks or nant tasks and different web.config's can be produced depending on the input config xml files.
This way you just have to manage the xml files.
There is only one overhead that the xslt file which is similar to web.config need to be managed. i.e whenever there is any tag getting added in the web.config the xslt also needs to be changed.
I don't think you can 100% avoid this.
The last years of work ever and ever shows: where human worked, there are fails.
So, here are 3 ideas from my last company, not the best maybe, but better then nothing:
Write an batch file or an C#.Net Application that change your web.config on a doubleclick
Write a "ToDo on Release"-List
Do pair-realesing (== pair programming while realease :))
I recently worked on an app in a very interesting environment. There was 6 or 7 parallel levels for this application and only the 1st 2 levels were able to be touched by developers. As part of the company policy all builds were done as Tivoli packages, and very complex to setup.
The final kicker was that no code changes are allowed past the 1st level or "Dev" servers so web.config contained multiple encrypted sections of environment variables. The application is built to sense what environment its on by path and variables set in IIS.
This is a beast to maintain so what is a simple or better architecture for this type of problem?
Hmm, (disclaimer: I'll talk about something I've written)
Your subject seems slightly different from the post, but I think I have an idea of what you mean. The tool I'm writing, dashy, lets you handle a single codebase, and lets you configure it for various enviroments. It doesn't, however, place security restrictions on these enviroments over the other. But, depending on your source control, and general system, it may be of interest. You should get a reasonable idea of the way it works from the picture on the homepage. Perhaps it's of interest, perhaps not, but it's what we use to manage deployment to different environments. It's a work in progress ("beta") at the moment, but the current version is suitable for testing.
The latest version of ASP.NET now supports web config transformations, allowing you to change specific settings within your project for deployment, testing or staging. Here's a great intro by Tom Hundley.
Have you tried using OpenExeConfiguration of the ConfigurationManager and explicitly load the proper configurations for the appropriate environments instead of just using the default web.config?
For more, check out OpenExeConfiguration on MSDN
You could include all configurations for all environments in the web config and prefix their keys with the machine name of the appropriate environment. Then, using Server.MachineName (or some other way to identify the server that the app is running on) you can access the right configuration.
<appSettings>
<add key="DEVMACHINENAME_baseURL" value="http://dev.foo.com" />
<add key="QAMACHINENAME_baseURL" value="http://qa.foo.com" />
</appSettings>
No one would have to go in and modify anything in the web.config since the application can look up information for itself.
I am using the built in test framework in VS2008 and I would like be able to write a test that makes sure all the expected web.config settings have been defined so that if by accident one is removed or changed my suite of tests will detect it and not have to be tested in a runtime scenario. How would I set this up?
I do not want to setup a mockup of my web.config since I don't want to maintain two versions and this would make my test invalid anyways since I am really trying to capture the fact that the project's web.config is correct.
Any suggestions, alternatives, hints?
Solution: I ended up using the copy in the pre-build that was suggested with one change. On copy I rename the web.config to app.config so that the test project would automatically pick it up.
I tried to split out the config files as suggested as well but the problem I ran into was when the test project ran, it actually didn't run out of the bin directory (which setting the config files to 'Content' type would copy to) but instead to a results directory that has been pre defined. I could not figure out how to make it copy thos extra files to this results directory so the config files could never be found.
I'am using the pre-build event to copy working web.config to your test project directory.
Set the command line of the pre-build event of test project to string like this:
copy $(SolutionDir)\YourWebAppDir\web.config $(ProjectDir) /y
After that your tests will always run with actual web.config version.
Comment to pcampbell's answer:
I think if you use the configSource attribute you can just set it to the same path in web.config of your web app and app.config of test project and that makes not necessary to use build events.
sorry, I can't leave comments yet.
To expand on bniwredyc's answer, perhaps consider:
refactoring your web.config to reference a new config file like appSettings.config or similar.
modify your project's web.config to:
<appSettings configSource="appSettings.config" />
modify your Unit Test project's app.config to use this file as well.
modify your post or pre-build events to copy just this file.
this also helps ease of deployment in Test/Staging/Prod
Ultimately, the web.config is an XML file. You could generate a schema to validate the sections required are present and that required values have been populated. Obviously, you couldn't contextually validate any sort of business logic that the configuration might contain, but you could use a combination of an XSD validation plus a lightweight class that is used to parse conditions within the file.
Used in conjunction with a copy pre-build event you actually create a very nice test harness for your production quality configurations.