Monday, August 3, 2009

ASP.NET Multi-environment Deployment Configuration

I'm still new to ASP.NET, fresh off of the JEE bandwagon. In many ways, deploying an ASP.NET application to IIS can be blazingly simple (e.g. "just copy the built artifacts to the site folder"). Like JEE, though, there doesn't seem a good, widely agreed upon solution for separating environment-specific configuration from the application-specific configuration.

Like many shops, we have several environments. We have two "debug" environments and two "non-debug" environments where we want things quiet and locked down. The "debug" environments can show full exception detail, verbose logging, etc. The "non-debug" environments -- including production -- do not. Our UAT environment is configured as a "non-debug" environment so that its as close to how it will be experienced in production as possible. Furthermore, the databases, internal URLs and such also vary from environment to environment.

Most of the solutions I came across suggested maintaining multiple web.configs, one for each environment. I dislike such solutions because it requires duplicate maintenance (I'm lazy, and humans inevitably fail to do the "dual" part). I did find one suggestion I liked: use XSL transformation to modify points in your web.config for each environment.

Thus far, I have crafted an XML stylesheet for our dev and uat environments. Each of these is designed to transform our web.config from source control (set up for local environments) into a configuration specific to that environment. The stylesheet itself largely clones the existing XML, replacing specific values or attributes, such as:

Connection strings (substituting variables where necessary)

Exception detail (toggled on or off)

Debuggable ASPx compilation (toggled on or off)

WCF service endpoint addresses (URLs specific to each environment)

Logging listener log file paths

etc.

For example, to turn off debug information for the pages, the following snippet is used:

<!-- Set the debug compilation to enable/disable insertion of debugging symbols into the compiled page. Because this affects performance, set this value to true only during development. -->

<xsl:template match="/configuration/system.web/compilation/@debug">

<xsl:attribute name="debug">true</xsl:attribute>

</xsl:template>

I've also broken my stylesheets into a base one for the web.config (e.g. my AppServices project's stylesheet is AppServices.xsl) as well as an environment-specific stylesheet that sets the variables for it (e.g. AppServices-dev.xsl, AppServices-uat.xsl):

Thus far, the plan is working quite well. The Hudson CI server is building and automatically deploying the applications, transforming the web.config as it goes. From time-to-time I have to alter the stylesheets to extract another environment-specific setting, but this is rare and straight forward.