Windows Azure Pack, Service Provider Foundation and IaaS API Support for VMM Service Templates

Windows Azure Pack, Service Provider Foundation and IaaS API Support for VMM Service Templates

  • Comments 3
  • Likes

Introduction

Special thanks to Srimathi Santhanam, Thomas Yip, and Filippo Seracini (a.k.a. The Doctor) for putting together and testing the examples in this post.

Following the release of System Center 2012 R2 and Windows Azure Pack in fall of 2013, we have been getting an increasing number of inquiries from people wanting help on how to think about support for VMM Service Templates in our IaaS APIs. To be completely clear: Service Provider Foundation has included support for managing and using VMM service templates since its inception.

The most likely reason for people asking questions is the very strong emphasis on Azure consistency which permeates all our efforts in pursuit of the Cloud OS vision. To help people truly realize the long-term benefits of the Cloud OS (one experience, one toolset, one API) we began some time ago to encourage customers and partners to focus on Windows Azure Pack’s Service Management API as the entry point for programming against IaaS capabilities in the part of the Cloud OS powered by Windows Server & System Center. From this perspective, Service Provider Foundation is merely the conduit between Windows Azure Pack’s Service Management API and System Center, not the primary IaaS API itself.

However, also because of the emphasis on Azure consistency, the Service Management API in Windows Azure Pack does not include support for VMM service templates. And while it does include support for the Azure-consistent VM Role artifact, which is a similar concept, this newer artifact is currently lacking some capabilities of VMM service templates (e.g. multiple tiers in a single definition).

So, while we wait on VM Role evolution to close the gaps, what should people do? The answer is to take a hybrid approach: continue to focus on Windows Azure Pack’s Service Management API for your IaaS programming efforts, but also use Service Provider Foundation for VMM service templates if you want programmatic access to those capabilities. Done correctly, you can retain the Windows Azure Pack plan/subscription context and also make use of VMM service templates if you need to.

To provide support for service templates, you first have to add a service template to the tenant subscription, and then execute the desired operations on the service (create, delete, scale out, etc.).

The following information describes how to do this, with generalized examples (i.e. you will need to edit these examples for them to function properly in your deployed environment).

To add a Service Template to a Tenant Subscription via Service Management Automation (SMA) Runbook

The Admin provides tenants (subscribed through WAP Tenant API) with access to a Service Template through an SMA runbook that gets triggered by SPF when the tenant creates a subscription:

  1. Open Admin console in the WAP portal, access the “Automation” section (left nav pane) and create a new runbook 
  2. Paste the script you see below into the new runbook
  3. Replace the variables with your environment-specific values
  4. Publish the runbook, and make sure to enter the tag value of “SPF” into the runbook configuration (just those three letters, no quotes, this is critical else you won’t see the runbook in the next step), click save
  5. Access the “VM Clouds” section, “Automation” tab, and create a new event-to-runbook mapping: object = “subscription”; action = create; runbook = the one you just created, whatever you named it
workflow AddServiceTemplateToSubscription
{
    param
    (
     [Parameter(Mandatory=$true)]
        [string] $name,
        
        [Parameter(Mandatory=$true)]
        [string] $operation,
        
        [Parameter(Mandatory=$true)]
        [object] $resourceObject
    )
    
    #only perform action when it is subscription create
    
    if (($name -ne "Subscription") -or ($operation -ne "Create"))
    {
        throw "Mismatch operation. Finishing runbook"
    }
 
        $subscriptionName = <your subscriptionName>
 
    if ($resourceObject.SubscriptionName -eq $subscriptionName)
    {
        $UserRoleId = $resourceObject.SubscriptionId
        
        #define variable for service template we are going to use
        $serviceTemplateName = <your ServiceTemplateName>
        $serviceTemplateRelease = <your ServiceTemplateRelease>
        $SystemCred = <Your Credentials>
        $VmmServer1 = <Your VMM Server>
        
        if ($VmmServer1 -eq $null -or $VmmServer1 -eq "") {    
           throw "Cannot get VMM Instance"
        }    
        if ($SystemCred -eq $null -or $SystemCred -eq "") {    
           throw "Cannot get SystemCred"
        }
        try
        {
            InlineScript
            {
                Import-Module virtualmachinemanager
                $VmmServer = get-scvmmserver -computername localhost
                $UserRole = ""
                $count = 0
                do # it may take a while for the userrole to be created
                {
                    $Userrole = Get-SCUserRole -ID $using:UserRoleID -VMMServer $VmmServer
                    sleep 10
                    $count++
                } while (($UserRole -eq $null) -and ($count -lt 20))
                if ($UserRole -eq $null)
                {
                    throw "UserRole is not created in VMM"
                }
                
                #get service template
                $serviceTemplate = get-SCServiceTemplate -Name $using:serviceTemplateName | where {$_.Release -eq $using:serviceTemplateRelease}
                if ($serviceTemplate -eq $null)
                {
                    throw "Service Template not found"
                }
                
                #Grant permission 
                Grant-SCResource -Resource $serviceTemplate -UserRoleName $UserRole.Name
                
            } -PSComputerName $VmmServer1 -PSCredential $SystemCred
        }
        catch
        {
            throw $_
        }
        
    }
}

To create a new Service by using .NET

  1. Connect to the Service Provider Foundation VMM service.
  2. Create a new method. Let’s call it CreateService.
  3. Create a new instance of VMConfiguration class.
  4. Add the VMConfiguration instance to a new instance of the ObservableCollection<VMConfiguration> class.
  5. Create a new instance of ServiceTierAndVMConfiguration class.
    • Set the Name property.
    • Set the VMConfigurations property with the previously created instance of the ObservableCollection<VMConfiguration> class.
  6. Create a new instance of the ObservableCollection<ServiceTierAndVMConfiguration> class.
    • Add the instance of ServiceTierAndVMConfiguration to it.
  7. Create a new instance of the ServiceDeploymentConfiguration class.
    • Set the Name property.
    • Set the Description property.
  8. Create a new instance of the NewServiceDeployment class
    • Set the ServiceConfiguration property with the instance of NewServiceDeployment previously created.
    • Set the TierConfiguration property with the instance of ObservableCollection<ServiceTierAndVMConfiguration> previously created.
  9. Create a new instance of Service.
public Service CreateService(string subscriptionId, string name, ServiceTemplate serviceTemplate, Guid stampId, Guid cloudId)
        {
 
            ServiceDeploymentConfiguration sdc = new ServiceDeploymentConfiguration()
            {
                Name = <name of your service deployment configuration>,
                Description = <description of your service deployment configuration>
            };
            
            // Depending on the Service Template create the VMTier and VMConfig
            ObservableCollection<ServiceTierAndVMConfiguration> serviceTierAndVMConfiguration = new ObservableCollection<ServiceTierAndVMConfiguration>();
            ObservableCollection<VMConfiguration> vmConfigurations = new ObservableCollection<VMConfiguration>();
            VMConfiguration vmc = new VMConfiguration()
            {
                ComputerName = <your tier VM name>
                VMName = <your VM name here>
            };
            vmConfigurations.Add(vmc);
 
            ServiceTierAndVMConfiguration tierAndVMConfig  = new ServiceTierAndVMConfiguration()
            {
                Name = "SQLServer Host Tier",
                VMConfigurations = vmConfigurations
            };
            serviceTierAndVMConfiguration.Add(tierAndVMConfig);
 
            // Provide ServiceTemplate ID
            Guid serviceTemplateID = <your serviceTemplateID. You can retrieve the serviceTemplateID via RDFE query>
 
            // Provide Cloud ID
            Guid cloudID = <your cloudID>
 
            // Provide Stamp ID
            Guid stampID = <your stampID>
 
 
            // add the ServiceDeployment
            NewServiceDeployment serviceDep = new NewServiceDeployment { 
                ServiceConfiguration = sdc ,
                TierConfigurations = serviceTierAndVMConfiguration
            };
            
            // Instantiate a VMM Service        
            var service = new Service()
            {
                Name = name,
                StampId = stampID,
                CloudId = cloudID,
                ServiceTemplateId = serviceTemplateID
                NewServiceDeployment = serviceDep
 
            };
 
            // POST a HTTP Request. Be sure to add the Tenant Certificate to the http request prior to this. See Snippet for Tenant Client below
            HttpResponseMessage response = TenantClient.PostAsJsonAsync<Service>(TenantServiceEndPoint + subscriptionId + @"/services/systemcenter/VMM/Services/", service);
            return response.Content.ReadAsAsync<Service>().Result;
        }

To POST an HTTP request

In order to invoke service creation, you need to have client code executing an HTTP POST.  For an example of Tenant Client, see code snippet below:

public TenantClient(string tenantServiceEndpoint, string authSiteEndPoint, string userName, string password, ref DateTime tokenExpiryTime)
        {
            httpClient = new HttpClient();
            var identityProviderEndpoint = new EndpointAddress(new Uri(authSiteEndPoint + "wstrust/issue/usernamemixed"));
 
            var identityProviderBinding = new WS2007HttpBinding(SecurityMode.TransportWithMessageCredential);
            identityProviderBinding.Security.Message.EstablishSecurityContext = false;
            identityProviderBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
            identityProviderBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
 
            var trustChannelFactory = new WSTrustChannelFactory(identityProviderBinding, identityProviderEndpoint)
            {
                TrustVersion = TrustVersion.WSTrust13,
            };
 
            trustChannelFactory.Credentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication() { CertificateValidationMode = X509CertificateValidationMode.None };
            trustChannelFactory.Credentials.SupportInteractive = false;
            trustChannelFactory.Credentials.UserName.UserName = userName;
            trustChannelFactory.Credentials.UserName.Password = password;
 
            var channel = trustChannelFactory.CreateChannel();
            var rst = new RequestSecurityToken(RequestTypes.Issue)
            {
                AppliesTo = new EndpointReference("http://azureservices/TenantSite"),
                TokenType = "urn:ietf:params:oauth:token-type:jwt",
                KeyType = KeyTypes.Bearer,
            };
 
            RequestSecurityTokenResponse rstr = null;
 
            var token = channel.Issue(rst, out rstr);
            tokenExpiryTime = new DateTime(token.ValidTo.Year, token.ValidTo.Month, token.ValidTo.Day, token.ValidTo.Hour, token.ValidTo.Minute, token.ValidTo.Second, DateTimeKind.Utc);
            var tokenString = (token as GenericXmlSecurityToken).TokenXml.InnerText;
            var jwtString = Encoding.UTF8.GetString(Convert.FromBase64String(tokenString));
 
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtString);
            this.tenantServiceEndPoint = tenantServiceEndpoint;
            httpClient.PrincipalId = <your ID>;
        }

In order for the client to work it requires a certificate, added prior to the HTTP request, as seen in the following example:

//Adding Certificate through Public Endpoint https://<>:30006/
 
            var handler = new WebRequestHandler();
            X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
            store.Open(OpenFlags.ReadOnly);
            var result = store.Certificates.Find(X509FindType.FindByIssuerName, <your trusted certificate autority>, false);
            if (result.Count == 0)
            {
                throw new Exception("tenant public api certificate not found.");
            }
            X509Certificate2 cert = result[0];
            handler.ClientCertificates.Add(cert);
            httpClient = new HttpClient(handler);
            this.tenantServiceEndPoint = tenantServiceEndpoint;
 
              
            SubscriptionCertificate subscriptionCertificate = new SubscriptionCertificate()
            {
                SubscriptionCertificateData = <data>,
                SubscriptionCertificatePublicKey = <SubscriptionCertificatePublicKey>,
                SubscriptionCertificateThumbprint = <SubscriptionCertificateThumbprint>
            };
 
            httpClient.PostAsXmlAsync<SubscriptionCertificate>(TenantServiceEndPoint + @"subscriptions/" + subscriptionId + @"/certificates", subscriptionCertificate);
       
Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hi John,
    It is unclear where CreateService method is stored. Can you give more details? Thanks.

  • My guess is that it's stored wherever you want and wherever you can call it from...

  • Dear John
    Cloud you provide detailed build step? thanks !!