This post will focus on some advanced programming topics concerned with IaaS (Infrastructure as a Service) which provided as windows azure virtual machine (with its related resources like virtual disk and virtual network), you know that windows azure started as PaaS cloud platform but regarding to some business cases which need to have full control over their virtual machine, so windows azure directed toward providing IaaS.
Sometimes you will need to manage your cloud IaaS through code may be for these reasons:
Working on hyper-cloud system by providing bursting connector to windows azure virtual machines
Providing multi-tenant system which consume windows azure virtual machine
Automated process on your on-premises or cloud service which need to utilize some virtual resources
We are going to implement the following basic operation using C# code:
List images
Create virtual machine
List virtual machines
Restart virtual machine
Delete virtual machine
Before going to implement the above operations we need to prepare client side and windows azure subscription to communicate correctly by providing management certificate (x.509 v3 certificates) which permit client access to resources in your Windows Azure subscription, whilst requests made using the Windows Azure Service Management REST API require authentication against a certificate that you provide to Windows Azure
More info about setting management certificate located here. And to install .cer on other client machine you will need the .pfx file, or if not exist by exporting .cer as .pfx
Note: You will need to install .net 4.5 on your machine to try the code
So let start
This post built on the post sent by Michael Washam "Advanced Windows Azure IaaS – Demo Code", so I'm here to declare some points and to add new operation which is not exist in Michael's demo
The basic C# class object used here as client to azure REST API for IaaS service is HttpClient (Provides a base class for sending HTTP requests and receiving HTTP responses from a resource identified by a URI) this object must be initialized with the required data like certificate, headers and content if required.
Also I'd like to refer here that the code is based on using Asynchronous programming with calls to azure which enhance the performance and gives us the ability to work with complex calls which depends on more than one sub-call to achieve some operation
The following code explain how to get certificate and initializing HttpClient object with required data like headers and content
HttpClient GetHttpClient()
{
X509Store certificateStore = null;
X509Certificate2 certificate = null;
try
{
certificateStore = new
X509Store(StoreName.My, StoreLocation.CurrentUser);
certificateStore.Open(OpenFlags.ReadOnly);
string thumbprint = ConfigurationManager.AppSettings["CertThumbprint"];
var certificates = certificateStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
if (certificates.Count > 0)
{
certificate = certificates[0];
}
}
finally
{
if (certificateStore != null) certificateStore.Close();
}
WebRequestHandler handler = new
WebRequestHandler();
if (certificate!= null)
{
handler.ClientCertificates.Add(certificate);
HttpClient httpClient = new
HttpClient(handler);
//And to set required headers lik x-ms-version
httpClient.DefaultRequestHeaders.Add("x-ms-version", "2012-03-01");
httpClient.DefaultRequestHeaders.Accept.Add(new
MediaTypeWithQualityHeaderValue("application/xml"));
return httpClient;
}
return
null;
}
Let us keep the object httpClient as reference object used to call windows azure REST API IaaS service. For each request operation we need to define:
Request URI
HTTP Method
Headers
Content body
(1) List images
The List OS Images operation retrieves a list of the OS images from the image repository
Request URI
https://management.core.windows.net/<subscription-id>/services/images]
Replace <subscription-id> with your windows Id
HTTP Method
GET (HTTP 1.1)
Headers
x-ms-version: 2012-03-01
Body
None.
C# Code
List<String> imageList = new
List<String>();
//replace _subscriptionid with your WA subscription
String uri = String.Format("https://management.core.windows.net/{0}/services/images", _subscriptionid);
HttpClient http = GetHttpClient();
Stream responseStream = await http.GetStreamAsync(uri);
if (responseStream != null)
{
XDocument xml = XDocument.Load(responseStream);
var images = xml.Root.Descendants(ns + "OSImage").Where(i => i.Element(ns + "OS").Value == "Windows");
foreach (var image in images)
{
string img = image.Element(ns + "Name").Value;
imageList.Add(img);
}
}
More information about the REST call (Request/Response) located here on this link http://msdn.microsoft.com/en-us/library/windowsazure/jj157191.aspx
(2) Create Virtual Machine
Creating virtual machine required service and deployment to be created first, so creating VM should be done through three steps incase hosted service and deployment is not created yet
Create hosted service, a container for service deployments in Windows Azure. A subscription may have zero or more hosted services
Create deployment, a service that is running on Windows Azure. A deployment may be running in either the staging or production deployment environment. It may be managed either by referencing its deployment ID, or by referencing the deployment environment in which it's running.
Create virtual machine, the previous two steps info required here in this step
I suggest here to use the same name for service, deployment and service to make it easy to manage virtual machines
Note: A name for the hosted service that is unique within Windows Azure. This name is the DNS prefix name and can be used to access the hosted service. For example: http://ServiceName.cloudapp.net//
2.1 Create service
Request URI
https://management.core.windows.net/<subscription-id>/services/hostedservices
HTTP Method
POST (HTTP 1.1)
Header
x-ms-version: 2012-03-01
Content-Type: application/xml
Body
More details about request body (and other information) are located here http://msdn.microsoft.com/en-us/library/windowsazure/gg441304.aspx
C# code
The following method show how to create hosted service
async
public
Task<String> NewAzureCloudService(String ServiceName, String Location, String AffinityGroup, String subscriptionid)
{
String requestID = String.Empty;
String uri = String.Format("https://management.core.windows.net/{0}/services/hostedservices", subscriptionid);
HttpClient http = GetHttpClient();
System.Text.ASCIIEncoding ae = new System.Text.ASCIIEncoding();
byte[] svcNameBytes = ae.GetBytes(ServiceName);
String locationEl = String.Empty;
String locationVal = String.Empty;
if (String.IsNullOrEmpty(Location) == false)
{
locationEl = "Location";
locationVal = Location;
}
else
{
locationEl = "AffinityGroup";
locationVal = AffinityGroup;
}
XElement srcTree = new XElement("CreateHostedService",
new XAttribute(XNamespace.Xmlns + "i", ns1),
new XElement("ServiceName", ServiceName),
new XElement("Label", Convert.ToBase64String(svcNameBytes)),
new XElement(locationEl, locationVal)
);
ApplyNamespace(srcTree, ns);
XDocument CSXML = new XDocument(srcTree);
HttpContent content = new StringContent(CSXML.ToString());
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/xml");
HttpResponseMessage responseMsg = await http.PostAsync(uri, content);
if (responseMsg != null)
{
requestID = responseMsg.Headers.GetValues("x-ms-request-id").FirstOrDefault();
}
return requestID;
}
2.2 Create Deployment
Request URI
https://management.core.windows.net/<subscription-id>/services/hostedservices/<service-name>/deploymentslots/<deployment-slot-name>
<deployment-slot-name> with staging or production, depending on where you wish to deploy your service package
<service-name> provided as input from the previous step
HTTP Method
POST (HTTP 1.1)
Header
x-ms-version: 2012-03-01
Content-Type: application/xml
Body
More details about request body (and other information) are located here http://msdn.microsoft.com/en-us/library/windowsazure/ee460813.aspx
C# code
The following method show how to create hosted service deployment
async
public
Task<String> NewAzureVMDeployment(String ServiceName, String VMName, String VNETName, XDocument VMXML, XDocument DNSXML)
{
String requestID = String.Empty;
String uri = String.Format("https://management.core.windows.net/{0}/services/hostedservices/{1}/deployments", _subscriptionid, ServiceName);
HttpClient http = GetHttpClient();
XElement srcTree = new XElement("Deployment",
new XAttribute(XNamespace.Xmlns + "i", ns1),
new XElement("Name", ServiceName),
new XElement("DeploymentSlot", "Production"),
new XElement("Label", ServiceName),
new XElement("RoleList", null)
);
if (String.IsNullOrEmpty(VNETName) == false)
{
srcTree.Add(new XElement("VirtualNetworkName", VNETName));
}
if(DNSXML != null)
{
srcTree.Add(new XElement("DNS", new XElement("DNSServers", DNSXML)));
}
XDocument deploymentXML = new XDocument(srcTree);
ApplyNamespace(srcTree, ns);
deploymentXML.Descendants(ns + "RoleList").FirstOrDefault().Add(VMXML.Root);
String fixedXML = deploymentXML.ToString().Replace(" xmlns=\"\"", "");
HttpContent content = new StringContent(fixedXML);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/xml");
HttpResponseMessage responseMsg = await http.PostAsync(uri, content);
if (responseMsg != null)
{
requestID = responseMsg.Headers.GetValues("x-ms-request-id").FirstOrDefault();
}
return requestID;
}
2.3 Create Virtual Machine
Request URI
https://management.core.windows.net/<subscription-id>/services/hostedservices/<cloudservice-name>/deployments/<deployment-name>/roles
<cloudservice-name> and <deployment-name> are provided as input from the previous steps
Http Method
POST (HTTP 1.1)
Header
x-ms-version: 2012-03-01
Content-Type: application/xml
Body
More details about request body (and other information) located here http://msdn.microsoft.com/en-us/library/windowsazure/jj157186.aspx
C# code
async
public
Task<String> NewAzureVM(String ServiceName, String VMName, XDocument VMXML)
{
String requestID = String.Empty;
String deployment = await GetAzureDeploymentName(ServiceName);
String uri = String.Format("https://management.core.windows.net/{0}/services/hostedservices/{1}/deployments/{2}/roles", _subscriptionid, ServiceName, deployment);
HttpClient http = GetHttpClient();
HttpContent content = new StringContent(VMXML.ToString());
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/xml");
HttpResponseMessage responseMsg = await http.PostAsync(uri, content);
if (responseMsg != null)
{
requestID = responseMsg.Headers.GetValues("x-ms-request-id").FirstOrDefault();
}
return requestID;
}
(3) List Virtual Machines
To list virtual machine hosted on windows azure subscription we have to loop over all hosted services to get its hosted virtual machines
To do that we need to execute the following operations:
listing hosted services
listing hosted service Virtual machine
3.1 Listing Hosted Services
Request URI
https://management.core.windows.net/<subscription-id>/services/hostedservices
HTTP Method
GET (HTTP 1.1)
Headers
x-ms-version: 2012-03-01
Body
None.
More info about this HTTP request located here on this link http://msdn.microsoft.com/en-us/library/windowsazure/ee460781.aspx
C# Code
async
private
Task<List<XDocument>> GetAzureServices(String subscriptionid)
{
String uri = String.Format("https://management.core.windows.net/{0}/services/hostedservices ", subscriptionid);
List<XDocument> services = new
List<XDocument>();
HttpClient http = GetHttpClient();
Stream responseStream = await http.GetStreamAsync(uri);
if (responseStream != null)
{
XDocument xml = XDocument.Load(responseStream);
var svcs = xml.Root.Descendants(ns + "HostedService");
foreach (XElement r in svcs)
{
XDocument vm = new XDocument(r);
services.Add(vm);
}
}
return services;
}
3.2 Listing Hosted Service Virtual Machines
Request URI
https://management.core.windows.net/<subscription-id>/services/hostedservices/<service-name>/deployments/<deployment-name>/roles/<role-name>
HTTP Method
GET (HTTP 1.1)
Headers
x-ms-version: 2012-03-01
Body
None.
More info about this HTTP request here http://msdn.microsoft.com/en-us/library/windowsazure/jj157193.aspx
C# Code
async
public
Task<XDocument> GetAzureVM(String ServiceName, String VMName, String subscriptionid)
{
String deployment = await GetAzureDeploymentName(ServiceName);
XDocument vmXML = new XDocument();
String uri = String.Format("https://management.core.windows.net/{0}/services/hostedservices/{1}/deployments/{2}/roles/{3}",
subscriptionid, ServiceName, deployment, VMName);
HttpClient http = GetHttpClient();
Stream responseStream = await http.GetStreamAsync(uri);
if (responseStream != null)
{
vmXML = XDocument.Load(responseStream);
}
return vmXML;
}
So the final method which can be used to list all virtual machines is:
async
public
Task<XDocument> GetAzureVMs()
{
List<XDocument> services = await GetAzureServices();
XDocument vms = new XDocument();
vms.Add(new XElement("VirtualMachines"));
ApplyNamespace(vms.Root, ns);
foreach (var svc in services)
{
string ServiceName = svc.Root.Element(ns + "ServiceName").Value;
String uri = String.Format("https://management.core.windows.net/{0}/services/hostedservices/{1}/deploymentslots/{2}", _subscriptionid, ServiceName, "Production");
try
{
HttpClient http = GetHttpClient();
Stream responseStream = await http.GetStreamAsync(uri);
if (responseStream != null)
{
XDocument xml = XDocument.Load(responseStream);
var roles = xml.Root.Descendants(ns + "RoleInstance");
foreach (XElement r in roles)
{
XElement svcnameel = new XElement("ServiceName", ServiceName);
ApplyNamespace(svcnameel, ns);
r.Add(svcnameel); // not part of the roleinstance
vms.Root.Add(r);
}
}
}
catch (HttpRequestException http)
{
// no vms with cloud service
}
}
return vms;
}
(4) Restart Virtual Machine
Request URI
https://management.core.windows.net/<subscription-id>/services/hostedservices/<service-name>/deployments/<deployment-name>/roles/<role-name>/Operations
HTTP Method
POST (HTTP 1.1)
Headers
x-ms-version: 2012-03-01
Content-Type: application/xml
Body
<RestartRoleOperation xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<OperationType>RestartRoleOperation</OperationType>
</RestartRoleOperation>
More details about this http request here
http://msdn.microsoft.com/en-us/library/windowsazure/jj157197.aspx
C# Code
async
public
Task<String> RebootVM(String ServiceName, String RoleName)
{
String requestID = String.Empty;
String deployment = await GetAzureDeploymentName(ServiceName);
String uri = String.Format("https://management.core.windows.net/{0}/services/hostedservices/{1}/deployments/{2}/roleInstances/{3}/Operations",
_subscriptionid, ServiceName, deployment, RoleName);
HttpClient http = GetHttpClient();
XElement srcTree = new XElement("RestartRoleOperation",
new XAttribute(XNamespace.Xmlns + "i", ns1),
new XElement("OperationType", "RestartRoleOperation")
);
ApplyNamespace(srcTree, ns);
XDocument CSXML = new XDocument(srcTree);
HttpContent content = new StringContent(CSXML.ToString());
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/xml");
HttpResponseMessage responseMsg = await http.PostAsync(uri, content);
if (responseMsg != null)
{
requestID = responseMsg.Headers.GetValues("x-ms-request-id").FirstOrDefault();
}
return requestID;
}
(5) Delete Virtual Machine
You can delete your hosted virtual machine by deleting its deployment, but I prefer to delete its hosted service also, so you can easily manage your virtual machines from code
5.1 Delete Deployment
Request URI
https://management.core.windows.net/< subscription-id >/services/hostedservices/< service-name >/deployments/<Deployment-Name>
HTTP Method
DELETE (HTTP 1.1)
Headers
x-ms-version: 2012-03-01
Body
None.
C# code
async
public
Task<HttpResponseMessage> DeleteDeployment( string deploymentName)
{
string xml = string.Empty;
String uri = String.Format("https://management.core.windows.net/{0}/services/hostedservices/{1}/deployments/{2}", _subscriptionid, deploymentName, deploymentName);
HttpClient http = GetHttpClient();
HttpResponseMessage responseMessage = await http.DeleteAsync(uri);
return responseMessage;
}
5.2 Delete Hosted Service
Request URI
https://management.core.windows.net/<subscription-id>/services/hostedservices/<service-name>
HTTP Method
DELETE (HTTP 1.1)
Headers
x-ms-version: 2012-03-01
Body
None.
C# code
async
public
Task<HttpResponseMessage> DeleteService(string serviceName)
{
string xml = string.Empty;
String uri = String.Format("https://management.core.windows.net/{0}/services/hostedservices/{1}", _subscriptionid, serviceName);
Log.Info("Windows Azure URI (http DELETE verb): " + uri, typeof(VMManager));
HttpClient http = GetHttpClient();
HttpResponseMessage responseMessage = await http.DeleteAsync(uri);
return responseMessage;
}
And the following is the method which can used to delete both of deployment and service
async
public
Task<string> DeleteVM(string vmName)
{
string responseString = string.Empty;
// as a convention here in this post, a unified name used for service, deployment and VM instance to make it easy to manage VMs
HttpClient http = GetHttpClient();
HttpResponseMessage responseMessage = await DeleteDeployment(vmName);
if (responseMessage != null)
{
string requestID = responseMessage.Headers.GetValues("x-ms-request-id").FirstOrDefault();
OperationResult result = await PollGetOperationStatus(requestID, 5, 120);
if (result.Status == OperationStatus.Succeeded)
{
responseString = result.Message;
HttpResponseMessage sResponseMessage = await DeleteService(vmName);
if (sResponseMessage != null)
{
OperationResult sResult = await PollGetOperationStatus(requestID, 5, 120);
responseString += sResult.Message;
}
}
else
{
responseString = result.Message;
}
}
return responseString;
}
Note: This article is subject to be updated
Hisham
References
Advanced Windows Azure IaaS – Demo Code
Windows Azure Service Management REST API Reference
Introduction to the Azure Platform
Representational state transfer
Asynchronous Programming with Async and Await (C# and Visual Basic)
HttpClient Class