Using the Windows Azure Pack Service Management API Sample Managed Client Library
Overview
The APIClients managed client library is a sample .NET library supplied with the Windows Azure pack Developers Kit. It is a wrapper for the Service Management REST API and provides an object model that abstracts Windows Azure Pack REST entities into an easy to use managed library. The library also greatly simplifies authenticating calls to the Service Management API. This article show how to build a Visual Studio 2013 C# console solution that makes Service Management managed client calls as a Windows Azure Pack administrator. The sample code shows how to list the available plans, create a plan and then subscribe a user to the plan. It also covers how Fiddler can be used to see the REST calls coming from the sample code to the Service Management API.
Note. The sample code was tested against an express deployment of Windows Azure Pack. You can read more here - http://technet.microsoft.com/en-us/library/dn296439.aspx.
Sample Managed Client Library
The sample managed client library ships as a Visual Studio project that you can add to an existing project. The managed client library Visual Studio project has three sections:
- Classes
- Data Contracts
- Utilities
Classes
The Classes provide 3 main sets of functionality:
- Admin API
- Tenant API
- Exception Handling
IAdminManagementClient declares the available admin API methods with the implementations in AdminManagementClient. Likewise for the Tenant API where ITenantManagementClient and ITenantManagement.cs provide the interface declaration and implementations respectively. Both classes derive from ManagementClientBase.
The classes provide functionality for managing the following Windows Azure Pack entities/ Data Contracts:
- Users
- Plans
- Subscriptions
- Resource Providers
- Addons
- Settings
The Service Management API methods available for tenants and admins varies but covers the management tasks you would want to use for each set of functionality. Underneath these calls map to REST API calls to the Service Management API. For example, the call to retrieve the available plans is ListPlansAsync which returns a Plan list of the available plans. Underneath this maps to the following REST call https://<ServiceMgmt>:<Port>/plans. The return includes a JSON package of the available Plans.
The service management REST API is documented http://msdn.microsoft.com/en-us/library/dn629548.aspx.
Data Contracts
Data contracts are used to define the data passed from the client library to the Service Management API on the admin or tenant servers. The client library has data contract definitions for the various Service Management API entities such as Users, Plans, Subscriptions and Resource Providers. To see how the Data Contracts map to the Service Management REST API calls, see the MSDN documentation. For example, the Plan Data Contract has a matching REST object documented at http://msdn.microsoft.com/en-us/library/dn448627.aspx. The Data Contracts are defined in the sample DataContracts folder.
For more information on Data Contracts, see http://msdn.microsoft.com/en-us/library/ms733127(v=vs.110).aspx.
Utilities
A number of utilities for simplifying tasks such as authenticating REST calls. An overview of Windows Azure Pack authentication, including code samples, is available here http://go.microsoft.com/fwlink/?LinkId=331159.
Building the Visual Studio Project
These steps walk you through building a Visual Studio project that includes the Service Management Managed Client library. The solution is then used to demonstrate a number Service Management API tasks, such as querying for plans and creating subscriptions.
- Download the Windows Azure Pack Developers Kit samples from http://go.microsoft.com/fwlink/?LinkId=324039.
- Extract the APIClients folder and copy it to your Visual Studio 2013 Projects folder.
- In Visual Studio 2013, create a Visual C# Console Application solution named MyWAPClient.
- Add the APIClients project In Solution Explorer with these steps
- Right-click the solution MyWAPClient, select Add and then Existing Project….
- In the Add Existing Project dialog navigate to the APIClients folder and select Microsoft.WindowsAzurePack.Samples (CSPROJ) project file.
- Click OK to close the dialog box. If you are presented with a trustworthy source dialog, select OK to add the project.
- In Solution Explorer add the required references by:
- In the MyWAPClient solution right-click References and select Add Reference….
- Click Solution in the left pane and then select Microsoft.WindowsAzurePack.Samples.
- Click Assemblies in the left pane and add the following assembly references.
- System.Configuration
- System.Runtime.Serialization
- System.Net.Http (4.0.0.0)
That’s all that’s required for creating a project that includes the Service Management Sample Client Library. The next step is to add some code to list the available plans.
List the Available Plans
Calling the Service Management API is a multi-step process. First, an authentication token has to be retrieved for authenticating REST calls to the Service Management API. Second the AdminAPI client object has to be created and finally calls made to the Service Management API. The following code shows the following:
- Retrieve configuration information stored in the App.Config file
- Retrieve the administrator user token from the Windows Azure Pack Windows Authentication Endpoint.
- Make a call to list the available plans.
The steps to implementing the sample are:
In program.cs add the following declarations
- using Microsoft.WindowsAzurePack.Samples.DataContracts;
- using System.Configuration;
- using System.Diagnostics;
- using System.Net;
- using System.Net.Security;
Change the MyWAPClient namespace to namespace Microsoft.WindowsAzurePack.Samples.MyWAPClient
Change the main function to the following:
static void Main(string[] args) { //Admin Credentials for getting Admin User Token and the location of the Admin API Endpoint string windowsAuthEndpoint = ConfigurationManager.AppSettings["windowsAuthEndpoint"]; string adminDomainName = ConfigurationManager.AppSettings["adminDomainName"]; string adminUsername = ConfigurationManager.AppSettings["adminUsername"]; string adminPassword = ConfigurationManager.AppSettings["adminPassword"]; string adminApiEndpoint = ConfigurationManager.AppSettings["adminApiEndpoint"]; // This is used here to suppress/ignore certificate errors when using test certificates. // Remove this line of code when you are using trusted certificates. ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; }); //Get Administrator User Token Trace.WriteLine("Getting User token..."); var token = TokenIssuer.GetWindowsAuthToken(windowsAuthEndpoint, adminDomainName, adminUsername, adminPassword); try { //TASK ONE - List Plans ListPlans(adminApiEndpoint, token); // TASK TWO - Create a Plan and Subscription // Create a new plan /* Plan plan; string planName = "My Plan Name"; string resourceProviderType = "SQL Servers"; plan = CreatePlan(planName, resourceProviderType, adminApiEndpoint, token); // Create a subscription string userName = "test@test.com"; string coAdminName = "mycoadmin@contosa.com"; string subscriptionName = "My subscription name"; Subscription subscription; subscription=CreateSubscription(userName, coAdminName, subscriptionName, plan.Id, adminApiEndpoint, token); Console.WriteLine("Subscription " + subscription.SubscriptionName + " created to plan " + plan.DisplayName + " for user " + userName); */ } catch (Exception ex) { Console.WriteLine(ex.Message); }; Console.ReadLine(); }
Add the following method that lists the available plans.
static void ListPlans(string adminApiEndpoint, string token) { AdminManagementClient adminClient; try { adminClient = new AdminManagementClient(new Uri(adminApiEndpoint), token); } catch (Exception ex) { Trace.WriteLine("List Plans: Failed to get Service Management API client"); throw new Exception("List Plans: Failed to get Service Management API client", ex); } try { PlanList pList = adminClient.ListPlansAsync().Result; foreach (Plan plan in pList) { Console.WriteLine("Name: " + plan.DisplayName); Console.WriteLine("ID: " + plan.Id); Console.WriteLine(); } } catch (Exception ex) { Trace.WriteLine("List Plans: Failed to get plans -" + ex.Message); throw new Exception("List Plans: Failed to get plans", ex); } }
Add the configuration information for your deployment. Do this by adding the following section to the section of the MyWAPClient project App.Config file.
<appSettings> <add key="windowsAuthEndpoint" value="https://<WindowsAuthenticationEndpoint>:30072" /> <add key="adminDomainName" value="<domain>" /> <add key="adminUsername" value="<administrator>" /> <add key="adminPassword" value="<password>" /> <add key="adminApiEndpoint" value="https://<AdminAPIEndpoint>:30004" /> </appSettings>
The values you add depend on your deployment. Taking a peek at IIS manager is a useful way to determine the following:
- adminApiEndpoint - Service Management API site (MgmtSvc-AdminAPI)
- windowsAuthEndpoint - Windows Authentication site (MgmtSvc-WindowsAuthSite)
Try building and running the code. You should see a list of the installed Plans and the associated plan identifier displayed. When the sample is run, a REST call is made to the Service Management API to retrieve a list of the available plans. The actual call is documented in the Windows Azure Pack Developers Kit here http://msdn.microsoft.com/en-us/library/dn448758.aspx.
Plans and Subscriptions
So now that you can build, run and debug a simple Service Management Managed Client app. Let’s take a look at something deeper. In Windows Azure Pack the services you provide, such as Web Site Hosting or SQL Server databases, are offered to tenant users as Plans. A subscription in Windows Azure Pack is the contract created for a tenant user when she wants to use a specific Plan. Tenants can subscribe to public plans or the tenants can be subscribed to plans by Windows Azure Pack administrators. To support team management, subscriptions can also be configured to have one or more co-administrators. The following code samples demonstrate how to, as an admin, create a plan and then create a subscription to the plan for a tenant user.
Creating the Plan
The base information needed for creating a plan is a plan name, the service the plan offers, an advertisement and other configuration information. This sample constructs the information it needs from a supplied plan name and service (resource provider) name.
Add the following function that sets up the necessary data and creates a new plan.
/// <summary>
/// Creates a plan at the supplied Admin endpoint
/// </summary>
/// <param name="name">Name of the plan to be created</param>
/// <param name="resourceName">service type to be created - SQL Server, MY SQL etc.</param>
/// <param name="adminApiEndpoint">Service Managment API Admin endpoint</param>
/// <param name="token">Authentication token</param>
/// <returns></returns>
public static Plan CreatePlan(string name, string resourceName, string adminApiEndpoint, string token)
{
Plan plan = null;
string instanceIdentifier = null;
string resourceProviderName = null;
ResourceProvider foundResource = null;
ResourceProviderList resourceProviderList = null;
AdminManagementClient adminClient = new AdminManagementClient(new Uri(adminApiEndpoint), token);
//Check plan name is unique
PlanList pList = adminClient.ListPlansAsync().Result;
Plan planExist = null;
planExist = pList.Find(delegate(Plan p)
{
return p.DisplayName == name;
});
if (planExist != null)
{
Trace.WriteLine("Create Plan: Plan (" + name + ") already exists");
throw (new Exception("Create Plan: Plan (" + name + ") already exists"));
}
// get the resource providers...
try
{
resourceProviderList = adminClient.ListResourceProvidersAsync().Result;
}
catch (Exception ex)
{
Trace.WriteLine("Create Plan: Failed to get resource providers - " + ex.Message);
throw new Exception("Create Plan: Failed to get resource providers", ex);
}
// ... to get the resource provider id & name
foreach (ResourceProvider resourceProvider in resourceProviderList)
{
if (resourceProvider.DisplayName == resourceName)
{
foundResource = resourceProvider;
instanceIdentifier = resourceProvider.InstanceId;
resourceProviderName = resourceProvider.Name;
}
}
// the requested resource provider was not found
if (instanceIdentifier == null)
{
Trace.WriteLine("Create Plan: Resource Provider (" + resourceName + ") not found");
throw (new Exception("Create Plan: Resource Provider (" + resourceName + ") not found"));
}
// Create advertisement
var planAdvertisement = new PlanAdvertisement()
{
DisplayName = name,
LanguageCode = "en-us",
Description = null
};
IList<PlanAdvertisement> planAdvertisementList = new List<PlanAdvertisement>();
planAdvertisementList.Add(planAdvertisement);
// Create service quota
var serviceQuota = new ServiceQuota()
{
ServiceName = foundResource.Name,
ServiceInstanceId = foundResource.InstanceId,
ServiceDisplayName = foundResource.DisplayName,
ConfigState = QuotaConfigurationState.NotConfigured,
QuotaSyncState = QuotaSyncState.OutOfSync,
Settings = new List<ServiceQuotaSetting>(),
};
ServiceQuotaList serviceQuotaList = new ServiceQuotaList();
serviceQuotaList.Add(serviceQuota);
// In this sample, these are passed empty
IList<PlanAddOnReference> addOnReferences = new List<PlanAddOnReference>();
IList<PlanAddOn> addOns = new List<PlanAddOn>();
var newPlan = new Plan()
{
DisplayName = name,
Id = name + Guid.NewGuid().ToString(), // create unique identifier
State = PlanState.Public,
ConfigState = (int)QuotaConfigurationState.NotConfigured,
QuotaSyncState = QuotaSyncState.OutOfSync,
LastErrorMessage = null,
Advertisements = planAdvertisementList,
ServiceQuotas = serviceQuotaList,
SubscriptionCount = 0,
MaxSubscriptionsPerAccount = 1,
AddOnReferences = addOnReferences,
AddOns = addOns,
InvitationCode = null,
Price = null
};
// all data structure are ready, create the plan
try
{
plan = adminClient.CreatePlanAsync(newPlan).Result;
}
catch (Exception ex)
{
Trace.WriteLine("Create Plan: Failed to create plan - " + ex.Message);
throw new Exception("Create Plan: Failed to create plan", ex);
}
//plan created successfully
Trace.WriteLine("Create Plan: Plan (" + plan.DisplayName + ") created");
return plan;
}
Creating the Subscription
A sample that creates a subscription has to know several pieces of information.
- The user that wants to make the subscription. This has to be a known user within the system.
- The plan identifier for the plan being subscribed to.
- A Subscription identifier GUID value. This is created by the client code that wants to create a subscription.
- A name for the subscription.
- A list of the required Co-Administrators.
Note. For creating users, the following article gives a great overview of creating a user in the Service Management API as well as creating it in the authentication system - http://blogs.technet.com/b/privatecloud/archive/2014/02/03/creating-users-in-windows-azure-pack.aspx. The following code sample function shows to how to validate that the user exists, construct the subscription input data, including a single co-administrator, and finally create the subscription.
Add the following function to your project.
/// <summary>
/// Creates a subscription.
/// </summary>
/// <param name="adminAccount">The user account subscribing to the subscription</param>
/// <param name="coAdmin">The coadministrator for the subscription</param>
/// <param name="subscriptionName">The subscription name</param>
/// <param name="planId">the plan identifier that the user is subscribing to</param>
/// <param name="adminApiEndpoint">Service Managment API Admin endpoint</param>
/// <param name="token">authentication token</param>
/// <returns></returns>
static Subscription CreateSubscription(string adminAccount, string coAdmin, string subscriptionName, string planId, string adminApiEndpoint, string token)
{
Plan planExist;
User user = null;
Subscription subscription;
AzureProvisioningInfo azureProvisioningInfo = new AzureProvisioningInfo();
AdminManagementClient adminClient;
try
{
adminClient = new AdminManagementClient(new Uri(adminApiEndpoint), token);
}
catch (Exception ex)
{
Trace.WriteLine("Create Subscription: Failed to get Service Management API client");
throw new Exception("Create Subscription: Failed to get Service Management API client", ex);
}
//Confirm user exists
try
{
user = adminClient.GetUserAsync(adminAccount).Result;
}
catch (Exception ex)
{
Trace.WriteLine("Create Subscription: Failed to get user (" + adminAccount + ") - " + ex.Message);
throw new Exception("Create Subscription: Failed to get user (" + adminAccount + ")", ex);
}
//Confirm plan exists
try
{
planExist = adminClient.GetPlanAsync(planId).Result;
}
catch (Exception ex)
{
Trace.WriteLine("Create Subscription: Failed to get plan (" + planId + ") - " + ex.Message);
throw new Exception("Create Subscription: Failed to get plan (" + planId + ")", ex);
}
// build subscription provisioning info
List<string> coAdmins = new List<string>();
coAdmins.Add(coAdmin);
azureProvisioningInfo.SubscriptionId = Guid.NewGuid();
azureProvisioningInfo.AccountAdminLiveEmailId = null;
azureProvisioningInfo.AccountAdminLivePuid = user.Name;
azureProvisioningInfo.OfferCategory = null;
azureProvisioningInfo.FriendlyName = subscriptionName;
azureProvisioningInfo.CoAdminNames = coAdmins;
azureProvisioningInfo.Status = null;
azureProvisioningInfo.PlanId = planExist.Id;
// Provision subscription
try
{
subscription = adminClient.ProvisionSubscriptionAsync(azureProvisioningInfo).Result;
}
catch (Exception ex)
{
Trace.WriteLine("Create Subscription: Failed to create subscription (" + subscriptionName + ") - " + ex.Message);
throw new Exception("Create Subscription: Failed to create subscription (" + subscriptionName + ")", ex);
}
// Succeeded
Trace.WriteLine("Create Subscription: subscription (" + subscription.SubscriptionName + ") created");
return subscription;
}
Call the Plan & Subscription creation code
Within in the main function uncomment the following code.
// TASK TWO - Create a Plan and Subscription/*// Create a new planPlan plan;string planName = "My Plan Name";string resourceProviderType = "SQL Servers";plan = CreatePlan(planName, resourceProviderType, adminApiEndpoint, token); // Create a subscriptionstring userName = "test@test.com";string coAdminName = "mycoadmin@contosa.com";string subscriptionName = "My subscription name";Subscription subscription; subscription=CreateSubscription(userName, coAdminName, subscriptionName, plan.Id, adminApiEndpoint, token);Console.WriteLine("Subscription " + subscription.SubscriptionName + " created to plan " + plan.DisplayName + " for user " + userName);*/
The code performs two tasks. Firstly it call CreatePlan to create a plan that offers SQL Server. By changing resourceProviderType you can offer different services such as MySQL Servers or VM Clouds. To get a list of the available resource providers (Services) use ListResourceProvidersAsync. Secondly CreateSubscription is called to subscribe the supplied user to the previously created plan. It also adds a single co-admin to the subscription.
Run the program and you should see that the plan and subscription are created and viewable in the admin management portal. In the tenant management portal, the user should see the plan she was subscribed to. If not, exceptions raised should give you an idea about what failed. Also take a look at the Fiddler captures (see next for maore information) – these should give you very specific information. A common issue that the plans aren’t configured to allow more than subscription. In Fiddler you’ll see this reported in the response to the subscription creation call.
Using Fiddler to View REST Calls
Fiddler is a useful debugging tool for REST calls. It can be used to see the REST calls from your application. It can also be configured to intercept the REST calls coming from the Admin and Tenant management portals. This makes it very useful for understanding how the management portals interact with the Service Management API. If you’re building a custom management portal, for example, the ability to see how management tasks are processed will be invaluable to you. To use Fiddler,
- Download fiddler from http://www.telerik.com/fiddler.
- Install Fiddler on the computer that you run your WAP code from.
- Set up Fiddler to capture HTTPS connections by selecting Tools, Fiddler options. In the Fiddler Options dialog select the Capture HTTPS connects and Decrypt HTTPS traffic check boxes.
Fiddler in action
With Fiddler Running run the List Plans sample again. In the left pane you should see the call to get the available plans.
Select the plan and in the right pane select the raw tab from the top pane. This is the raw REST call made to the service management API and should look similar to this:
GET https://computer:30004/plans HTTP/1.1
Authorization: Bearer <token removed>
Host: <computer>:30004
The response, shown in the bottom right pane will look similar to the following. For clarity several plans have been removed from this sample.
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Mon, 28 Apr 2014 21:42:44 GMT
Content-Length: 6792
[{"Id":"MyPlaht1siq4x","DisplayName":"My Plan 1","State":0,"ConfigState":1,"QuotaSyncState":0,"LastErrorMessage":null,"Advertisements":[{"LanguageCode":"en-us","DisplayName":"My Plan 1","Description":null}],"ServiceQuotas":[{"ServiceName":"sqlservers","ServiceInstanceId":"A6720023-C0DD-4D5A-AE3B-DEF377202F75","ServiceDisplayName":"SQL Servers","ServiceInstanceDisplayName":"SQL Servers","ConfigState":1,"QuotaSyncState":0,"Settings":[{"Key":"Editions","Value":"[{\"displayName\":\"Default\",\"groupName\":\"Default\",\"resourceCount\":\"10\",\"resourceSize\":\"1024\",\"resourceSizeLimit\":\"1024\",\"offerEditionId\":\"032114061727\",\"groupType\":null}]"}]}],"SubscriptionCount":4,"MaxSubscriptionsPerAccount":1,"AddOnReferences":[],"AddOns":[],"InvitationCode":null,"Price":null]
With a large return set, it can be difficult to interpret this data. Fiddler helps by allowing you to see the JSON information formatted for readability. Do this by selecting the JSON tab in the bottom pane.
Summary
So that was brief introduction to the Service Management API sample managed client API. We covered building a Visual Studio solution, listing and creating plans, creating subscriptions and the use of Fiddler in debugging your code. The cool part is that the sample comes complete with source code and should be a great starting point for managing Windows Azure Pack with the Service Management API.