Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Azure Worker Role Config File Transformations

I've setup a new worker role and setup a couple of new config transforms for it via SlowCheetah. When I build the project with one of the new configs selected, I do in fact see that configs folder get created underneath the \bin folder as you would expect (for ex. \bin\Production).

When I package a cloud service for deployment using one of the new configs, my web projects get their configs transformed appropriately but my worker role (which is just a library) does not even though I see underneath the \bin folder an updated \bin\production.

It would appear the azure packaging tooling is ignoring the config set for the worker role library. How can I get it to pick the config file from the appropriate the configuration?

like image 700
James Alexander Avatar asked Dec 11 '12 20:12

James Alexander


People also ask

What is Xdt transform SetAttributes?

The xdt:Transform attribute value "SetAttributes" indicates that the purpose of this transform is to change attribute values of an existing element in the Web.

How does web config transform work?

Transformation actions are specified using XML attributes defined in the XML-Document-Transform namespace, that is mapped to the xdt prefix. There are xdt:Transform and xdt:Locator attributes that we use in the Web. Config file. So, xdt:Locator locates element(s) and xdt:transform specifies the action over it.

What is Slowcheetah?

This package allows you to automatically transform your app. config (or any file) when you press F5 in Visual Studio. You can have different transformations based on the build configuration. This will enable you to easily have different app settings, connection strings, etc for Debug versus Release.


2 Answers

Yes, you can do this - and it is even very easy once you know how.
App.config is not transformed by design but fortunately the Azure Team made the build/deploy process very extensible exactly for these kinds of scenarios. What you need to do is reasonably well documented, though in a very roundabout way and most articles assume you are already familiar with MSBuild scripts and the like.

Below you will find the lines you need to put into your project that will make this Just Work. That should take no more than five minutes. Please note that this is not a hack - the whole Azure deploy process is designed to support this kind of thing.

If you want to know more, there are some links to related articles at the bottom.

Some conceptual points

  1. The recommended way to achieve this kind of thing in Azure is to not use Web.config and App.config but instead use the CloudConfigurationManager and use Role Settings. However, sometimes that just isn't the right answer, usually because of built-in or 3rd party components that require *.config settings (smtp, wcf, elmah etc).
  2. Web Config transformations is designed for transforming only the web.config. This means that app.config is not transformed by design.
  3. Web Config transformations is designed to only kick in when publishing so when you run locally, even in the Cloud Emulator, your Web.config won't be transformed.

The way we can solve this is by hooking into the build process for the Cloud project. When you deploy the project to Azure, the Cloud project will be built using a build process you can hook into. In short, the cloud project builds the web and worker roles and puts them under the Obj folder under your cloud project. It then runs a process that essentially zips all that up and finally put the result into the Bin folder. From there, the "zip" file and a configuration file is uploaded to Azure.

The solution

Is to manually edit your Cloud.csproj file (if you do it from within Visual Studio you need to unload the project first). Then add this right above the closing </project> tag:

  <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v11.0\Web\Microsoft.Web.Publishing.targets" />
    <PropertyGroup>
      <WorkerRoleDir>$(IntermediateOutputPath)WorkerRole1\</WorkerRoleDir>
      <AppConfigOriginal>$(WorkerRoleDir)WorkerRole1.dll.config</AppConfigOriginal>
      <AppConfigTransformer>$(SolutionDir)WorkerRole1\App.$(Configuration).config</AppConfigTransformer>
      <AppConfigAfterTransformed>$(WorkerRoleDir)AfterTransformed.config</AppConfigAfterTransformed>
    </PropertyGroup>
    <Target Name="TransformAppConfig" AfterTargets="AfterPackageComputeService">
      <Message Text="Transforming $(AppConfigOriginal) via $(AppConfigTransformer) to $(AppConfigAfterTransformed)" />
      <TransformXml Source="$(AppConfigOriginal)" Transform="$(AppConfigTransformer)" Destination="$(AppConfigAfterTransformed)" />
      <Copy SourceFiles="$(AppConfigOriginal)" DestinationFiles="$(WorkerRoleDir)App.Config.Original" />
      <Copy SourceFiles="$(AppConfigAfterTransformed)" DestinationFiles="$(AppConfigOriginal)" />
    </Target>

Notes

  • There are a couple of hard-coded paths in there that you will have to modify. I am sure there is a way to make them soft but that requires more MSBuild skills than I have.
  • The transformation will actually run when you deploy to your local Cloud Emulator, but it won't be used. As such, the result is consistent with the behavior of Web.config which is also not transformed. But, if your transformation was to fail, you will get a build error even when just running in the Emulator.
  • See also This other SO question
  • An in-depth exploration
  • Tom Hollanders highly linked article on deploying directly from MSBuild
like image 73
flytzen Avatar answered Oct 04 '22 16:10

flytzen


I find @Frans answer too complicated, and below is the code I found in the internet. Given that you already have your app.config transformation set up and working, open your cloud project (.ccproj) in text editor, find this line:

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

and insert the following after it:

  <!-- Get worker role transform start -->
  <Target Name="CopyWorkerRoleConfigurations" AfterTargets="CopyWorkerRoleFiles">
    <Copy SourceFiles="$(WorkerTargetDir)\YOUR-PROJECT-NAME.dll.config" DestinationFolder="$(IntermediateOutputPath)YOUR-PROJECT-NAME" OverwriteReadOnlyFiles="true" />
  </Target>
  <!-- Get worker role transform end -->

and replace YOUR-PROJECT-NAME with your worker project name.

UPDATE

I actually found a better way to do this (MSBuild 4+): script above will NOT work if you have more than 1 worker role with app.config transformations in your Azure project. Here is the more generalized way:

  <Target Name="CopyWorkerRoleConfigurations" AfterTargets="CopyWorkerRoleFiles">
    <PropertyGroup>
         <RootFolder>$([System.IO.Path]::GetDirectoryName($(MSBuildProjectDirectory)))</RootFolder>
    </PropertyGroup>

    <Copy SourceFiles="$(RootFolder)\%(ProjectName)\bin\$(Configuration)\%(EntryPoint).config" DestinationFolder="%(WorkerRoleReferences.OutputDir)" OverwriteReadOnlyFiles="true" />
  </Target>  
like image 40
avs099 Avatar answered Oct 04 '22 16:10

avs099