With version 1.3 of the Azure SDK, it became possible to run multiple and composite websites inside a single Web Role. This is achieved using the features many web developers are familiar with from IIS:

  • Web Site
  • Virtual Applications
  • Virtual Directories

If you’re not familiar with the nuances of what you can and can’t achieve with each of these types of IIS entity the subtler details (particularly between the last two) are described in a detailed blog by Wenlong Dong.

Above, I made a differentiation between Multiple Web Sites and Composite Web Sites. This I mean to be that Multiple Web Sites are two input endpoints, such as a website running on a different port or on the same port with a host header separating incoming traffic. Composite Web Sites I mean to be a single Web Site running on a single endpoint (say port 80) but with different applications running in subdirectories of the website (http://blog.bareweb.eu and http://blog.bareweb.eu/myApplication1/). These can be configured at the same time, and I will start off describing Composite Web Sites and move onto Multiple WebSites.

The new functionality in 1.3 is very powerful, and a game changer when it comes to the financial outlay that Azure represents – you can suddenly use a single role to host a number of lightweight applications and maintain their separation. However, the feature at the time of writing is not particularly well documented, and so I will run through an example of how to achieve the required configuration.

Composite Web Sites

Firstly, create a new Azure Project and add a single Web Role.

Create Master Web Role

Create Master Web Role

This gives you a basic application and all the required ServiceDefinition and ServiceConfiguration files. We will be modifying the ServiceDefinition.csdef file later in order to enable the multiple roles.

For now, we should create the other web sites that we want to run as a VirtualDirectory or VirtualApplication. It may be the case that you are already have applications that you want to add in, and so you have to use “Add Existing Web Site”. However, I am adding new, so I right-click on the solution in Solution Explorer, select Add and then New Web Site …

Add New Website

Add New Website

I will add two new Websites to the solution, one as a VirtualApplication and one as a VirtualDirectory. When you are creating these projects (or adding Existing) it is worth bearing in mind that the paths of the solutions should be trivially relative to the path of the ServiceDefinition.csdef file, as if you choose to use relative paths then this is the relative base of the path. Firstly, create the ChildVirtualApplication website.

Add WebSite for Virtual Application

Add WebSite for Virtual Application

Then we can create an ASP.NET Empty Web Site that will be used for our VirtualDirectory. In this example I’ll put an image in the project but little else, as a common use for Virtual Directories is to bring in shared assets or other such items.

Add Virtual Directory WebSite

Add Virtual Directory WebSite

It’s important to note that we added these sites DIRECTLY into the solution, not using the Azure Tools to add new Roles, as doing so is not what we are trying to achieve.

Once we’ve added these sites, our Solution will look as below:

Completed Solution Structure

Completed Solution Structure

Now, if we started our application we would only get the MasterWebRole running. There would be no way of accessing the AssetsVirtualDirectory or ChildVirtualApplication through the DevFabric. The same would apply if we deployed to Azure, only MasterWebRole would be transferred and would run. Note: with the default configuration you will be able to access the new Web Sites through the ASP.NET Development Server, but this is not through Azure Dev Fabric and so is not what we really want.

Asp.Net Dev Server is available

Asp.Net Dev Server is available

You can configure your projects not to start the ASP.NET Development Server by right-clicking each of the new Projects and selecting the following options:

Disable ASP.NET Development Server

Disable ASP.NET Development Server

To resolve this, we need to actually modify the ServiceDefinition.csdef file to enable the multiple websites within the WebRole. This sentence actually describes quite what we have to do, we need to find the <WebRole/> node and within that the <Sites/> child node. Inside this, there should be one <Site/> defined, with a value similar to the below:

<Site name="Web">
<Bindings>
<Binding name="Endpoint1" endpointName="Endpoint1" />
</Bindings>
</Site>

This shows a single Site, with a single Endpoint, which exactly matches our experience when we start our application. What we really want is for the new Web Sites we added to our Project to run on subdirectories off the root of the MasterWebRole application:

  • / runs MasterWebRole
  • /Child/ runs ChildVirtualApplication
  • /Assets/ runs AssetsVirtualDirectory

We can achieve this very simply by adding VirtualDirectory and VirtualApplication nodes to the <Site/> node listed above. Firstly we will add in the /Child/ VirtualApplication, note that as earlier mentioned, the relative path is relative to the ServiceDefinition.csdef file and so may vary depending on where you place your Web Sites. You can use Absolute paths if you find it easier.

The xml to set up the VirtualApplication is:

<VirtualApplication name="child" physicalDirectory="../ChildVirtualApplication" />

The name property specified the subdirectory that we can use to access VirtualApplication, the physicalDirectory is the path to the child application.

The xml to set up the Virtual Directory is:

<VirtualDirectory name="assets" physicalDirectory="../AssetsVirtualDirectory" />
This makes the <Site/> node complete, look like:
<Site name="Web">
        <VirtualApplication name="child" physicalDirectory="../ChildVirtualApplication" />
        <VirtualDirectory name="assets" physicalDirectory="../AssetsVirtualDirectory" />
        <Bindings>
          <Binding name="Endpoint1" endpointName="Endpoint1" />
        </Bindings>
</Site>

Now, when we launch the DevFabric application, we can navigate to “/child/” and request the image at “/assets/Images/Azure.png” and the files are request successfully via DevFabric.

Master Web Role shows /assets/ image

Master Web Role shows /assets/ image

And in the Child Application:

Child application running on subdirectory

Child application running on subdirectory

Multiple Web Sites

In the example up to now, we have only shown what can be achieved within a single <Site/>. This will let you add a new site that runs at the root of the domain, which may be a requirement for some applications. To do this, we will need to add a new SecondMasterSite, as so:

Add additional site

Add additional site

Once we have this SecondMasterSite available, we will create a new entry for it in the ServiceDefinition.csdef and add in a new <Binding/> and associated <InputEndpoint/>so that we can load the new Site on a brand new port.

<Site name="Second" physicalDirectory="../SecondMasterSite">
<Bindings>
<Binding name="Endpoint2" endpointName="Endpoint2" />
</Bindings>
</Site>

<InputEndpoint name="Endpoint2" protocol="http" port="81" />

In this way we can specify that the new Endpoint should run on port 81. Note that when using DevFabric, the actual port that the application runs on may vary if the port requested is not available.

We are free to add in additional VirtualDirectories and VirtualApplications into this second Site just as we are with the first one. Therefore we can bring in the assets and child application from the first example into the second.

<Site name="Second" physicalDirectory="../SecondMasterSite">
<VirtualApplication name="child" physicalDirectory="../ChildVirtualApplication" />
<VirtualDirectory name="assets" physicalDirectory="../AssetsVirtualDirectory" />
<Bindings>
<Binding name="Endpoint2" endpointName="Endpoint2" />
</Bindings>
</Site>
This gives us the <WebRole/> node as follows - note that the Imports Diagnostics is not important for this example:
<WebRole name="MasterWebRole">
<Sites>
<Site name="Web" physicalDirectory="../MasterWebRole">
<VirtualApplication name="child" physicalDirectory="../ChildVirtualApplication" />
<VirtualDirectory name="assets" physicalDirectory="../AssetsVirtualDirectory" />
<Bindings>
<Binding name="Endpoint1" endpointName="Endpoint1" />
</Bindings>
</Site>
<Site name="Second" physicalDirectory="../SecondMasterSite">
<VirtualApplication name="child" physicalDirectory="../ChildVirtualApplication" />
<VirtualDirectory name="assets" physicalDirectory="../AssetsVirtualDirectory" />
<Bindings>
<Binding name="Endpoint2" endpointName="Endpoint2" />
</Bindings>
</Site>
</Sites>
<Endpoints>
<InputEndpoint name="Endpoint1" protocol="http" port="80" />
<InputEndpoint name="Endpoint2" protocol="http" port="81" />
</Endpoints>
<Imports>
<Import moduleName="Diagnostics" />
</Imports>
</WebRole>

Now when we run this, Visual Studio will automatically load a new web browser for our new InputEndpoint (helping us determine the port if it is not exactly as specified).

You may receive an error when running this, due to the web.config not allowing compilation for debug by default. Change the System.WebCompilation attribute to  debug=”true” if you get this error:

Error due to not being able to debug

Error due to not being able to debug

Set Debug=True in web.config of new application

Set Debug=True in web.config of new application

This gives the below operating website (on port 82 for me):

Second Website running

Second Website running

Once you overcome this issue (if you encountered it at all), you can begin to consider some more advanced ways of differentiating InputEndpoints. For example, it is not necessary to differentiate between <Site/> entries using a port – instead you can use host headers – the HTTP header send by requesting browsers to an application which includes the domain name they are trying to access. IIS is able to detect this header and automatically route traffic to the correct website based on this, even when the sites are running on the same port. Therefore you do not need to run on separate ports, but instead can use a single InputEndpoint, but specify different hostheaders as per the below. Note that for this to work, you should set up DNS entries that match the remote IP addresses, or for local use, modify the HOSTS file.

<WebRole name="MasterWebRole">
<Sites>
<Site name="Web" physicalDirectory="../MasterWebRole">
<VirtualApplication name="child" physicalDirectory="../ChildVirtualApplication" />
<VirtualDirectory name="assets" physicalDirectory="../AssetsVirtualDirectory" />
<Bindings>
<Binding name="Endpoint1" endpointName="Endpoint1" hostHeader="www.firstwebsite.com" />
</Bindings>
</Site>
<Site name="Second" physicalDirectory="../SecondMasterSite">
<VirtualApplication name="child" physicalDirectory="../ChildVirtualApplication" />
<VirtualDirectory name="assets" physicalDirectory="../AssetsVirtualDirectory" />
<Bindings>
<Binding name="Endpoint1" endpointName="Endpoint1" hostHeader="www.secondwebsite.com" />
</Bindings>
</Site>
</Sites>
<Endpoints>
<InputEndpoint name="Endpoint1" protocol="http" port="80" />
</Endpoints>
<Imports>
<Import moduleName="Diagnostics" />
</Imports>
</WebRole>

Packaging for Azure

So far your DevFabric will be running perfectly well, but the eagle eyed amongst you will notice that each application is actually running directly off the source. This isn’t ideal, and if you were to try to deploy this to Azure you would be deploying the source directly. Instead, you should publish each of your WebSites to a location and then reference these files. I achieve this simply by adding a new folder at the same level as all of the new applications called “Publish” (path from ServiceDefinition.csdef is “../Publish/”) and then publishing each application to there. This makes updating the ServiceDefinition.csdef easy, as “../SecondMasterSite” simply becomes “../Publish/SecondMasterSite”.

The benefit of not Packaging is that you can make changes at runtime in some cases to the applications, but you must always remember to republish locally before you deploy to Azure.

<WebRole name="MasterWebRole">
<Sites>
<Site name="Web" physicalDirectory="../MasterWebRole">
<VirtualApplication name="child" physicalDirectory="../Publish/ChildVirtualApplication" />
<VirtualDirectory name="assets" physicalDirectory="../Publish/AssetsVirtualDirectory" />
<Bindings>
<Binding name="Endpoint1" endpointName="Endpoint1" hostHeader="www.firstwebsite.com" />
</Bindings>
</Site>
<Site name="Second" physicalDirectory="../Publish/SecondMasterSite">
<VirtualApplication name="child" physicalDirectory="../Publish/ChildVirtualApplication" />
<VirtualDirectory name="assets" physicalDirectory="../Publish/AssetsVirtualDirectory" />
<Bindings>
<Binding name="Endpoint1" endpointName="Endpoint1" hostHeader="www.secondwebsite.com" />
</Bindings>
</Site>
</Sites>
<Endpoints>
<InputEndpoint name="Endpoint1" protocol="http" port="80" />
</Endpoints>
<Imports>
<Import moduleName="Diagnostics" />
</Imports>
</WebRole>

Finally

I hope this has been a useful reference. The documentation for this at the time of writing is not entirely clear, but what I did find useful was the Service Definition Schema reference. For further reading, you can try the training kit.

I have attached my solution to this post. It is not packaged for Azure and so should work straight away on DevFabric. MultipleWebSites

About these ads