Windows Azure Management Client Libraries for .NET and It Supports Portable Class Library

One of the missing pieces of the Windows Azure story is within our reach now! A few days ago Azure folks have released Windows Azure .NET Management Client Libraries
30 October 2013
6 minutes read

Related Posts

One of the missing pieces of the Windows Azure story is within our reach now! Awesome Azure folks have released Windows Azure .NET Management Client Libraries which also support portable class library (PCL). It is now possible to build custom Windows Azure management applications in .NET ecosystem with possible minimum effort. Brady Gaster has a very detailed blog post about the first release of these libraries if you would like to get started with it really quickly. In this post, I'll look at the these libraries in a web developer point of view.

Management libraries are available as NuGet packages and they are at the pre-release stage for now (ignore the HdInsight package below):

image

Some packages are there for the infrastructural purposes such as tracing, exception handling, etc. and other packages such as Storage and Compute has .NET clients to manage specific services which is good and bad at the same time. When we take a first look at the libraries, we can see a few great things:

  • All operations are based on the HttpClient. This gives us the opportunity to be very flexible (if the client infrastructure allows us) by injecting our inner handler to process the requests.
  • All client operations are asynchronous from top to bottom.
  • It supports Portable Class Library.

Getting started is also very easy. The following code is all I needed to do to query the storage accounts I have inside my subscription:

static void Main(string[] args)
{
	const string CertThumbprint = "your-cert-thumbprint";
	const string SubscriptionId = "your-subscription-id";
	X509Certificate2 cert = Utils.FindX509Certificate(CertThumbprint);
	SubscriptionCloudCredentials creds = 
		new CertificateCloudCredentials(SubscriptionId, cert);

	StorageManagementClient storageClient = 
		CloudContext.Clients.CreateStorageManagementClient(creds);

	StorageServiceListResponse response = storageClient.StorageAccounts.List();
	foreach (StorageServiceListResponse.StorageService storageService in response)
	{
		Console.WriteLine(storageService.Uri);
	}

	Console.ReadLine();
}

The client certificate authentication is performed to authenticate our requests. Notice that how easy it was to get a hold of a storage client. By using the CloudContext class, I was able to create the storage client by using the CreateStorageManagementClient extension method which lives under the Microsoft.WindowsAzure.Management.Storage assembly.

How HttpClient is Used Underneath

If we look under the carpet, we will see that each time I call the CreateStorageManagementClient, we'll use a different instance of the HttpClient which is not we would want. However, I assume that StorageManagementClient's public members are thread safe which means that you can use one instance of that class throughout our web applications but I'm not sure. Nevertheless, I would prefer an approach which is similar to the following one:

// single instance that I would hold onto throughout my application lifecycle. I would most
// probably handle this instance through my IoC container.
using (CloudContext cloudContex = new CloudContext(creds))
{
    // These StorageManagementClient instances are either the same reference or 
    // use the same HttpClient underneath.
    StorageManagementClient storageClient = 
        cloudContex.CreateStorageManagementClient();
}

On the other hand, we need to perform a similar operation to create a compute management client to manage our could service and virtual machines: call the CreateComputeManagementClient extension method on the CloudContext.Clients. Here, we have the same behavior in terms of HttpClient instance.

A Sample Usage in an ASP.NET MVC Application

I created a very simple ASP.NET MVC application which only lists storage services, hosted services and web spaces. I used an IoC container (Autofac) to inject the clients through the controller constructor. Below is the registration code I have in my application.

protected void Application_Start()
{
    // Lines removed for brevity...

    // Get a hold of the credentials.
    const string CertThumbprint = "your-cert-thumbprint";
    const string SubscriptionId = "your-subscription-id";
    X509Certificate2 cert = FindX509Certificate(CertThumbprint);
    SubscriptionCloudCredentials creds =
        new CertificateCloudCredentials(SubscriptionId, cert);

    // Get the ContainerBuilder ready and register the MVC controllers.
    ContainerBuilder builder = new ContainerBuilder();
    builder.RegisterControllers(Assembly.GetExecutingAssembly());

    // Register the clients
    builder.Register(c => CloudContext.Clients.CreateComputeManagementClient(creds))
        .As<IComputeManagementClient>().InstancePerHttpRequest();
    builder.Register(c => CloudContext.Clients.CreateStorageManagementClient(creds))
        .As<IStorageManagementClient>().InstancePerHttpRequest();
    builder.Register(c => CloudContext.Clients.CreateWebSiteManagementClient(creds))
        .As<IWebSiteManagementClient>().InstancePerHttpRequest();

    // Set the dependency resolver.
    AutofacDependencyResolver dependencyResolver = 
        new AutofacDependencyResolver(builder.Build());

    DependencyResolver.SetResolver(dependencyResolver);
}

Noticed that I registered the management client classes as per-request instance. This option will create separate client instances per each request. As the underlying architecture creates separate HttpClient instances per each client creating, I'll not be using the same HttpClient throughout my application lifecycle. I'm also not sure whether the client classes are thread safe. That's why I went for the per-request option.

My controller is even simpler.

public class HomeController : Controller
{
    private readonly IComputeManagementClient _computeClient;
    private readonly IStorageManagementClient _storageClient;
    private readonly IWebSiteManagementClient _webSiteClient;

    public HomeController(
        IComputeManagementClient computeClient, 
        IStorageManagementClient storageClient, 
        IWebSiteManagementClient webSiteClient)
    {
        _computeClient = computeClient;
        _storageClient = storageClient;
        _webSiteClient = webSiteClient;
    }

    public async Task<ActionResult> Index()
    {
        Task<StorageServiceListResponse> storageServiceResponseTask = 
            _storageClient.StorageAccounts.ListAsync();
        Task<HostedServiceListResponse> hostedServiceResponseTask = 
            _computeClient.HostedServices.ListAsync();
        Task<WebSpacesListResponse> webSpaceResponseTask = 
            _webSiteClient.WebSpaces.ListAsync();

        await Task.WhenAll(storageServiceResponseTask, 
            hostedServiceResponseTask, webSpaceResponseTask);

        return View(new HomeViewModel 
        { 
            StorageServices = storageServiceResponseTask.Result,
            HostedServices = hostedServiceResponseTask.Result,
            WebSpaces = webSpaceResponseTask.Result
        });
    }
}

public class HomeViewModel
{
    public IEnumerable<StorageServiceListResponse.StorageService> StorageServices { get; set; }
    public IEnumerable<HostedServiceListResponse.HostedService> HostedServices { get; set; }
    public IEnumerable<WebSpacesListResponse.WebSpace> WebSpaces { get; set; }
}

Notice that I used Task.WhenAll to fire all the asynchronous work in parallel which is best of both worlds. If you also look at the client classes, you will see that operations are divided into logical groups. For example, here is the IComputeManagementClient interface:

public interface IComputeManagementClient
{
    Uri BaseUri { get; }
    SubscriptionCloudCredentials Credentials { get; }

    IDeploymentOperations Deployments { get; }
    IHostedServiceOperations HostedServices { get; }
    IOperatingSystemOperations OperatingSystems { get; }
    IServiceCertificateOperations ServiceCertificates { get; }
    IVirtualMachineDiskOperations VirtualMachineDisks { get; }
    IVirtualMachineImageOperations VirtualMachineImages { get; }
    IVirtualMachineOperations VirtualMachines { get; }

    Task<Models.ComputeOperationStatusResponse> GetOperationStatusAsync(
        string requestId, 
        CancellationToken cancellationToken);
}

HostedServices, OperatingSystems, VirtualMachines and so on. Each logical group of operations are separated as class properties.

Getting Inside the HTTP Pipeline

The current implementation of the libraries also allow us to get into the HTTP pipeline really easily. However, the way we do it today is a little ugly but it depends on the mentioned HttpClient usage underneath. Nevertheless, this extensibility is really promising. Every management client class inherits the ServiceClient<T> class and that class has a method called WithHandler. Every available client provides a method with the same name by calling the underlying WithHandler method from ServiceClient<T>. By calling this method with a DelegatingHandler instance, we can get into the HTTP pipeline and have a chance to manipulate the request processing. For example, we can inject an handler which has a request retry logic if certain error cases are met. Windows Azure Management Library provides an abstract class for a retry handler which has the basic functionality: LinearRetryHandler. As this is an abstract class, we can inherit this class to create our own retry handler. If we need, we can always override the ShouldRetry method but for our case, we don't need it.

public class CustomRetryHandler : LinearRetryHandler
{
}

Now, we can use our retry handler to create the management clients:

RetryHandler retryHandler = new CustomRetryHandler();
IComputeManagementClient client = 
    CloudContext.Clients.CreateComputeManagementClient(creds).WithHandler(retryHandler)

Now, if a request to a Windows Azure Management API fails with some of the certain status codes, the handler will retry the request.

There are More…

There are more features that are worth mentioning but it will make a very lengthy blog post :) Especially the unit testing and tracing are the topics that I'm looking forward to blogging about.