Freigeben über


Custom Resource Providers in Windows Azure Pack - Extending the Hello World Sample calling a SMA Runbook

Today we have a guest blogger! Torsten Kuhn, Principal Consultant from Microsoft Consulting Services in Germany will walk us through the process of extending the Hello World Custom Resource Provider to call a SMA Runbook!

Hello readers I'm Torsten Kuhn a Principal Consultant from Microsoft Services and in this post I will walk you through the different steps required to programmatically extend the Hello World custom Resource Provider sample that is included in the Windows Azure Pack Developers Kit. This post is based on the Azure Pack example resource provider 'Hello World'. In this blog post I'll show all the necessary steps to extend the 'Hello World' example from the UI to the backend REST API with custom code.

Dear readers! – You should be familiar with: Visual Studio, REST API's, JavaScript and Compile, Compile, Compile… J

This blog post goes in line with the post from Victor Arzate that describes what Resource Providers are and how you can deploy the Hello World sample. You can learn more about the Hello World sample here and also, you can learn more about custom resource providers here.

Prerequisites

  • Visual Studio 2012 installed (in my case I had Update 4 applied as well). Note that Visual Studio 2012 does not need to be installed in the same machine where you have Windows Azure Pack (WAP) installed.
  • Windows Installer XML (WiX) toolset 3.7.1224.0 installed (direct download: https://wixtoolset.org/releases/feed/v3.7)
  • A Windows Azure Pack environment up & running.
  • Hello World Custom Resource Provider deployed as described in the blog post from Victor Arzate here.
  • Service Management Automation (SMA) from the System Center 2012 R2 Orchestrator DVD a trial version could be downloaded here. This blog uses the "Sample-Using-RunbookParameters" runbook that is included in the SMA extension of the WAP Admin Site.

This blog explains the following steps to extend the Hello World Sample with a call to a SMA Runbook:

Step 1 - Create the backend REST API

Step 2 - Implement the call to the SMA Runbook

Step 3 - Changes to the Hello World API client

Step 4 - Call into the REST API from the MVC controller of the tenant site

Step 5 - Implement the UI logic that triggers the call to the backend service

 

Step 1 – Create the backend REST API

Launch Visual Studio 2012 and open the Hello World Solution located in the folder where you extracted the sample archive (in my case C:\Projects\Microsoft.WAP.Samples.HelloWorld\ Microsoft.WAP.Samples.HelloWorld.sln). Navigate to the Microsoft.WAP.Samples.HelloWorld.Api project. Open the WebApiConfig.cs located in the App_Start folder and add the following lines:

config.Routes.MapHttpRoute(

name: "ExecuteRunbook",

routeTemplate: "subscriptions/{subscriptionId}/executerunbook",

defaults: new { controller = "FileShare" });

This will route the ExecuteRunbook call to the FileShareController. Go to the FileShareController.cs and add the following empty function stub into the FileShareController class (in step 2 we will implement the call to the REST API):

[HttpPost]

[System.Web.Http.ActionName("executerunbook")]

public void ExecuteRunbook(string subscriptionId, RunbookParameter runbookParameters)

{

 

}

We will pass the runbook parameters in a custom RunbookParameter class. In order to create this class navigate to the Microsoft.WAP.Samples.HelloWorld.ApiClient project select the DataContracts folder, add a new class named RunbookParameter and fill in the following code:

using System;

using System.Runtime.Serialization;

 

namespace Microsoft.WindowsAzurePack.Samples.HelloWorld.ApiClient.DataContracts

{

[DataContract(Namespace = Constants.DataContractNamespaces.Default)]

public class RunbookParameter

{

[DataMember(Order = 0)]

public string Name { get; set; }

[DataMember(Order = 1)]

public int Number { get; set; }

[DataMember(Order = 2)]

public string[] StringArray { get; set; }

[DataMember(Order = 3)]

public DateTime Date { get; set; }

[DataMember(Order = 4)]

public Boolean SayGoodbye { get; set; }

}

}

Make sure that the System.Runtime.Serialization assembly is referenced by the project.

 

Step 2 – Implement the call to the SMA Runbook

Now we will fill in the code to call the SMA Runbook. First we need to add a service reference to the SMA web service we installed in the prerequisites section. Make sure that the Microsoft.WAP.Samples.HelloWorld.Api project is selected and use the "Add Service Reference" command from the projects context menu. Enter the URL to the endpoint of the Orchestrator web site (on my machine https: \\theserver:9090) and add the empty GUID parameter 00000000-0000-0000-0000-000000000000 as displayed in the picture otherwise the call to the service endpoint will fail with a 404 HTTP error. Use SMAWebservice as the namespace:

Open the file FileShareController.cs file in the Controllers subfolder and add the following using statements:

using System;

using System.Collections.Generic;

using System.Globalization;

using System.Data.Services.Client;

using System.Net;

using System.Net.Security;

using System.Threading.Tasks;

using Microsoft.WindowsAzurePack.Samples.HelloWorld.Api.SMAWebService;

using IO = System.IO;

using System.Linq;

using System.Web.Http;

using Microsoft.WindowsAzurePack.Samples.HelloWorld.ApiClient.DataContracts;

Then navigate to the ExecuteRunbook empty function that we created in step 1 and replace it with the following code:

[HttpPost]

[System.Web.Http.ActionName("executerunbook")]

public void ExecuteRunbook(string subscriptionId, RunbookParameter rbParameter)

{

var api = new OrchestratorApi(new Uri("https://theserver:9090//00000000-0000-0000-0000-000000000000"));

((DataServiceContext)api).Credentials = CredentialCache.DefaultCredentials;

ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });

 

var runbook = api.Runbooks.Where(r => r.RunbookName == "Sample-Using-RunbookParameters").AsEnumerable().FirstOrDefault();

if (runbook == null) return;

 

var runbookParams = new List<NameValuePair>

{

new NameValuePair() {Name = "Name", Value = rbParameter.Name},

new NameValuePair() {Name = "Number", Value = rbParameter.Number.ToString(CultureInfo.InvariantCulture)},

new NameValuePair() {Name = "StringArray", Value = string.Join(",", rbParameter.StringArray)},

new NameValuePair() {Name = "Date", Value = rbParameter.Date.ToLongDateString()},

new NameValuePair() {Name = "SayGoodbye", Value = rbParameter.SayGoodbye.ToString(CultureInfo.InvariantCulture)}

};

 

OperationParameter operationParameters = new BodyOperationParameter("parameters", runbookParams);

var uriSma = new Uri(    string.Concat(api.Runbooks, string.Format("(guid'{0}')/{1}", runbook.RunbookID, "Start")),UriKind.Absolute);

var jobIdValue = api.Execute<Guid>(uriSma, "POST", true, operationParameters) as QueryOperationResponse<Guid>;

if (jobIdValue == null) return;

 

var jobId = jobIdValue.Single();

Task.Factory.StartNew(() => QueryJobCompletion(jobId));

}

 

private void QueryJobCompletion(Guid jobId)

{

 

}

Please note that in the last line in I started a new background task with a delegate to QueryJobCompletion. This function queries for job completion and handles the status update for the called runbook. In a future blog I will explain the different options we have to give feedback from long running backend operations back to the WAP portal user.

 

Step 3 – Changes to the HelloWorld API client

The HelloWorld API client (Microsoft.WAP.Samples.HelloWorld.ApiClient) hides all the complexity when calling the backend REST API's. The Tenant site MVC controller only makes simple function calls to the client API and then the client creates the target Uri's and makes the necessary HTTP PUT and GET calls to the backend, transforms the results in a way that makes it easy for the UI part of the portal to use them. Navigate to the Microsoft.WAP.Samples.HelloWorld.ApiClient project and open the HellworldClient.cs file. Add the following constant as the URI template for the new REST API at the top off HelloWorldClient class:

private const string TenantExecuteRunbook = "{0}/" + RegisteredPath + "/executerunbook";

Then add the following function to the same class:

public async Task ExecuteRunbook(string subscriptionId, RunbookParameter rb)

{    

var requestUrl =

this.CreateRequestUri(

string.Format(CultureInfo.InvariantCulture,

TenantExecuteRunbook, subscriptionId));

await this.PostAsync<RunbookParameter>(requestUrl, rb);

}

 

 

Step 4 – Call into the REST API from the MVC controller of the tenant site

Now we will add the new HelloWorldClient API call to the Tenant site's HelloWorldTenantController. To do so navigate to the Microsoft.WAP.Samples.HelloWorld.TenantExtension project and open the Models subfolder. Create a new RunbookParameterModel class that is used to pass the parameter values for the REST API from the UI to the HelloWorldTenantController and add the following code fragment:

using Microsoft.WindowsAzurePack.Samples.HelloWorld.ApiClient.DataContracts;

public class RunbookParameterModel

{

public string Name { get; set; }

public int Number { get; set; }

public string[] StringArray { get; set; }

public DateTime Date { get; set; }

public Boolean SayGoodbye { get; set; }

 

public RunbookParameter ToApiObject()

{

return new RunbookParameter()

{

Name = this.Name,

Number = this.Number,

StringArray = this.StringArray,

Date = this.Date,

SayGoodbye = this.SayGoodbye

};

}

}

 

Open the HelloWorldTenantController.cs and add the following method to the existing code:

[HttpPost]

[ActionName("ExecuteRunbook")]

public async Task<JsonResult> ExecuteRunbook(string subscriptionId,RunbookParameterModel rb)

{

await ClientFactory.HelloWorldClient.ExecuteRunbook(subscriptionId,rb.ToApiObject());

return this.Json("Success");

}

 

 

Step 5 – Implement the UI logic that triggers the call to the backend service

The last piece of the puzzle is the JavaScript code we need to trigger the call to the ExcecuteRunbook API from the user interface. For simplicity reasons we make the call from a button event handler, the parameter values are passed as constant values. In one of my next blogs I will show you how to collect the parameter values from a wizard based UI.

  • Open the HelloWorldTenant.Controller.js located in the TenantExtension\Content\Scripts subfolder and add the following function:

function executeRunbook(subscriptionId, name, number, stringArray, date, sayGoodbye) {

return Shell.Net.ajaxPost({

data: {

subscriptionId: subscriptionId,

Name: name,

Number: number,

StringArray: stringArray,

Date: date,

SayGoodbye: sayGoodbye

},

url: baseUrl + "/ExecuteRunbook"

});

}

 

  • In order to make the function accessible from the file share tab navigate to the bottom of the controller file and add the following line:

global.HelloWorldTenantExtension = global.HelloWorldTenantExtension || {};

global.HelloWorldTenantExtension.Controller = {

createFileShare: createFileShare,

listFileSharesUrl: listFileSharesUrl,

getFileShares: getFileShares,

getfileSharesData: getfileSharesData,

navigateToListView: navigateToListView,

executeRunbook: executeRunbook

};

 

  • Open the HellworldTenant.fileSharesTab.js file and navigate to the updateContextualCommands function, add the following line:

Exp.UI.Commands.Contextual.add(new Exp.UI.Command("Execute", "Execute",Exp.UI.CommandIconDescriptor.getWellKnown("Start"), true, null,onExecuteRunbook));

 

  • In the same file add the following command handler:

function onExecuteRunbook() {

var subscriptionId =

    global.Exp.Rdfe.getSubscriptionsRegisteredToService("helloworld")[0].id,

name = "test",

number = 100,

stringArray = new Array("value1", "value2", "value3"),

date = new Date(),

sayGoodbye = true;

 

var promise = HelloWorldTenantExtension.Controller.executeRunbook(subscriptionId, name,

            number, stringArray, date, sayGoodbye);

global.waz.interaction.showProgress(

promise,

{

initialText: "Executing runbook...",

successText: "Runbook launched successfully.",

failureText: "Failed to execute runbook."

}

);

promise.done(function () {

});

}

 

Now compile the solution. As a result you will get a new version of the MSI setup for the HelloWorld sample. If you followed all instruction in Victor Arzate's blog you have already an installed and registered version of HelloWorld. Make sure to uninstall the existing version in control panel and install the new one but don't touch the extension registration. Login to the Azure Pack Tenant Site and navigate to the HelloWorld extension:

You see your new Execute button and can now launch the runbook.

 

Until next time! Torsten

Comments

  • Anonymous
    January 01, 2003
    Hi BrightBlade_z,
    The SMA runbook is called from the Tenant Site and not from the Admin Site. As a prerequisite make sure
    that the HelloWorld Sample is properly installed and registered following the steps described in the deployment post (from Victor Arzate). Make sure that the basic HelloWorld sample is running without problems on your machine. Launching the tenant site you should see the file shares view selecting the Hello World node.
    Where comes the HTTP error 404 from ? Did you find it in the logs ?
    Regards Torsten
  • Anonymous
    January 01, 2003
    If my description is not clear, I can email it to you? Including IIS logs and VS debug screenshot
  • Anonymous
    January 01, 2003
    404 in the VS debug, request "https://azureportal.contoso.com:30004/services/helloworld/fileservers" state is 404, but portal and sma service in the one vm and no reference the SMA web service the request state is 200.
  • Anonymous
    January 01, 2003
    Hi zhengyq - Could you provide more details on your question? Thanks!
  • Anonymous
    January 01, 2003
    I have a solution, SMA WEB SERIVICE and WAP PORTAL installed on a VM, SMA WORKROLE installed another VM, the problem solving.
  • Anonymous
    January 01, 2003
    Hi Brightblade_z,
    what test has failed ? What are the error symtoms ? I need a little bit more Information to understand your problems.
    Regards Torsten
  • Anonymous
    January 01, 2003
    but portal and spf service in the one VM is ok
  • Anonymous
    January 01, 2003
    hi,Torsten Kuhn thank. “step2” is Ok, add sma web service reference is ok, my spf service and azure pack portal in the two VM, installed "msi" is ok , open admin portal "hello world" label is the blank. debug request is 404. bug portal and spf service in the one VM is ok
  • Anonymous
    January 01, 2003
    Hi, you could add a Service reference to the SMA Web Service only by following "Step 2" and adding an empty GUID Parameter (see the screenshot) to the end of the SMA url. In my dev environment for example the URL is https: \theserver:9090�0000000-0000-0000-0000-000000000000 . If the empty GUID is missing you will get a HTTP error 404. Regards Torsten
  • Anonymous
    January 01, 2003
    Hi brightblade_z,
    could you send me your Solution to tillipopp@hotmail.de
    I will take a look at your sourcecode.
    Regards
    Torsten
  • Anonymous
    January 01, 2003
    Hi Venkat
    my "hello world" add a service reference to the SMA web service , do not modify any code , in the admin portal "fileserver" label found 404 error. users can not subscribe to plan, because the display is not activated. but delete SMA web service , is all right.
  • Anonymous
    January 01, 2003
    Test failed,Can anyone tell me why this is?
  • Anonymous
    January 01, 2003
    hi Torsten Kuhn
    if no reference to the SMA Web Service the “hello world” sample is ok, in the tenant site i can see the file shares view selecting the Hello World node, debug request state is 200. but reference the SMA Web Service , "hello world" node is blank, request state is 404.
  • Anonymous
    January 01, 2003
    portal and sma service in the two vm is bad can't see the list of shares, one vm is good.
  • Anonymous
    January 01, 2003
    HI Torsten Kuhn

    Thank you. But I suspect that this is not a code problem, "Hello World" project I haven't modified any code, only difference is the blog, my SMA Web Service and WAP portal on the two VM. But my VS is domain computer, and Configured certificates, and domain administrator is SPF admin. I can`t find the problem
  • Anonymous
    January 01, 2003
    hi Victor Arzate. my "Hello World" add a service reference to the SMA web service ,do not modify any code,Admin portal "FILESERVER" label found "https://azureportal.contoso.com:30004/services/helloworld/fileservers" 404 error. but delete SMA web service, all right.
  • Anonymous
    March 13, 2014
    Throughout this Success with Hybrid Cloud series, I’ve emphasized the importance of linking private and
  • Anonymous
    April 13, 2014
    hi,Why is my "Hello World" reveals no activated?
  • Anonymous
    April 15, 2014
    thanks Victor Arzate! my "Hello World" Add Service Reference SMA web service. WAP admin portal "hello world"
    “file Servers” and “products” label finds "https://azureportal.contoso.com:30004/services/helloworld/fileservers " 404 error. but delete SMA web service all right.
  • Anonymous
    April 15, 2014
    hi Victor Arzate thank you. My "Hello world" add service reference SMA Web Service , do not modify any code , but file servers label found "https://azureportal.contoso.com:30004/services/helloworld/fileservers" 404 error. delete SMA Web Service , all right
  • Anonymous
    April 19, 2014
    hi,my email is zhengyiqun@outlook.com. my messages are not approved
  • Anonymous
    April 20, 2014
    Hi victor thanks. My "Hello World" add a service reference to the SMA web service , do not modify any code . access to "fileserver" label get 404 error . delete the reference, you can access. Rerun the register script problem persists
  • Anonymous
    May 07, 2014
    Pingback from Windows Azure Pack???????????? | blchen?????????
  • Anonymous
    May 20, 2014
    Pingback from Success with Hybrid Cloud: Getting Deep – Windows Azure Pack | Dot Net RSS
  • Anonymous
    May 20, 2014
    Pingback from Success with Hybrid Cloud: Getting Deep – Windows Azure Pack | Dot Net RSS
  • Anonymous
    May 20, 2014
    Pingback from Success with Hybrid Cloud: Getting Deep – Windows Azure Pack | Dot Net RSS
  • Anonymous
    May 29, 2014
    hello, I did it as you wrote, but there are a few problems, adminsite have cloudSlider resource provier, but tenantsite no cloudSlider resource provider. can you help me?
  • Anonymous
    November 05, 2014
    I know I'm joining the party a little late, but did you ever find out what the problem was with the SMA service reference where it was not activating the service on the Admin portal side?
  • Anonymous
    December 12, 2014
    Hi Victor, after I implemented this "Hello World" into my environment, I was unable to add the service to plan. and the error message shown as below: do you have any suggestions or ideas why this happening?
    Error:
    HttpStatusCode: InternalServerError, ErrorCode: ErrorFromUnderlyingResourceProviders, ClientErrorMessage:One or more errors occurred while contacting the underlying resource providers. The operation may be partially completed.
    --->
    Microsoft.WindowsAzure.Server.Management.Core.ManagementServiceException: One or more errors occurred while contacting the underlying resource providers. The operation may be partially completed. Details: The request to the underlying service has timed out.
    at Microsoft.WindowsAzure.Server.Management.Core.QuotaManager.ThrowFanoutException(ResourceProviderException resourceProviderException)
    at Microsoft.WindowsAzure.Server.Management.Core.QuotaManager.d__fd.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
    at Microsoft.WindowsAzure.Server.AdminManagement.Service.Controllers.PlanServicesController.d__0.MoveNext()
    <---
    , operationName:, version:, accept language:, subscription Id:, client request Id:, principal Id:, page request Id:, server request id:
  • Anonymous
    December 30, 2014
    原文地址: http://blogs.technet.com/b/privatecloud/archive/2014/06/26/sample-portal-code-based-on-windows
  • Anonymous
    January 26, 2015
    "In a future blog I will explain the different options we have to give feedback from long running backend operations back to the WAP portal user" - Did you ever get around to writing anything about this?
  • Anonymous
    September 16, 2015
    The comment has been removed
  • Anonymous
    September 16, 2015
    The comment has been removed
  • Anonymous
    September 17, 2015
    The comment has been removed
  • Anonymous
    September 17, 2015
    Hi Shar,

    have you reviewed this blog post to help you troubleshoot the solution: http://blogs.technet.com/b/privatecloud/archive/2014/03/13/custom-resource-providers-in-windows-azure-pack-debugging-the-hello-world-sample.aspx

    Hope this helps,
    Victor
  • Anonymous
    September 22, 2015
    The comment has been removed
  • Anonymous
    September 30, 2015
    Hi Ann,

    Please see above in the comments section the contact details for Torsten Kuhn. Can you please send him an email so that we can follow up offline?

    Thanks,
    Victor
  • Anonymous
    October 08, 2015
    The comment has been removed
  • Anonymous
    October 14, 2015
    The comment has been removed
  • Anonymous
    October 14, 2015
    Hi Simon, please send an email to Torsten Kun (tillipopp@hotmail.de). He's the author of this blog.
  • Anonymous
    November 12, 2015
    The comment has been removed
  • Anonymous
    January 08, 2016
    Hi Victor ,
    I am completely new for azure pack. I installed Hello World program and want to do some modification.
    Just I want to pit a button on quickstart page and want to call some controller method on button pressing.
    Any Idea how to achieve this??

    Thanks.
    Chandan
  • Anonymous
    January 08, 2016
    Hi Victor and Thorsten

    Great and very helpful blog. Thanks for sharing !

    Matthias
  • Anonymous
    January 08, 2016
    Hi Victor and Thorsten

    Great and very helpful blog. Thanks for sharing !

    Matthias
  • Anonymous
    November 19, 2016
    I think this is one of the most important information for me.And i'm glad reading your article. But should remark on few general things, The website style is wonderful, the articles is really excellent :D. Good job, cheers