This blog post could also be entitled “You suck CloudBerry labs”.

Rant Alert

Ok so first let me get this CloudBerry Labs business off my chest . I have purchased 2 licenses from you. I am a developer so I re-image my machines more than your average punter. Of course when I do I always forget to undo the activation through the app. My Bad!

However, when I email you through your online form. email you directly multiple times, tweet you and get ZERO response over the course of several weeks I start to think you as a company suck!

So here I am with 2 paid licenses and now I can’t use them because you are ignoring your customers.

Let this be a lesson to anyone else considering forking over the $39.99 US for Windows Azure Explorer PRO

The Good Shiz

Ok, I feel slightly better now.

Right, so why did I need Windows Azure Explorer PRO. I needed it because it offered a pretty easy to use interface for managing Blob Storage on Azure and specifically setting HTTP Headers as a bulk operation. The free version of the product never used to allow saving of these headers but it appears that it does now and I only discovered that after writing this post. That however is not the point. I have paid for the Pro version and want to use the additional features.

So I set about writing a little console app this morning that would do all the heavy lifting for me and here it is below in all it’s glory split in to the main program and the worker class.

Essentially the program looks for all containers at the specified storage account and then uses the Task Parallel Library (TPL) to execute a method on the SetCacheControl class for each Container. Each container is enumerated on it’s own thread in parallel setting the cache-control header on each the blob item.



Main

using Microsoft.WindowsAzure; using Microsoft.WindowsAzure.Storage; using Microsoft.WindowsAzure.Storage.Auth; using Microsoft.WindowsAzure.Storage.Blob; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace BondiGeek.Azure { class Program { staticvoid Main(string[] args) { ListBlobsInContainer(); Console.ReadLine(); } staticvoid ListBlobsInContainer() { // Retrieve storage account from connection string. Microsoft.WindowsAzure.Storage.CloudStorageAccount storageAccount = Microsoft.WindowsAzure.Storage.CloudStorageAccount.Parse("DefaultEndpointsProtocol=https;AccountName=;AccountKey="); // Create the blob client. CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); //Create a Task for each container and execute in parallel.int containerCount = blobClient.ListContainers().ToList().Count; Task[] tasks = new Task[containerCount]; int index = 0; foreach (var item in blobClient.ListContainers()) { SetCacheControl worker = new SetCacheControl(item); tasks[index] = Task.Factory.StartNew(() => worker.EnumerateBlobs()); index++; } Task.WaitAll(tasks); } } }

Worker Class

using Microsoft.WindowsAzure; using Microsoft.WindowsAzure.Storage; using Microsoft.WindowsAzure.Storage.Auth; using Microsoft.WindowsAzure.Storage.Blob; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace BondiGeek.Azure { publicclass SetCacheControl { string cacheControl = "max-age=604800, must-revalidate"; CloudBlobContainer container { get; set; } public SetCacheControl() { } public SetCacheControl(CloudBlobContainer container) { this.container = container; } publicvoid EnumerateBlobs() { foreach (var blobItem in container.ListBlobs("", true, BlobListingDetails.All, null, null)) { if (blobItem is CloudBlockBlob) { ((CloudBlockBlob)blobItem).Properties.CacheControl = cacheControl; ((CloudBlockBlob)blobItem).SetProperties(); } Console.WriteLine(blobItem.Uri); } } } }