Azure Multi-Tenant Addressing

There is a great thread on the MSDN forums about single-tenant vs. multi-tenant. It’s definitely worth the read. There is also a multi-tenant Azure sample available here. Thanks to smarx for helping me think through some of this.

 

We are building an Azure-based, multi-tenant web prototype and came across an issue that is worth considering. In particular, how do you address the website based on your tenancy? We identified four ways to accomplish this – I would be interested if you know of other good techniques.

 

The first one that came to mind was to use a sub-domain to specify the tenant (tenant1.azureapp.com, tenant2.azureapp.com, etc). I have done systems like this in the past and it worked very well. However, Azure complicates things a little bit. Currently, Azure does not give you a specific IP address, but rather gives you an FQDN like coolapp.cloudapp.net. I was not able to figure out a way to automatically map all hosts to this address as the A record is expecting an IP address. Assuming I didn’t miss something, this means that you have to create a CNAME DNS entry for each tenant. This isn’t a big deal for a “small” number of tenants, and there are use cases where the CNAME will actually be in the tenant’s domain. However, for a “large” number of tenants, this seems like additional management and could perhaps cause DNS issues (imagine 10K CNAMES in a domain).

 

The next option I explored, and ultimately decided on, was to use the tenant name in the url (coolapp.cloudapp.net/tenant1/default.aspx, coolapp.cloudapp.net/tenant2/profile.aspx, etc.)

 

Another option thrown out was to map the tenant based on the Live ID. This became very complex in scenarios where a Live ID was valid in multiple tenants, so it didn’t work for us, but this could be a good option for some.

 

Another option was to specify the tenant name or ID in the query string. This works fine, but didn’t seem as user friendly as the first two options. It turns out that this is exactly how we are implementing it under the covers, but I wanted to provide a more user friendly method. This is by far the easiest implementation.

 

URL Rewriting

The option I ended up choosing basically maps a URL from coolapp.cloudapp.net/tenant1/default.aspx to coolapp.cloudapp.net/app/default.aspx?tenant=tenant1. The initial plan was to use the IIS URL rewriting module to implement this feature. Unfortunately, Azure doesn’t currently run this module, but since this is a commonly requested feature, hopefully it will make it into a future version of Azure.

 

There are several page frameworks, including the ASP.NET MVC framework that provide URL rewriting capabilities. It is also easy to implement URL re-writing in code. In the Application_BeginRequest method in global.asax, use the HTTPContext.Current.RewritePath method to do the mapping for you.

 

Our logic is pretty straight forward:

If the request begins with “/app/”, redirect to “/”. I don't want people accessing our app pages directly, which gives us the freedom to version and change the mappings later. 

 

If the request is in the root web folder or in the images web folder, ignore it.

 

Otherwise, extract the tenant id and the page from the URL. The URL is in the format coolapp.cloudapp.net/{tenant}/{page}. Make sure to deal with the coolapp.cloudapp.net/{tenant}/ request.

 

Look the tenant id up in a database, and, if the id is valid and the page exists in the app folder, map to /app/{page}?tenantID={tenant}

 

If the tenant id is not valid or if the page doesn’t exist, just let the request pass through and IIS will return a 404 error.

 

Two Things I Learned

In general, this was a pretty simple task to implement. It isn’t fully tested yet, but so far, I’ve only hit two snags.

 

The first, mentioned above, is to make sure to explicitly deal with the coolapp.cloudapp.net/{tenant}/ URL. 

 

The second, and maybe there’s a way to fix this, is to adjust the form Action property to point to the friendly URL instead of the actual URL. I used this piece of code in the Page_Load event of all of the ASPX files in the /app/ folder:

 

form1.Action = "/" + Request.QueryString["tenantID"] + Request.Path.Substring(Request.Path.LastIndexOf("/"));