A few days ago, the new Azure SDK 2.2 was announced by Scott Guthrie. Jeff Wilcox is currently working on the Azure Team and he is working on the code for the new managed management API,
which is still in preview. Let me show you how easy it is to use this API. Have fun!
Downloading and compiling the code
First of all I downloaded the current Azure-SDK source from GitHub. After the download you open the solution from within the libraries folder. Visual Studio will not build the solution, until you restore the NuGet packages used in the solution.
Installing the new management API using NuGet
If you don’t want to use the latest sources, you can save some time and install the management API using NuGet:
Install-Package Microsoft.WindowsAzure.Management.Libraries -IncludePrerelease
If you are not familiar with NuGet, you can learn how to use it here: NuGet Overview
Creating a new Azure Management Certificate
To be able to use the management API, you need to create a management certificate. The certificate is used to establish a secure connection to your Azure subscription and to identify you. Here is how the management certificate is created using makecert (It is part of the Windows SDK. Download it here):
makecert -r -pe -a sha1 -n "CN=YOURCN" -ss my -len 2048 -sp "Microsoft Enhanced RSA and AES Crypt
ographic Provider" -sy 24 ManagementTestCert.cer
After you have created the certificate, you need to upload the .CER file to Azure. Login to the portal and click on Settings => Certificates. You will find the upload button in the bottom center of the screen:
Export the certificate containing the private key using certmgr on your pc. Open a command prompt with administrative rights (Run as administrator) and execute certmgr:
Select the certificate you created recently, and click on “Export” to start the wizard. Click next, choose “Yes, export the private key”, click next, click next, tick the password box and enter a SOLID password here. Another tip: Save that exported file in a save place, even if you have given this thing a password! This is the key to all of your Azure services! Click next and save the file. Give it the .pfx extension. Click next and again next and finish the wizard.
Create a sample application
Since the new management API is a PCL, you can use it with Silverlight, .NET in general, Windows Phone and Windows 8. So it is you choice which kind of project to use. For quick testing I prefer WPF.
Add a folder to your project and add the certificate
Add a new folder to your project, and name it something like “Certificates”. Right click the folder and choose to add an existing item. Browse to the folder where you have saved the .pfx file and add it to the certificates folder. Once you have done that, right click the certificate file, choose “properties” and set the build action to “Content” and copy to output directory to “Copy always”.
Loading the certificate from your project folder
To be able to create a new CertificateCloudCredentials
instance, we need to pass a instance of the
X509Certificate2
class. Add the following using statement to your code:
using System.Security.Cryptography.X509Certificates;
We use constructor overload 8 of 13 of the X509Certificate2
class to pass a path and a password to the constructor. The path is a relative path to the certificate, residing in the certificates folder you created previously and the password is the one you used to secure the certificate and private key (the .pfx file) when you exported the certificate using certmgr.
X509Certificate2 cert = new X509Certificate2(
@"Certificates\YOUR CERTIFICATE FILE NAME","YOUR PASSWORD");
CertificateCloudCredentials creds = new CertificateCloudCredentials(
"YOUR AZURE SUBSCRIPTION ID", cert);
Creating the management client
Another piece of information, besides the certificate is needed, to create an instance of the
ManagmentClient
class. You need the id of you Windows Azure subscription. You can get this information from the portal. Just click on settings, and copy the subscription id you want to use for testing.
A word of warning: Please consider, that the management API is very powerful and that you could accidentally delete existing services or generate additional costs creating new services!
Ok. With that said and all the other things in place, you can create an instance of the management client. Since the client implements the
IDisposable
interface, it can be instantiated from within a using-statement:
using (ManagementClient client = CloudContext.Clients.CreateManagementClient(creds))
{
foreach (var manCert in await client.ManagementCertificates.ListAsync())
{
await AddOutput(string.Format("Cert Thumbprint:{0}," +
" Cert Created:{1}", manCert.Thumbprint, manCert.Created));
}
await AddOutput("Performed operations since 10/01/2013.....");
await AddOutput("////////////////////////");
var utcStart = new DateTime(2013, 10, 01, 0, 0, 0).ToUniversalTime();
var operations = await client.Subscriptions.ListOperationsAsync(
new SubscriptionListOperationsParameters() { StartTime = utcStart, EndTime = DateTime.UtcNow });
foreach (var op in operations.SubscriptionOperations)
{
await AddOutput(string.Format("Operation name:{0}, " +
"Operation id:{1}", op.OperationName, op.OperationId));
}
await AddOutput("Affinity groups and their locations.....");
await AddOutput("////////////////////////");
foreach (var ag in await client.AffinityGroups.ListAsync())
{
await AddOutput(string.Format("Affiniy group name:{0}, Location:{1}", ag.Name, ag.Location));
}
}
Now we are ready to explore the fresh Management API!
The logic behind the API
How to use the API, if there is no documentation (or very little) ? Well, in this case Jeff has done excellent work, creating a very simple to use logic, so that you can figure out on how to use the API very easily. Just open the Object Explorer in Visual Studio and expand all the management assemblies like I did:
If you take a closer look, for example at the Microsoft.WindowsAzure.Management.Compute assembly and the src\NetworkManagement assembly, you can see, that there is always a class that ends with “…Client” . These classes are instantiated using the static CloudContext.Clients.[THE CLIENT YOU NEED] …Client methods and passing the cloud credentials previously created. The pattern used here is called Factory pattern. I suggest to read through the source and learn how to develop a clean API.
The ManagemetClient class
This is the class with the base functions covered. You can perform the following operations:
- Create, Delete and List Affinity Groups
- Create, Delete and List management-certificates
- List Locations, and the available services from within those locations, read the locations display names and names
- List the operation’s that have been performed on a subscription within a specific timeframe
- Register resources with your subscriptions
In this short snippet I will show you how to list management certificates, the last operations performed within a month and the affinity groups in your subscription:
using (ManagementClient client = CloudContext.Clients.CreateManagementClient(creds))
{
foreach (var manCert in await client.ManagementCertificates.ListAsync())
{
await AddOutput(string.Format(
"Cert Thumbprint:{0}, Cert Created:{1}", manCert.Thumbprint, manCert.Created));
}
await AddOutput("Performed operations since 10/01/2013.....");
await AddOutput("////////////////////////");
var utcStart = new DateTime(2013, 10, 01, 0, 0, 0).ToUniversalTime();
var operations = await client.Subscriptions.ListOperationsAsync(
new SubscriptionListOperationsParameters() { StartTime = utcStart, EndTime = DateTime.UtcNow });
foreach (var op in operations.SubscriptionOperations)
{
await AddOutput(string.Format("Operation name:{0}, Operation id:{1}",
op.OperationName, op.OperationId));
}
await AddOutput("Affinity groups and their locations.....");
await AddOutput("////////////////////////");
foreach (var ag in await client.AffinityGroups.ListAsync())
{
await AddOutput(string.Format("Affiniy group name:{0}," +
" Location:{1}", ag.Name, ag.Location));
}
}
Before any operation can start, we need to load the management certificate, and create the
CertificateCloudCredentials
. The next step is to create an instance of the ManagementClient class.
In the next few lines you can see, that I am calling the ListAsync
method, every time I need to list entries, like certificates,
SubscriptionOperations
or AffinityGroups
.
To list the SubscriptionOperations
, it is necessary to pass an instance of the
SubscriptionListOperationsParameters
class to the ListOperationsAsync
method of the
Subscriptions.ListOperationsAsync
method.
To do that, we define the start and the end-date of the timespan to filter the amount of the operations returned. The time values passed need to be UTC values to avoid time-zone related quirks.
The StorageManagementClient class
This class allows you to manage everything related to storage within your Windows Azure subscription. You can perform the following operations:
- Create storage accounts
- List the available storage accounts and their system properties
- Delete storage accounts
- Check the availability of a storage account name, that you want to create
- Get the primary and secondary access keys for a specific storage account
- Re-generate the primary or secondary access key
- Update label and description of the storage account
- Enable or disable geo-replication for a storage account
Well, that’s a bunch of stuff that can be done. Let’s see now how that works.
using (StorageManagementClient client = CloudContext.Clients.CreateStorageManagementClient(creds))
{
await AddOutput("Available storage accounts.....");
await AddOutput("////////////////////////");
foreach (var storageAccount in await client.StorageAccounts.ListAsync())
{
await AddOutput(string.Format("Storage account name:{0}", storageAccount.ServiceName));
#region Storage Account properties
await AddOutput(string.Format(
"Properties for storage account {0}:", storageAccount.ServiceName));
await AddOutput(string.Format("Property name:{0}, Value:{1}",
"Affinity Group:", storageAccount.Properties.AffinityGroup));
await AddOutput(string.Format("Property name:{0}, Value:{1}",
"Description:", storageAccount.Properties.Description));
await AddOutput(string.Format("Endpoint properties for each endpoint " +
"(part of properties) for storage account {0}:", storageAccount.ServiceName));
foreach (var ep in storageAccount.Properties.Endpoints)
{
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"Absolute Path", ep.AbsolutePath));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"Absolute URI", ep.AbsoluteUri));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"DNS safehost", ep.DnsSafeHost));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"Escaped Uri fragment", ep.Fragment));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"Host", ep.Host));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"Hostname type", ep.HostNameType));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"Is absolute Uri", ep.IsAbsoluteUri));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"Is absolute Uri", ep.IsAbsoluteUri));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"Is default port", ep.IsDefaultPort));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"Is file", ep.IsFile));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"Is loopback", ep.IsLoopback));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"Is UNC", ep.IsUnc));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"Is wellformed", ep.IsWellFormedOriginalString()));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"Local Path", ep.LocalPath));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"Original string", ep.OriginalString));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"Path and query", ep.PathAndQuery));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"Port", ep.Port));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"Query", ep.Query));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"Scheme", ep.Scheme));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"User escaped", ep.UserEscaped));
await AddOutput(string.Format("Endpoint property name:{0}, Value:{1}",
"User Info", ep.UserInfo));
await AddOutput(string.Format("Segments, " +
"for Endpoint {0} (part of properties):", ep.AbsolutePath));
foreach (var segment in ep.Segments)
{
await AddOutput(string.Format("Segment {0}", segment));
}
}
await AddOutput(string.Format("Property name:{0}, Value:{1}",
"Geo primary region:", storageAccount.Properties.GeoPrimaryRegion));
await AddOutput(string.Format("Property name:{0}, Value:{1}",
"Geo replication enabled:", storageAccount.Properties.GeoReplicationEnabled));
await AddOutput(string.Format("Property name:{0}, Value:{1}",
"Geo secondary region:", storageAccount.Properties.GeoSecondaryRegion));
await AddOutput(string.Format("Property name:{0}, Value:{1}",
"Label", storageAccount.Properties.Label));
await AddOutput(string.Format("Property name:{0}, Value:{1}",
"Last failover time", storageAccount.Properties.LastGeoFailoverTime));
await AddOutput(string.Format("Property name:{0}, Value:{1}",
"Location", storageAccount.Properties.Location));
await AddOutput(string.Format("Property name:{0}, Value:{1}",
"Status", storageAccount.Properties.Status));
await AddOutput(string.Format("Property name:{0}, Value:{1}",
"Status of geo primary region", storageAccount.Properties.StatusOfGeoPrimaryRegion));
await AddOutput(string.Format("Property name:{0}, Value:{1}",
"Status of geo secondary region", storageAccount.Properties.StatusOfGeoSecondaryRegion));
await AddOutput(string.Format("Extended, for Endpoint {0} " +
"(part of properties):", storageAccount.ServiceName));
foreach (var extProp in storageAccount.ExtendedProperties)
{
await AddOutput(string.Format("Extended proptery key: {0}, value: {1}", extProp.Key, extProp.Value));
}
#endregion
}
}
Like in the first sample, we request a specific type of client from the
CloudContext
. The first thing you see here, is the “foreaching” through every single storage account available in your subscription.
Then the properties and the extended properties of the storage account are requested. Operations like adding a new storage account, deleting and updating are commented out, to allow you to execute the example safely without messing up your subscription.
The WebSiteManagmentClient class
Websites on Windows Azure are very popular because of the easy deployment, and the wide range of API’s that can be used to develop cool websites. The Visual Studio tooling is excellent as well (like the rest for Azure tooling in VS). And lot’s of other good reasons!
Now let’s see what this management client has to offer:
- Create websites
- Delete websites
- Create GIT repositories for deployment
- Delete GIT repositories
- Get website details
- Generate new random publishing passwords
- Get the configuration for a specific website
- Get the historical usage metrics for a website
- Get the current usage metrics for a website
- Get the publish profile for a specific website
- Restart a website
- Update the website
- Update the website configuration
- Manage server farms
I will demonstrate you how to list webspaces and websites from within those webspaces and how to list all of their properties.
Creating new websites, restarting and deleting depend on all those values. All repository operations as well.
foreach (var webspace in await client.WebSpaces.ListAsync(token))
{
await AddOutput(string.Format("Listing all properties for webspace {0}", webspace.Name));
await AddOutput(string.Format("Property name:{0}, property value {1}", "Name", webspace.Name));
await AddOutput(string.Format("Property name:{0}, property value {1}",
"Availability state", webspace.AvailabilityState));
await AddOutput(string.Format("Property name:{0}, property value {1}",
"Current number of workers", webspace.CurrentNumberOfWorkers));
await AddOutput(string.Format("Property name:{0}, property value {1}",
"Current worker size", webspace.CurrentWorkerSize));
await AddOutput(string.Format("Property name:{0}, property value {1}",
"Geo location", webspace.GeoLocation));
await AddOutput(string.Format("Property name:{0}, property value {1}",
"Geo region", webspace.GeoRegion));
await AddOutput(string.Format("Property name:{0}, property value {1}", "Plan", webspace.Plan));
await AddOutput(string.Format("Property name:{0}, property value {1}", "Status", webspace.Status));
await AddOutput(string.Format("Property name:{0}, property value {1}",
"Subscription", webspace.Subscription));
await AddOutput(string.Format("Property name:{0}, property value {1}",
"Worker Size", webspace.WorkerSize));
WebSiteListParameters websListParameters = new WebSiteListParameters();
await AddOutput(string.Format("Listing available websites in webspace {0}", webspace.Name));
foreach (var website in await client.WebSpaces.ListWebSitesAsync(webspace.Name, websListParameters, token))
{
await AddOutput(string.Format("Listing properties for website " +
"{0} in webspace {1}", website.Name, webspace.Name));
await AddOutput(string.Format("Property name:{0}, property value {1}",
"Admin enabled", website.AdminEnabled));
await AddOutput(string.Format("Property name:{0}, property value {1}",
"Availability state", website.AvailabilityState));
await AddOutput(string.Format("Property name:{0}, property value {1}",
"ComputeMode", website.ComputeMode));
await AddOutput(string.Format("Property name:{0}, property value {1}",
"Enabled", website.Enabled));
await AddOutput(string.Format("Listing enabled hostnames " +
"(part of properties) for website {0} in webspace {1}", website.Name, webspace.Name));
foreach (var enHostname in website.EnabledHostNames)
{
await AddOutput(string.Format("Enaabled hostname {0} for website {1}", enHostname, website.Name));
}
await AddOutput(string.Format("Listing hostnames (part of properties) " +
"for website {0} in webspace {1}", website.Name, webspace.Name));
foreach (var enHostname in website.HostNames)
{
await AddOutput(string.Format("Hostname {0} for website {1}", enHostname, website.Name));
}
await AddOutput(string.Format("Property name:{0}, property value {1}", "Owner", website.Owner));
await AddOutput(string.Format("Property name:{0}, property value {1}",
"Repository Sitename", website.RepositorySiteName));
await AddOutput(string.Format("Property name:{0}, property value {1}",
"Runtime availability state", website.RuntimeAvailabilityState));
await AddOutput(string.Format("Property name:{0}, property value {1}",
"Server Farm", website.ServerFarm));
await AddOutput(string.Format("Property name:{0}, property value {1}",
"Site mode", website.SiteMode));
await AddOutput(string.Format(
"Listing site-properties (part of properties) for website {0} in webspace {1}",
website.Name, webspace.Name));
await AddOutput(string.Format(
"Listing AppSettings in site-properties (part of properties) for website {0} in webspace {1}",
website.Name, webspace.Name));
foreach (var appSettings in website.SiteProperties.AppSettings)
{
await AddOutput(string.Format("Key:{0}, value {1}", appSettings.Key, appSettings.Value));
}
await AddOutput(string.Format(
"Listing metadata in site-properties (part of properties) for website {0} in webspace {1}",
website.Name, webspace.Name));
foreach (var metadata in website.SiteProperties.Metadata)
{
await AddOutput(string.Format("Key:{0}, value {1}", metadata.Key, metadata.Value));
}
await AddOutput(string.Format(
"Listing properties (part of properties) for website {0} in webspace {1}",
website.Name, webspace.Name));
foreach (var property in website.SiteProperties.Properties)
{
await AddOutput(string.Format("Key:{0}, value {1}", property.Key, property.Value));
}
await AddOutput(string.Format(
"Listing ssl-certificates (part of properties) for website {0} in webspace {1}",
website.Name, webspace.Name));
foreach (var sslcert in website.SslCertificates)
{
await AddOutput(string.Format("Cert property:{0}, property value {1}",
"ExpirationDate", sslcert.ExpirationDate));
await AddOutput(string.Format("Cert property:{0}, property value {1}",
"Friendly name", sslcert.FriendlyName));
await AddOutput(string.Format("Cert property:{0}, property value {1}",
"SiteName", sslcert.SiteName));
await AddOutput(string.Format("Cert property:{0}, property value {1}",
"Subject name", sslcert.SubjectName));
await AddOutput(string.Format("Cert property:{0}, property value {1}",
"Thumbprint", sslcert.Thumbprint));
await AddOutput(string.Format("Cert property:{0}, property value {1}",
"Selflink uri", sslcert.SelfLinkUri.AbsoluteUri));
}
await AddOutput(string.Format("Property name:{0}, property value {1}",
"State", website.State));
await AddOutput(string.Format("Property name:{0}, property value {1}",
"Uri", website.Uri.AbsolutePath));
await AddOutput(string.Format("Property name:{0}, property value {1}",
"Usage State", website.UsageState));
await AddOutput(string.Format("Property name:{0}, property value {1}",
"Webspace", website.WebSpace));
}
}
That should be enough to dive into the new Azure management API. I will write a second blog-post and cover the the other services, like the Scheduler (which seems to have two classes currently) as well.