Introduction
This is about 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 we may need to manage windows azure virtual machines (IaaS) through
code for one or more of 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
<o:p>
<o:p>
<o:p>
<o:p>
We are going to implement the following basic operation
using C# code:<o:p>
- List images
- Create virtual machine
- List virtual machines
- Restart virtual machine
- Delete virtual machine
<o:p>
<o:p>
<o:p>
<o:p>
<o:p>
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<o:p>
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 <o:p>
Note: You will need to install .net 4.5 on your
machine to try the code
Using the code
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. <o:p>
Also I’d like to refer here that the code is based on using Asynchronous
programming with azure calls 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 <o:p>
The following code explain how to get certificate and initializing
HttpClient object with required data like headers and content<o:p>
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);
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:<o:p>
- Request URI
- HTTP Method
- Headers
- Content body
(1) List Images
<o:p>
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.
List<String> imageList = new List<String>();
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
<o:p>
(2) Create Virtual Machine<o:p>
Creating virtual machine requires 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 <o:p>
- 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
<o:p>
<o:p>
<o:p>
I suggest here to use the same
name for service, deployment and virtual machine to make it easy to manage virtual
machines <o:p>
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
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
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
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<o:p>
To list virtual machine hosted on windows azure subscription
we have to loop over all hosted services to get its hosted virtual machines<o:p>
To do that we need to execute the following operations:<o:p>
- listing hosted services
- listing virtual machines for each hosted service
<o:p>
<o:p>
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
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
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);
vms.Root.Add(r);
}
}
}
catch (HttpRequestException http)
{
}
}
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:
Headers:
x-ms-version: 2012-03-01
Content-Type: application/xml
Body:
<RestartRoleOperation xmlns="http://schemas.microsoft.com/windowsazure" 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
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.
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.
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;
}
<o:p>
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;
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;
}
References
Advanced
Windows Azure IaaS – Demo Code<o:p>
Windows
Azure Service Management REST API Reference<o:p>
Introduction to
the Azure Platform<o:p>
Representational
state transfer<o:p>
Asynchronous
Programming with Async and Await (C# and Visual Basic)<o:p>
<o:p>
HttpClient
Class