Back to Insights

Using Windows Azure Blob Storage and Sitefinity 3.7

Lately, one of the most common threads seen on techie blogs has to do with so-called cloud computing. One of the main features that makes cloud computing attractive is the possibility that it will allow developers to focus on development, rather than infrastructure. Basically, resources can be allocated and released as necessary and you are charged accordingly. 

Not everyone will be tempted to instantly move solutions to the cloud, especially when sensitive data is involved. However, it is possible to develop solutions that use both on premise and cloud infrastructure. Such as — this blog. In this post, our focus is on how to use Azure with Binary Large Objects (BLOB) storage.[1]

We have used Sitefinity 3.7[2] as a CMS with all of our blobs now hosted in Azure. When this idea first appeared, my question was "how will we do it?" Creating and migrating everything that we have to a Windows Azure Solution in Visual Studio 2010 wasn't an option because we didn't want to host our whole site there. Our second thought was to create our own library using the tools provided with the Azure SDK, but it still didn't seem like the right thing to do as we really wanted to keep it simple. We did know that, in order to get there, we needed to develop a connector for Sitefinity that encapsulated all of the necessary interactions with Azure.

Fortunately, Azure gives us the possibility of using a REST[3] protocol interface to perform all of the needed operations within the platform. Basically, we send HTTP requests with commands and data, and receive responses back, all with plain old simple .NET C# with no need to setup additional libraries or environments.

The not-so-obvious part is properly setting the shared key, understanding the structure of the requests and proceeding from there.[4]

Every time that a request has to be sent to Azure, it has to be done following this model (please note that we are not using the lite version of the authentication scheme):

StringToSign = VERB + "\n" +
               Content-Encoding + "\n"
               Content-Language + "\n"
               Content-Length + "\n"
               Content-MD5 + "\n" +
               Content-Type + "\n" +
               Date + "\n" +
               If-Modified-Since + "\n"
               If-Match + "\n"
               If-None-Match + "\n"
               If-Unmodified-Since + "\n"
               Range + "\n"
               CanonicalizedHeaders +

Perhaps the most challenging part is to correctly define the authorization header and the canonical string. Please check below for one suggestion of implementation (note that non-relevant parts of this code were removed):

        private void AddAuthorizationHeader(WebRequest webRequest, Request request)
            if (webRequest.Headers[WindowsAzureServiceUtils.HeaderDate] == null)
                webRequest.Headers.Add(WindowsAzureServiceUtils.HeaderDate, WindowsAzureServiceUtils.GetHttpDateInRfc1123Format());

            if (webRequest.Headers[WindowsAzureServiceUtils.HeaderPrefixMS+"version"] == null)
                webRequest.Headers.Add(WindowsAzureServiceUtils.HeaderPrefixMS + "version", "2009-09-19");

             String signature = string.Empty;

            using (HMACSHA256 hmacSha256 = new HMACSHA256(AzureStorageConstants.Key))
                string canonicalizedString = WindowsAzureServiceUtils.MakeCanonicalString(request.ContainerName, request.Key, webRequest);
                Byte[] dataToHmac = System.Text.Encoding.UTF8.GetBytes(canonicalizedString);
                signature = Convert.ToBase64String(hmacSha256.ComputeHash(dataToHmac));

            String authorizationHeader = String.Format(
                  "{0} {1}:{2}",

          webRequest.Headers.Add(HttpRequestHeader.Authorization, authorizationHeader);

        public static string MakeCanonicalString(HttpWebRequest request, string containerName, string key, SortedList queryParams, SortedList headers, string expires)
            const string CarriageReturnLineFeed = "\n";
            StringBuilder stringToSign = new StringBuilder();

            // VERB
            stringToSign.Append(request.Method + CarriageReturnLineFeed);

            // Content-Encoding

            // Content-Language

            // Content-Length
            if (request.Method == "GET")
                stringToSign.Append(request.ContentLength + CarriageReturnLineFeed);
            // Content-MD5

            // Content-Type
            stringToSign.Append(request.ContentType + CarriageReturnLineFeed);

            // Date

            // If-Modified-Since

            // If-Match

            // If-None-Match

            // If-Unmodified-Since

            // Range

            // Construct canonicalized headers
            // Look for header names that start with x-ms then sort them in case-insensitive manner
            List httpStorageHeaderNameArray = new List();
            foreach (string headerKey in request.Headers.Keys)
                if (headerKey.ToLowerInvariant().StartsWith(HeaderPrefixMS, StringComparison.Ordinal))


            // Now go through each header's values in sorted order and append them to the canonicalized string.
            // At the end of this, you should have a bunch of headers of the form x-ms-somekey:value
            foreach (string storageHeaderKey in httpStorageHeaderNameArray)
                stringToSign.Append(storageHeaderKey + ":" + request.Headers[storageHeaderKey] + CarriageReturnLineFeed);

            // Finally, add canonicalized resources
            // This is done bt prepending a '/' to the account name and resource path.

            stringToSign.Append("/" + AzureStorageConstants.Account + request.RequestUri.AbsolutePath);

            if (request.Address.AbsoluteUri.Contains("comp"))
                if (request.Address.AbsoluteUri.Contains("list"))
                    stringToSign.Append(CarriageReturnLineFeed + "comp:list");
                else if (request.Address.AbsoluteUri.Contains("acl"))
                    stringToSign.Append(CarriageReturnLineFeed + "comp:acl");
                else if (request.Address.AbsoluteUri.Contains("metadata"))
                    stringToSign.Append(CarriageReturnLineFeed + "comp:metadata");
                else if (request.Address.AbsoluteUri.Contains("properties"))
                    stringToSign.Append(CarriageReturnLineFeed + "comp:properties");

            if (request.Address.AbsoluteUri.Contains("restype"))
                if (request.Address.AbsoluteUri.Contains("container"))
                    stringToSign.Append(CarriageReturnLineFeed + "restype:container");

            return stringToSign.ToString();

A possible outcome for this could be as follows (this is a request to create a block blob):

Request Syntax:

Request Headers:
x-ms-version: 2009-09-19
x-ms-date: Sun, 27 Sep 2009 22:33:35 GMT
Content-Type: text/plain; charset=UTF-8
x-ms-blob-type: BlockBlob
x-ms-meta-m1: v1
x-ms-meta-m2: v2
Authorization: SharedKey myaccount:YhuFJjN4fAR8/AmBrqBz7MG2uFinQ4rkh4dscbj598g=
Content-Length: 11

Request Body:
hello world

This example implementation should be enough for coding the needed operations regarding blobs and their containers. For further information, please refer to the Blob Service API reference .

[1] For more information about Azure, go to Microsoft Windows Azure site.

[2] Want to know more about Sitefinity? Check Telerik website.

[3] REST protocol at Wikipedia and Azure REST Reference.

[4] I highly recommend using Fiddler to monitor requests and responses.