How to know where (Azure Datacenter) my app is running ?

Do you want to know (which Datacenter) where your Azure application is running ? Do you need all the information about your deployment from a Web Role ? Ok, is not and easy game unfortunatly. I had a question from my partner asking me how to deploy the same application in different Datacenter and change the behavior of the app at runtime checking where it is running. I first checked the ServiceRuntime API but I did’t find anything useful: we have two main classes, RoleEnvironment, Role and Roleinstances.

RoleEnviroment
image

We can understand if we are in emulation or not, if our role is available and the current RoleInstance. We can also get the DeploymentID , the unique identifier of the deployment in which the role instance is running. This is where I started. The only way to get all the information I needed was to using the Management API. So let’s look into the process to get the name of the Datacenter where my solution is running.

To use the Management API from our Web Role (or Worker Role) we need :

  1. Subscription ID
  2. Thumbprint of the certificate use to connect using the API
  3. The same certificate uploaded into the HostedServices (at runtime we have to use this)

I wrote a library that does all the job and this is the code needed to get the Datacenter Name:

 string subId = RoleEnvironment.GetConfigurationSettingValue("SubscriptionID").ToString();
string thumbPrint = RoleEnvironment.GetConfigurationSettingValue("Thumbprint").ToString();
 
WATools.WADeploymentInfo depInfo = new WATools.WADeploymentInfo(subId,thumbPrint,RoleEnvironment.DeploymentId,"");
WATools.WAToolsResponse resp = depInfo.GetWindowsAzureLocation();
if (!resp.HasError) lblDatacenter.Text = resp.ReturnValue;

How it works ?

Every Management API calls I did in the code use this two easy function:

private Uri CreateUri(string method, string newServiceName = null)

{

    string hostName = "";

    if (newServiceName == null) hostName = HostedServiceName; else hostName = newServiceName;

    Uri uri = null;

    String uriString;

    switch (method)

    {

        case "ChangeConfig":

            uriString = String.Format("https://management.core.windows.net/" + SubscriptionID +

                      "/services/hostedservices/{0}/deploymentslots/production/?comp=config", hostName);

            uri = new Uri(uriString);

            break;

        case "GetHostedServiceProperties":

            uriString = String.Format("https://management.core.windows.net/" + SubscriptionID +

                      "/services/hostedservices/{0}?embed-detail=true", hostName);

            uri = new Uri(uriString);

            break;

        case "GetDeploymentInfo":

            uriString = String.Format("https://management.core.windows.net/" + SubscriptionID +

                      "/services/hostedservices/{0}/deploymentslots/production", hostName);

            uri = new Uri(uriString);

            break;

        case "ListHostedServices":

            uriString = String.Format("https://management.core.windows.net/" + SubscriptionID + "/services/hostedservices");

            uri = new Uri(uriString);

            break;

        case "GetLocation":

            uriString = String.Format("https://management.core.windows.net/" + SubscriptionID + "/locations");

            uri = new Uri(uriString);

            break;

    }

    return uri;

}

private HttpWebRequest CreateHttpRequest(Uri uri, X509Certificate2 cert, string method, string version)

{

    HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);

    request.Method = method;

    request.ClientCertificates.Add(cert);

    request.ContentType = "application/xml";

    request.Headers.Add("x-ms-version", version);

    return request;

}

First we need to call the List Hosted Services to get the list of Hosted Service within the subsciption. This API required just the Subscription ID.

public List<string> GetHostedServices(X509Certificate2 cert)

{

    Uri uri = CreateUri("ListHostedServices");

    HttpWebRequest request = CreateHttpRequest(uri, cert, "GET", "2010-10-28");

    List<string> HostedServices = new List<string>();

    try

    {

        using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())

        {

            Stream dataStream = response.GetResponseStream();

            XElement hostedServiceElement = XElement.Load(dataStream);

            IEnumerable<XElement> ServicesName = hostedServiceElement.Descendants("{https://schemas.microsoft.com/windowsazure}ServiceName");

            foreach (XElement el in ServicesName)

                HostedServices.Add(el.Value);

            return HostedServices;

        }

    }

    catch (Exception ex)

    {

        return null;

    }

    return null;

}

HostedServices

We need now to enumarate all the Hosted Services and for each one we have to call Get Deployment API.

public string FindHostedServiceById(X509Certificate2 cert, List<string> hostedServices, string DeploymentID)

{

    foreach (string name in hostedServices)

    {

        Uri uri = CreateUri("GetDeploymentInfo", name);

        HttpWebRequest request = CreateHttpRequest(uri, cert, "GET", "2010-10-28");

        try

        {

            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())

            {

                Stream dataStream = response.GetResponseStream();

                XElement hostedService = XElement.Load(dataStream);

                XElement PrivateId = hostedService.Descendants("{https://schemas.microsoft.com/windowsazure}PrivateID").First();

                if (PrivateId.Value == DeploymentID) return name;

                dataStream.Close();

            }

        }

        catch (Exception ex)

        {

            return null;

        }

    }

    return null;

DeploymentInfo

For each hosted service we have a bunch of useful information but the most important one is the PrivateID. This ID is exactly the same we have calling RoleEnvironment.DeploymentId. Doing a match we know which HostedServiceName has our PrivateID so we know the name of the HostedService where our app is running on. The final step is to call the Get Hosted Service Properties API:

public WAToolsResponse GetWindowsAzureLocation()

{

    X509Certificate2 cert = GetCertificate();

    if (cert == null)

    {

        return new WAToolsResponse(true,"",new Exception((string.Format("Unable to load {0} certificate",Thumbprint))),"","OpenCertificate");

       
    }

    // Request Hosted Services List to find the righ HostedServices throuhg the PrivateID

    List<string> hostedServices = GetHostedServices(cert);

    if (hostedServices==null)

        return new WAToolsResponse(true, "", new Exception((string.Format("Unable to enumerate hostedservices", Thumbprint))), "", "GetHostedServices");

   
    // Try to find the HostedServiceName from the Deployment ID

    string HostedName = FindHostedServiceById(cert, hostedServices, HostedServicePrivateId);

    if (HostedName == null)

        return new WAToolsResponse(true, "", new Exception((string.Format("Unable to find the PrivateID:{0}", HostedServicePrivateId))), "", "GetHostedServices"); ;

    Uri uri = CreateUri("GetHostedServiceProperties", HostedName);

    HttpWebRequest request = CreateHttpRequest(uri, cert, "GET", "2011-06-01");

   
    try

    {           
        using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())

            {

                try

                {

                    Stream dataStream = response.GetResponseStream();

                    XElement hostedService = XElement.Load(dataStream);

                    XElement Location = hostedService.Descendants("{https://schemas.microsoft.com/windowsazure}Location").First();

                    return new WAToolsResponse(false,Location.Value,null,uri.ToString(),"GetResponse");

                }

                catch (Exception ex)

                {

                    return new WAToolsResponse(true,"",ex,uri.ToString(),"GetResponseStream");

                }

            }

    }

    catch (Exception ex)

    {

        return new WAToolsResponse(true,"",ex,uri.ToString(),"HttpWebRequest");

      
    }

   
}

DeploymentInfoAll

We have a lot of useful information here and finally the Location property of our application.

Here the Visual Studio 2010 project of WATools.

Fabio