Automating backup of a SQL Azure database to Azure Blob Storage with the help of PowerShell and Task Scheduler
Creating custom Windows event logs and sources in a Windows Azure Compute startup task

Hosting a public web site and an internal services site in one Windows Azure web role

Imagine you want to host some (Web) services in an IIS web site inside your Windows Azure compute web role. In addition, you also want to host your web application/portal in another web site in Full IIS in the *same* web role.

So, basically, the scenario looks like the following – where the endpoint of the web portal is an input endpoint which is available through the firewalls and the load balancer, but the endpoint of the services web site is an internal endpoint which is (and which should) only be reachable within our Azure application system.


Let’s take a look at a sample solution.
We have an Azure project, two web applications (for the portal and for the services) and a Contracts project which contains the WCF service and data contracts:


What we need to achieve is having two web sites configured in one web role our Azure project – one with the portal, one with the services. And we need two endpoints: one input (external) and one internal endpoint:

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="WCFinWebRoleInternalEndpoints" 
  <WebRole name="WebPortal">
      <Site name="Web">
          <Binding name="WebPortal" endpointName="WebPortal" />          
      <Site name="Services" physicalDirectory="..\BackendServices">
          <Binding name="Services" endpointName="Services" />
      <InputEndpoint name="WebPortal" protocol="http" port="80" />
      <InternalEndpoint name="Services" protocol="http" />
      <Import moduleName="Diagnostics" />

OK. Now our services web site runs inside our web role VM on a port that is not publicly available.
If you look at the internal endpoint definition you can see there is no port definition. This is because Windows Azure will assign a port dynamically.

Well, turns out this is a bit of a problem if our web portal wants to talk to the WCF services in that other (internal) web site. Which address, which port to use to talk to on the client/consumer side?

Of course there is help. The RoleEnvironment API from Windows Azure is here to heal our wounds.
What we need is the address and the port that Azure has assigned to our abstract internal endpoint (which we named ‘Services’ in the above .csdef). With this information we simply create a new endpoint URL and assign it to a WCF ChannelFactory: Here we have some generic helper method which can look at the endpoint of a ChannelFactory and adjust the address for an internal Windows Azure endpoint:

using System;
using System.ServiceModel;
using Microsoft.WindowsAzure.ServiceRuntime;

namespace Thinktecture.WindowsAzure
    public static class ChannelFactoryInternalEndpointExtensions
        public static void AdjustEndpointAddress(this ChannelFactory channelFactory, 
string internalEndpointName) { var serviceEndpoint = RoleEnvironment.CurrentRoleInstance
.InstanceEndpoints [internalEndpointName].IPEndpoint; if (serviceEndpoint != null) { var serviceAddress = channelFactory.Endpoint.Address.Uri; var serviceUrl = new Uri(String.Format("{0}://{1}:{2}{3}", serviceAddress.Scheme, serviceEndpoint.Address, serviceEndpoint.Port, serviceAddress.AbsolutePath)); channelFactory.Endpoint.Address =
new EndpointAddress(serviceUrl); } } } }

With this tiny extension method in place the actual code for calling the WCF services hosted in the internal web site looks like the following
(illustrated for both, ‘Add Service Reference…’ and a pure shared contracts with ChannelFactory scenario::

public partial class _Default : System.Web.UI.Page
    protected void Page_Load(object sender, EventArgs e)
        // ASR... approach:
        var client = new EchoServiceClient();

        var result = client.Echo("Hello Cloud");
        labelResult.Text = result;

        // ChannelFactory and shared contracts approach:
        var channelFactory = new ChannelFactory<IEchoService>("*");
        var clientChannel = channelFactory.CreateChannel();

        result = clientChannel.Echo("Hello Cloud");
        labelResult.Text = result;

Voila. Nice architectural cloud approach made real.

Hope this helps.


Feed You can follow this conversation by subscribing to the comment feed for this post.


Hi Christian,
I have implented the code - but I cannot get this to work. I am getting a 404 server error when trying to connect to the WCF service from my web role.
Can you please send me some sample code ?

Neville Parakh

Hi Christian,

Would it be possible to get the code for this sample application?

Many thanks,

Christian Weyer

Sure - you guys just send me email (you can figure out my email address, I am sure).

John Dandison

Christian - this is fantastic. Really got me out of a bind. I added a couple of lines of code for discoverability of services hosted in other roles. I folded and went the extra parameter route, but I'm sure there's a LINQ query out there that could do what I was doing without that extra parameter. Essentially finding all endpoints in all roles that match the endpoint name, not just within that role.

public static void AdjustEndpointAddress(this ChannelFactory channelFactory, string roleName, string internalEndpointName)
var appService = RoleEnvironment.Roles.Single(x => x.Key == roleName);
var endpoints = appService.Value.Instances.Where(x => x.InstanceEndpoints.ContainsKey(internalEndpointName));
//var roles = RoleEnvironment.Roles.Values.Select(x => x.Instances.Select(y => y.InstanceEndpoints.Where(z => z.Key.Equals(internalEndpointName))));
//var serviceEndpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints[internalEndpointName].IPEndpoint;
var serviceEndpoint = endpoints.First().InstanceEndpoints[internalEndpointName].IPEndpoint;
if (serviceEndpoint != null)
var serviceAddress = channelFactory.Endpoint.Address.Uri;
var serviceUrl = new Uri(String.Format("{0}://{1}:{2}{3}",
channelFactory.Endpoint.Address = new EndpointAddress(serviceUrl);

Christian Weyer

Nice :)

Verify your Comment

Previewing your Comment

This is only a preview. Your comment has not yet been posted.

Your comment could not be posted. Error type:
Your comment has been saved. Comments are moderated and will not appear until approved by the author. Post another comment

The letters and numbers you entered did not match the image. Please try again.

As a final step before posting your comment, enter the letters and numbers you see in the image below. This prevents automated programs from posting comments.

Having trouble reading this image? View an alternate.


Post a comment

Comments are moderated, and will not appear until the author has approved them.

Your Information

(Name is required. Email address will not be displayed with the comment.)