The Windows Azure SDK 1.5 was released over a year ago introducing support for multiple Service Configuration files. Ever since this release it has become really easy to manage multiple environments by simply defining a new Service Configuration and changing the settings for each environment:
A typical use case for working with multiple Service Configurations would be your database connection string. If you’re developing locally you probably have a SQL Express database running on your machine. Then when you deploy to Windows Azure to do some tests you want to use a different database (maybe you have a different Cloud Service for your ‘beta’ version: myapp-beta.cloudapp.net). And finally when you deploy to production you want the application to use the production database. Well by using multiple Service Configuration files you can manage these different connection strings and choose which one to use upon deployment:
At the moment this feature only covers ServiceConfiguration.cscfg files, but there might be times where you’ll also need multiple ServiceDefinition.csdef files.
Let’s come back to the “beta” example I previously mentioned. This is probably a deployment used by the team internally to see how things run in Windows Azure and to have a few users test it out. Now what exactly is included in a ServiceDefinition.csdef file? The definition of your Roles (with their sizes), Virtual Directories, Endpoints, … I’ve seen a few customers use Medium sized roles in production and ExtraSmall sized roles in their test environment to reduce the cost of compute hours. They typically use multiple ServiceDefinition.csdef files (one for each environment) which also means they need to make changes to all of these files if they add an endpoint for example.
Let’s see how we can use XDT transforms (the web.config transformations syntax) to support multiple ServiceDefinition.csdef files.
Last week the XDT (web.config) transform engine was released on NuGet (official post) which makes it really easy to build your own tools that can apply (web.)config transformations. And this is what Eric Hexter did, he released the WebConfigTransformRunner package on NuGet. This is essentially a command line tool which can run transformations. So in order to get started you’ll first need to add this package to your solution:
PM> Install-Package WebConfigTransformRunner
Once the package is installed you’ll find it in the packages\WebConfigTransformRunner.18.104.22.168\Tools directory. What we’ll do next is create a transformation that sets the size of our WebRole to ExtraSmall when we deploy to the CloudBeta environment. So first you’ll need to create a new ServiceDefinition.CloudBeta.csdef file in your Windows Azure project (make sure you do the same for the other configurations):
As you can see it’s pretty easy to achieve by using xdt:Transform and xdt:Locator (the syntax is explained here):
Now that we have our file the only thing left to do is modify the *.ccproj file (the project file of your Windows Azure project). We need to make 2 changes:
- Define the transformation files as Content (that way you’ll see them in Visual Studio in the Windows Azure project)
- Define where the WebConfigTransformRunner executable is located. Note that you might need to change the version number.
- Create a target named AfterValidateServiceModel which contains the logic to do run the actual transformation. This task will be executed when your Azure project builds (as defined in Microsoft.WindowsAzure.Targets)
That’s it. Save the file, reload the project in Visual Studio and you’re good to go. You can now have a base Service Definition file (ServiceDefinition.csdef) and have a transform file per environment to manage the differences between environments.
What about TransformXml?
If you’ve worked with config transformations and MSBuild in the past you’re probably wondering why I’m not using TransformXml? Well first because I really like the WebConfigTransformRunner tool and second, because of this issue with TransformXml (explained here):
Exception: Could not write Destination file: The process cannot access the file ‘D:\Repositories\Local\AzureConfigTransformations\AzureConfigTransformations\bin\Release\ServiceDefinition.csdef’ because it is being used by another process.
2> at Microsoft.Web.Publishing.Tasks.TransformXml.SaveTransformedFile(XmlTransformableDocument document, String destinationFile)
2> at Microsoft.Web.Publishing.Tasks.TransformXml.Execute()
But hey if you found a solution to this file in use error feel free to use TransformXml, it will work all the same.