Monday, June 13, 2011

Windows Azure Configuration Settings per build configuration

Don’t you like the neat feature that web projects have – applying different configuration settings based on build configuration? If you create a new Web Application project, you surely have noticed the 3 web.config files – web.config, web.debug.config & web.release.config. This is a feature of web projects, which uses nice xml transform task to shape your final web.config file according to your build configuration:

WebConfings

For instance you put your development connection string in your web.config file, and you put your production environment in your web.release.config file. Now you don’t have to manually edit the web.config when you deploy. Just build in Release configuration and you are ready!

Now, don’t you want to have something like that, but for your ServiceConfiguration.cscfg in your Cloud Service (Azure) project? I want! Here are the steps you have to follow to achieve this:

Full source code for the given sample can be downloaded from here.

1. Open the folder for your CloudService Project and manually add the new config file (i.e. ServiceConfiguration.Release.cscfg) (we will edit this file later). You have to do this because you can’t just Add New File to this project. The project template for Cloud Service project does not allow you to do so.

2. Unload your Cloud Service project and select “Edit XXXX.ccproj”

3. You have to manually include the ServiceConfiguration.Release.cscfg file to the project. So locate the ItemGroup section where your project files are included:

  <ItemGroup>
    <ServiceDefinition Include="ServiceDefinition.csdef" />
    <ServiceConfiguration Include="ServiceConfiguration.cscfg" />
  </ItemGroup
>

And add the new file within that ItemGroup section:

    <None Include="ServiceConfiguration.Release.cscfg" />

4. Navigate to the last line, just before

</Project>

and after

 <Import Project="$(CloudExtensionsDir)Microsoft.CloudService.targets" />

Now add the following code:

  <UsingTask TaskName="TransformXml" 
             AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
  <PropertyGroup>
    <ServiceConfigurationTransform>ServiceConfiguration.$(Configuration).cscfg</ServiceConfigurationTransform>
  </PropertyGroup>
  <Target Name="TransformServiceConfiguration" 
          BeforeTargets="CopyServiceDefinitionAndConfiguration" 
          Condition="exists('$(ServiceConfigurationTransform)')">
    <!-- Generate transformed service config in the intermediate directory -->
    <TransformXml Source="@(ServiceConfiguration)" 
                  Destination="$(IntermediateOutputPath)%(Filename)%(Extension)" 
                  Transform="$(ServiceConfigurationTransform)" />
    <!--Force build process to use the transformed configuration file from now on.-->
    <ItemGroup>
      <ServiceConfiguration Remove="ServiceConfiguration.cscfg" />
      <ServiceConfiguration Include="$(IntermediateOutputPath)ServiceConfiguration.cscfg" />
    </ItemGroup>
  </Target
>

5. Save and close the ccproj file, and reload your project.


6. Now lets edit the ServiceConfiguration.Release.cscfg file. Let’s assume we have a “MySetting” configuration setting which we want to alter based on build configuration. Then edit the .Release.cscfg file as follows:

<?xml version="1.0" encoding="utf-8"?>
<sc:ServiceConfiguration serviceName="AzureSettingsSample
"
  xmlns:sc="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration
"
  xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <sc:Role name="SimpleWorker" xdt:Locator="Match(name)">
    <sc:ConfigurationSettings>
      <sc:Setting name="MySetting" value="Release
"
        xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />
    </sc:ConfigurationSettings>
  </sc:Role>
</sc:ServiceConfiguration
>

The additional namespace declaration is required so that the TransformXml task recognizes the nodes and attributes.


Voilah! You can change as many settings as you would like, and never mess with commenting out production settings, or forget to change the “usedevelopmentstorage=true” diagnostics connection string!


The only thing you have to remember is that you have edit the ccproj file each time you add new build configuration and want to include new ServiceConfiguration.BuildConfig.cscfg file. You can have as many as you would like.


Credits go to Oleg Sych’s post on configuration settings.


Again, the full source code can be downloaded from here.

2 comments:

Rajesh Babu said...

Thanks for awesome post!!! I would call it a hard core hack :-)

skenway said...

Anton:

Do you have any thoughts on how we could manage Web References to APIs during our builds? For example, when I build for Test, I want to build with a connection to the API test environment. That would be much appreciated if you had any thoughts.

Oh by the way, amazing post!