10
votes

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?

4
cspack (azure packaging tools) does not depend on application configuration (app.config) instead it use service definition, as described here msdn.microsoft.com/en-us/library/gg432988.aspx. If you can provide more info on what app config settings you would want to include in build configuration and how they are defined, I can help. Also how do u build your package, command line or VS UI.AvkashChauhan
Via VS UI. I'm attempting to port an application to Azure and don't want to have to re-write large chunks of it to ignore the app.config. Things like system.net.mail configurations that I normally transform via config transforms.James Alexander
I have used SlowCheetah in the past and found that it only works for developers who have it installed - it does NOT work for developers who do not have it installed. In other words, the changes it makes to your .csproj are NOT enough to make it "just work" for everybody else - something additional is required. I don't know what that something additional is, though. :-(Jaxidian
This is all irrelevant now. SlowCheetah now supports Azure Worker Role config transforms. Hooray!James Alexander
@JamesAlexander SlowCheetah does not transform app.Production.config when create package is azure. How did you made it?fiberOptics

4 Answers

10
votes

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
9
votes

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>  
0
votes

Make sure you Cloud Service configurations are set up. Right-click the cloud project and you should see the configurations you are trying to package. For example, I rename and use Local, TEST & PROD configurations. Your Cloud Service project should then contain 3 config files:

  • ServiceConfiguration.Local.cscfg
  • ServiceConfiguration.PROD.cscfg
  • ServiceConfiguration.TEST.cscfg

Right-click the Cloud Service project and select "Package" and select the correct Service & Build configurations for your deployment.

Note: app.configs aren't transformed in the build process unless you add an MS - See this SO answer for a technique to do a transform in the build process. Howerver, for cloud deployments, you are better off using ServiceConfiguration.*.cscfg files in combination with CloudConfigurationManager.GetSetting("settingsKey") - CloudConfigurationManager.GetSetting was added in SDK 1.7 - if in Role, it gets the value from ServiceConfig else get from web.config/app.config appSettings

0
votes

There is one trick. Perhaps someone will come in handy. Azure Worker Role builder includes files that marked as "Copy". But app.config will be transform in WorkerRole1.dll.config (or some else), that is not exists in design-time. To cheat VS:

Unload role project .csproj.

Find ItemGroup section with including files in project. I have next few:

<None Include="app.config">
  <SubType>Designer</SubType>
</None>
<None Include="app.Debug.config">
  <DependentUpon>app.config</DependentUpon>
</None>
<None Include="app.Release.config">
  <DependentUpon>app.config</DependentUpon>
  <SubType>Designer</SubType>
</None>

Then set before or after:

<Content Include="bin\$(Configuration)\WorkerRole1.dll.config">
  <CopyToOutputDirectory>Always</CopyToOutputDirectory>
  <Link>WorkerRole1.dll.config</Link>
</Content>

This trick says to builder "WorkerRole1.dll.config" is exists as a link, this one will be obtained after build (and transformations) and include in WorkerRole package.