Here is an approach to providing ASP.NET server-side customization data post-deployment in Azure. It is particularly applicable to multi-tenant solutions, where individual users may need to be served pages using different master pages, user controls, html pages, themes, etc. It is also applicable to a single-tenant solution. The common factor is the ability to provide this customization post-deployment, i.e. in such a way that these files can be added to and modified after the web role package is deployed without the need to re-deploy it.
The approach is based on ASP.NET's VirtualPathProvider (http://msdn.microsoft.com/en-us/library/system.web.hosting.virtualpathprovider.aspx; thanks to Cliff Green for suggesting this). This class allows the developer to provide own logic for locating and reading a variety of files used by ASP.NET. (In the sample implementation attached to this posting I am using a url parameter supplied by the user as part of page request to choose different master layout pages to respond to the request). The implementation is not completely straightforward when deployed to Azure: in fact, what worked in the emulator did not work in the cloud. Here are some work-arounds/considerations I have used:
1. According to the documentation "typically, a instance is registered in an AppInitialize method defined in the App_Codedirectory,...". However, that directory, or more importantly the code it contains is never deployed into Azure and so registration never happens. Therefore, in my implementation the VirtualPathProvider is registered according to the method suggested in the remainder of the above statement: "...or during the Application_Start event in the Global.asax file".
2. There seems to be a bug, which leads ASP.NET to try to start a directory watcher for the virtual directory handled by the VirtualPathProvider. That obviously fails since the directory does not exist as a physical folder in the ASP.NET structure. There is a KB article referencing this problem (http://support.microsoft.com/kb/957661 ) except that it makes a reference to the use of ScriptManager, which I did not have and has a solution relying on a hotfix which I could not deploy to Azure. My workaround was therefore to create a dummy folder with the same name as the one I would later use to identify the root folder for my customization files (in the sample it's name is 'blob'. Furthermore, that folder would not be deployed to Azure unless it contained at least one file (not a source code) so in my sample it contains just an empty text file.
3. In the attached sample all the customization files are stored in the "custom" blob container. The path to each customization file is "/blob.<tenant id>.<filename>,<ext> though of course you are free to extend this scheme by changing the code included in the sample in the CustomizationFileProvider clas library. It is important however that you do not use "/" in the path as that will cause ASP.NET to look for this path on disk to set up a directory watcher (see #2 above). Otherwise, the path can be as complex as you want and as Azure blob storage allows (note that blob file names need to be lower case!).
Once you have deployed this solution, you can add/remove/change files in the blob storage and the changes should be reflected in what the website serves based on the value of the url parameter company, e.g. http://mysite/default.aspx?company=company1will cause the web role to use /blob.company1.user.master master page (I have included some samples in the customization folder - just remeber to upload them with lower case names! Also, before deploying the sample modify relevant storage and if needed remote desktop keys and user names).
The provided simple implementation will read blob storage every time a customization page is requested. A possible enhancement would be to cache these pages in the web role's local storage.