Creating a Windows Azure hosted service

In December 2009, I did a post on the Service Management API in Windows Azure. Over time I became unhappy with part of the post – specifically the use of XML serialization – and was looking for an opportunity to redo it. I showed an alternate usage pattern in a May 2011 post on the SQL Azure Management REST API.

I was finally able to revisit the topic in my book, Microsoft Windows Azure Development Cookbook, in which I provide a pretty comprehensive overview of how to use the Windows Azure Service Management REST API. There is an entire chapter devoted to the topic – and the publisher, Packt Publishing, have made the chapter available as a free download. It covers the use of the Service Management API for:

  • creating a Windows Azure hosted service
  • deploying an application into a hosted service
  • upgrading an application deployed to a hosted service
  • retrieving the properties of a hosted service
  • autoscaling with the Windows Azure Service Management REST API
  • using the Windows Azure Platform PowerShell cmdlets

The rest of this post is the section of the book on creating a Windows Azure hosted service. (Apologies for the code formatting here – some day I will learn how to get that right on the blog.)

Creating a Windows Azure hosted service

A hosted service is the administrative and security boundary for an application deployed to Windows Azure. The hosted service specifies the service name, a label, and either the Windows Azure datacenter location or the affinity group into which the application is to be deployed. These cannot be changed once the hosted service is created. The service name is the subdomain under cloudapp.net used by the application, and the label is a human-readable name used to identify the hosted service on the Windows Azure Portal.

The Windows Azure Service Management REST API exposes a create hosted service operation. The REST endpoint for the create hosted service operation specifies the subscription ID under which the hosted service is to be created. The request requires a payload comprising an XML document containing the properties needed to define the hosted service, as well as various optional properties. The service name provided must be unique across all hosted services in Windows Azure, so there is a possibility that a valid create hosted service operation will fail with a 409 Conflict error if the provided service name is already in use. As the create hosted service operation is asynchronous, the response contains a request ID that can be passed into a get operation status operation to check the current status of the operation.

In this recipe, we will learn how to use the Service Management API to create a Windows Azure hosted service.

Getting ready

The recipes in this chapter use the ServiceManagementOperation utility class to invoke operations against the Windows Azure Service Management REST API. We implement this class as follows:

1. Add a class named ServiceManagementOperation to the project.

2. Add the following assembly reference to the project:

System.Xml.Linq.dll

3. Add the following using statements to the top of the class file:

using System.Security.Cryptography.X509Certificates; using System.Net; using System.Xml.Linq; using System.IO;

4. Add the following private members to the class:

String thumbprint; String versionId = "2011-02-25";

5. Add the following constructor to the class:

public ServiceManagementOperation(String thumbprint) { this.thumbprint = thumbprint; }

6. Add the following method, retrieving an X.509 certificate from the certificate store, to the class:

private X509Certificate2 GetX509Certificate2( String thumbprint) { X509Certificate2 x509Certificate2 = null; X509Store store = new X509Store("My", StoreLocation.LocalMachine); try { store.Open(OpenFlags.ReadOnly); X509Certificate2Collection x509Certificate2Collection = store.Certificates.Find( X509FindType.FindByThumbprint, thumbprint, false); x509Certificate2 = x509Certificate2Collection[0]; } finally { store.Close(); } return x509Certificate2; }

7. Add the following method, creating an HttpWebRequest, to the class:

private HttpWebRequest CreateHttpWebRequest( Uri uri, String httpWebRequestMethod) { X509Certificate2 x509Certificate2 = GetX509Certificate2(thumbprint); HttpWebRequest httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(uri); httpWebRequest.Method = httpWebRequestMethod; httpWebRequest.Headers.Add("x-ms-version", versionId); httpWebRequest.ClientCertificates.Add(x509Certificate2); httpWebRequest.ContentType = "application/xml"; return httpWebRequest; }

8. Add the following method, invoking a GET operation on the Service Management API, to the class:

public XDocument Invoke(String uri) { XDocument responsePayload; Uri operationUri = new Uri(uri); HttpWebRequest httpWebRequest = CreateHttpWebRequest(operationUri, "GET"); using (HttpWebResponse response = (HttpWebResponse)httpWebRequest.GetResponse()) { Stream responseStream = response.GetResponseStream(); responsePayload = XDocument.Load(responseStream); } return responsePayload; }

9. Add the following method, invoking a POST operation on the Service Management API, to the class:

public String Invoke(String uri, XDocument payload) { Uri operationUri = new Uri(uri); HttpWebRequest httpWebRequest = CreateHttpWebRequest(operationUri, "POST"); using (Stream requestStream = httpWebRequest.GetRequestStream()) { using (StreamWriter streamWriter = new StreamWriter(requestStream, System.Text.UTF8Encoding.UTF8)) { payload.Save(streamWriter, SaveOptions.DisableFormatting); } } String requestId; using (HttpWebResponse response = (HttpWebResponse)httpWebRequest.GetResponse()) { requestId = response.Headers["x-ms-request-id"]; } return requestId; }

How it works…

In steps 1 through 3, we set up the class. In step 4, we add a version ID for service management operations. Note that Microsoft periodically releases new operations for which it provides a new version ID, which is usually applicable for operations added earlier. In step 4, we also add a private member for the X.509 certificate thumbprint that we initialize in the constructor we add in step 5.

In step 6, we open the Personal (My) certificate store on the local machine level and retrieve an X.509 certificate identified by thumbprint. If necessary, we can specify the current user level, instead of the local machine level, by using StoreLocation.CurrentUser instead of StoreLocation.LocalMachine.

In step 7, we create an HttpWebRequest with the desired HTTP method type, and add the X.509 certificate to it. We also add various headers including the required x-ms-version.

In step 8, we invoke a GET request against the Service Management API and load the response into an XML document which we then return. In step 9, we write an XML document, containing the payload, into the request stream for an HttpWebRequest and then invoke a POST request against the Service Management API. We extract the request ID from the response and return it.

How to do it…

We are now going to construct the payload required for the create hosted service operation, and then use it when we invoke the operation against the Windows Azure Service Management REST API. We do this as follows:

1. Add a new class named CreateHostedServiceExample to the WPF project.

2. If necessary, add the following assembly reference to the project:

System.Xml.Linq.dll

3. Add the following using statement to the top of the class file:

using System.Xml.Linq;

4. Add the following private members to the class:

XNamespace wa = "http://schemas.microsoft.com/windowsazure"; String createHostedServiceFormat = "https://management.core.windows.net/{0}/services/hostedservices";

5. Add the following method, creating a base-64 encoded string, to the class:

private String ConvertToBase64String(String value) { Byte[] bytes = System.Text.Encoding.UTF8.GetBytes(value); String base64String = Convert.ToBase64String(bytes); return base64String; }

6. Add the following method, creating the payload, to the class:

private XDocument CreatePayload( String serviceName, String label, String description, String location, String affinityGroup) { String base64LabelName = ConvertToBase64String(label); XElement xServiceName = new XElement(wa + "ServiceName", serviceName); XElement xLabel = new XElement(wa + "Label", base64LabelName); XElement xDescription = new XElement(wa + "Description", description); XElement xLocation = new XElement(wa + "Location", location); XElement xAffinityGroup = new XElement(wa + "AffinityGroup", affinityGroup); XElement createHostedService = new XElement(wa +"CreateHostedService"); createHostedService.Add(xServiceName); createHostedService.Add(xLabel); createHostedService.Add(xDescription); createHostedService.Add(xLocation); //createHostedService.Add(xAffinityGroup); XDocument payload = new XDocument(); payload.Add(createHostedService); payload.Declaration = new XDeclaration("1.0", "UTF-8", "no"); return payload; }

7. Add the following method, invoking the create hosted service operation, to the class:

private String CreateHostedService(String subscriptionId, String thumbprint, String serviceName, String label, String description, String location, String affinityGroup) { String uri = String.Format(createHostedServiceFormat, subscriptionId); XDocument payload = CreatePayload(serviceName, label, description, location, affinityGroup); ServiceManagementOperation operation = new ServiceManagementOperation(thumbprint); String requestId = operation.Invoke(uri, payload); return requestId; }

8. Add the following method, invoking the methods added earlier, to the class:

public static void UseCreateHostedServiceExample() { String subscriptionId = "{SUBSCRIPTION_ID}"; String thumbprint = "{THUMBPRINT}"; String serviceName = "{SERVICE_NAME}"; String label = "{LABEL}"; String description = "Newly created service"; String location = "{LOCATION}"; String affinityGroup = "{AFFINITY_GROUP}"; CreateHostedServiceExample example = new CreateHostedServiceExample(); String requestId = example.CreateHostedService( subscriptionId, thumbprint, serviceName, label, description, location, affinityGroup); }

How it works…

In steps 1 through 3, we set up the class. In step 4, we add private members to define the XML namespace used in creating the payload and the String format used in generating the endpoint for the create hosted service operation. In step 5, we add a helper method to create a base-64 encoded copy of a String.

We create the payload in step 6 by creating an XElement instance for each of the required and optional properties, as well as the root element. We add each of these elements to the root element and then add this to an XML document. Note that we do not add an AffinityGroup element because we provide a Location element and only one of them should be provided.

In step 7, we use the ServiceManagementOperation utility class, described in the Getting ready section, to invoke the create hosted service operation on the Service Management API. The Invoke() method creates an HttpWebRequest, adds the required X.509 certificate and the payload, and then sends the request to the create hosted services endpoint. It then parses the response to retrieve the request ID which can be used to check the status of the asynchronous create hosted services operation.

In step 8, we add a method that invokes the methods added earlier. We need to provide the subscription ID for the Windows Azure subscription, a globally unique service name for the hosted service, and a label used to identify the hosted service in the Windows Azure Portal. The location must be one of the official location names for a Windows Azure datacenter, such as North Central US. Alternatively, we can provide the GUID identifier of an existing affinity group and swap the commenting out in the code adding the Location and AffinityGroup elements in step 6. We see how to retrieve the list of locations and affinity groups in the Locations and affinity groups section of this recipe.

There’s more

Each Windows Azure subscription can create 6 hosted services. This is a soft limit that can be raised by requesting a quota increase from Windows Azure Support at:

http://www.microsoft.com/windowsazure/support/

There are also soft limits on the number of cores per subscription (20) and the number of Windows Azure storage accounts per subscription (5). These limits can also be increased by request to Windows Azure Support.

Locations and affinity groups

The list of locations and affinity groups can be retrieved using the list locations and list affinity groups operations respectively in the Service Management API. We see how to do this in the Using the Windows Azure Platform PowerShell Cmdlets recipe in this chapter.

As of this writing, the locations are:

  • Anywhere US
  • South Central US
  • North Central US
  • Anywhere Europe
  • North Europe
  • West Europe
  • Anywhere Asia
  • Southeast Asia
  • East Asia

The affinity groups are specific to a subscription.

About Neil Mackenzie

Cloud Solutions Architect. Microsoft
This entry was posted in Service Management, Windows Azure and tagged , . Bookmark the permalink.

4 Responses to Creating a Windows Azure hosted service

  1. Pingback: Dew Drop – August 30, 2011 | Alvin Ashcraft's Morning Dew

  2. Dear Neil,
    I have attempted to implement a small test project to create hosted Services on Azure, I followed all the steps that u mentioned in your Cookbook. But unfortunately I received this error.
    “The remote server returned an error: (400) Bad Request”
    I have also tried other methods (not using xDocument) and still get the same error.

    I made my certificate according to this tutorial :
    http://msdn.microsoft.com/en-us/library/windowsazure/gg651127.aspx
    and I can get the list of hosted service but I can not create any.

    I would appreciate of you help me and give me some hint.

    Kind Regards

    Sherry

  3. Sherry –

    I would make sure that the payload is correct since that is the most likely source of trouble. Specifically, ensure it is in the correct sequence, that Base64 things are Base64 encoded and that the location or affinity group is correct.

    • Thanks for your reply.
      I am also quite sure the playload which u used is correct.
      I even saved it on a xml file and it is the way which should be:

      ShaghayegRESTAPI
      djEuMA==
      Newly created service
      East Asia

      but still I get the error message : “The remote server returned an error: (400) Bad Request” with this status: ProtocolError.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s