Kelly's Space

Coding 'n stuff

Deploying multiple web applications to a single Azure instance and applying web.config transforms correctly

MSDN has a good article on configuring a web role for multiple web sites. The downside to this approach is that it doesn’t use project references and msbuild to ensure that each project is built and deployed before packaging. This is because it simply includes the contents of a folder as the content for the virtual directory (or web site). What I needed was a way to ensure that the following conditions were met:

  • All web projects are rebuilt when the Azure deployment package is built
  • All web.config transforms were executed
  • Only the files needed to run the website are included

After some trial and error, I figured it out using the following approach.

I started with a solution with a single Azure web role project, called Web1, and added an additional web project called Web2:

I’m going to configure the Azure project so that it has a virtual directory called /Web2 for the Web2 project. To do this, I need to edit the ServiceDefinition.csdef file as follows:

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="Azure" 
   xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  <WebRole name="Web1" vmsize="Small">
    <Sites>
      <Site name="Web" physicalDirectory="..\Web1">
        <VirtualApplication name="Web2" physicalDirectory="..\..\..\Publish\Web2" />
        <Bindings>
          <Binding name="Endpoint1" endpointName="Endpoint1" />
        </Bindings>
      </Site>
    </Sites>
    <Endpoints>
      <InputEndpoint name="Endpoint1" protocol="http" port="80" />
    </Endpoints>
    <Imports>
      <Import moduleName="Diagnostics" />
    </Imports>
  </WebRole>
</ServiceDefinition>

The highlighted lines above are the ones that I changed. For the physicalDirectory of Web2, I specified a folder in the parent directory called ..\..\..\Publish\Web2. This folder is going to be created off of the solution root folder during build. To make the Web2 project create this folder and deploy during build, I’ll create a new msbuild target in the project file.

To edit the project file, it must first be unloaded (Right click on Web2 in Solution Explorer, and select Unload Project. Once the project is unloaded, right click on Web2 again, and select Edit Web2.csproj

This will bring up the Web2.csproj file in the editor in Visual Studio. Find the following tag:

<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />

After this tag, add the following target:

  <Target Name="PublishToFileSystem" 
          DependsOnTargets="PipelinePreDeployCopyAllFilesToOneFolder">
    <Error Condition="'$(PublishDestination)'==''" 
           Text="The PublishDestination property is not set." />
    <MakeDir Condition="!Exists($(PublishDestination))" 
             Directories="$(PublishDestination)" />
    <ItemGroup>
      <PublishFiles Include="$(_PackageTempDir)\**\*.*" />
    </ItemGroup>
    <Copy SourceFiles="@(PublishFiles)" 
          DestinationFiles="@(PublishFiles->'$(PublishDestination)\%(RecursiveDir)%(Filename)%(Extension)')" 
          SkipUnchangedFiles="True" />
  </Target>

Save the file, and reload the project by right clicking on Web2 in Solution Explorer and selecting Load Project.

The last step is to create a prebuild step in the Azure project, so that it builds Web2 with the PublishToFileSystem target. To do this, unload the Azure project in Solution Explorer, and open it up in the editor. Find the following tag:

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

After this tag, add the following :

<Target Name="BeforeBuild">
    <MSBuild Projects="..\Web2\Web2.csproj" 
             ContinueOnError="false" 
             Targets="PublishToFileSystem" 
             Properties="Configuration=$(Configuration);PublishDestination=..\Publish\Web2\;AutoParameterizationWebConfigConnectionStrings=False" />
</Target>

Before the Azure project builds, it will call msbuild on the Web2.csproj project and build the PublishToFileSystem target. It sets the PublishDestination property to the ..\Publish\Web2\ folder that the ServiceDefinition.csdef file referenced.

Save the project file, and reload the Azure project in Visual Studio.

Finally, we need to ensure that the Web2 project gets built before being published. To do this, update the project dependencies so that the Azure project depends on the Web2 project. Right click on the Solution node in Solution explorer, and select Project Dependencies

Select Azure from the Projects list, and check Web2 and click OK.

Now, when you right click on the Azure project and select “Package”, it will build and package Web1 as it normally would, and Web2 will be built and packaged as a virtual directory called “Web2″, with config tranforms being applied and only the files needed to run the application included.

Viewing the output window after packaging the Azure project verifies that the config transform were applied:

>------ Build started: Project: Azure, Configuration: Release Any CPU ------
------ Build started: Project: Azure, Configuration: Release Any CPU ------
				Transformed Web.config using Web.Release.config into obj\Release\TransformWebConfig\transformed\Web.config.
				Copying all files to temporary location below for package/publish:
obj\Release\Package\PackageTmp.

You can add as many web sites as you want to a single Azure web role using this method. All you need to do is to add the PublishToFileSystem target to the new web project, and add an additional msbuild task to the BeforeBuild target in the Azure project file. You can download the source use in this sample here (Updated for VS2012). Hope this helps!

About these ads

17 responses to “Deploying multiple web applications to a single Azure instance and applying web.config transforms correctly

  1. Pete Claar (@pclaar) November 5, 2012 at 9:04 am

    This article was a huge help. Thanks for posting it!

  2. Darren Mart November 28, 2012 at 10:20 am

    Hi, this worked beautifully in VS2010/Azure SDK 1.6. After upgrading to VS2012 and SDK 1.8, I now get the following when trying to run the local Azure Emulator:

    Cannot find the physical directory ‘C:\Source\[AzureProject]\bin\Publish\Web2′ for virtual path Web/Web2/. C:\Source\[AzureProject]\bin\Debug\ServiceDefinition.csdef

    Any ideas for a quick fix? Thanks!

    • kellyhpdx November 28, 2012 at 10:53 am

      Hi Darren,

      I just ran into this same issue as well. It turns out that the current working directory for the Azure project changed to be bin\debug or bin\release during the build instead of the project directory. This means that the relative path that you have in the ServiceDefinition.csdef needs to be ..\..\..\publish\web2 instead of ..\publish\web2.

      Kelly

  3. Darren Mart November 28, 2012 at 1:15 pm

    That was indeed the issue, thanks for the response and for the excellent article, it’s been a huge time-saver!

  4. Kadosh Alejandro (@kadoshivan) December 14, 2012 at 9:29 am

    Applying this workaround is causing in my final package to have to instances of the same Web Project in “sitesroot” folder of the package.

    The result is that, for instance, if have a WebProject2, then in “sitesroot” folder I will have one folder with the content of this workaround and another one with the normal content without this workaround. It is combining this workaround with the normal process.

    Any suggestions?

    • kellyhpdx December 14, 2012 at 10:28 am

      Hi Kadosh,

      I would recommend you delete your Publish\ folder and try republishing. The approach that I outline here doesn’t clear out the folder during build, so it is possible that it is picking up old content. Your sitesroot should have two folders in it, but both of them should contain the publish versions with transformed web.config files.

      I’ve updated the source for this to use VS2012 and you can download a new version (link at the end of the article) to compare with your solution if deleting the publish folder doesn’t help.

      Good luck!

  5. Pingback: Building and Packaging Virtual Applications within Azure Projects - DavidHardin - Site Home - MSDN Blogs

  6. David Hardin January 18, 2013 at 3:51 pm

    Thanks for the post. I encountered the problem Darren Mart described above but the fix does not work with TFS Build. It only works for local Visual Studio packaging.

    I took your approach and expanded upon it so that it works with TFS Build. I blogged about it on MSDN as “Building and Packaging Virtual Applications within Azure Projects”:

    http://blogs.msdn.com/b/davidhardin/archive/2013/01/18/building-and-packaging-virtual-applications-within-azure-projects.aspx

    • kellyhpdx January 18, 2013 at 6:43 pm

      Excellent, thanks for the link!

      The one additional step I would like to include is one to delete the publish destination files before re-publishing. Not deleting them can cause files that you have deleted from your project to remain there with some interesting side effects (especially if they’re JavaScript and you use default bundles).

      Great article, David!

      • anilpurswani July 22, 2013 at 9:11 pm

        It is throwing error – “ProjectReferenceWithConfiguration” does not define a value for meta
        data “Name” ….. but if added the web2 as a webrole in azure project then it doesn’t throws this error…..so what is wrong with it?

    • anilpurswani July 22, 2013 at 9:15 pm

      About Virtual Applications, I tried your suggestions but still it throws error – “C:\Builds\1\beconnect\Srv_myservice\Sources\Source\Services\MyServiceAzure\bin\Dev\ServiceDefinition.csdef: Cannot find the physical directory ‘C:\Builds\1\beconnect\Srv_MyService\Sources\Source\Services\MyServiceAzure\bin\Dev\_PublishedWebsites\MyApps’ for virtual path Web/MyApp/”

  7. Pingback: Building and Packaging Virtual Applications within Azure Projects | MSDN Blogs

  8. gurunguns February 28, 2013 at 4:49 am

    Thanks for the post. Is it possible to implement similar functionality with worker role i.e. to deploy multiple project in single Worker role ??
    Please let me know the approach, as I have multiple worker role which I have to deploy in different instances.

  9. noamberda August 5, 2013 at 7:55 pm

    If i am using a custom build configuration this doesnt work. The build task in the azure project file is using the Release or Debug instead of the select publish build configuration.
    How i can use the publish build configuration in build task?

    Noam

    • kellyhpdx August 6, 2013 at 7:12 am

      You can specify the build configuration by switching to the builds tab in the Team Explorer window in Visual Studio, then right-clicking on your build definition and select “Edit build definition..” Then select the “Process” tab on the left, and it will display a grid with the build process parameters. In section 3 (Advanced), you can add additional MSBuild arguments. If you want to have it build a custom build configuration, specify it like this:

      /p:Configuration=xxxx

      where xxxx is the custom build configuration.

      Kelly

      • noamberda August 6, 2013 at 5:40 pm

        Thanks Kelly,
        Do you think it is possible to move the Azure build tasks to the publish process? It seems that when you publish or package there is a second process that is running after the build. If we add those tasks to the publish process.

        I would like to use as well the publish / package selected build configuration in the azure build task. In the package for example I can select the build configuration for this publish. I would like to use that. Microsoft somehow are using it within the publish process.

        Noam

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: