0

Add New Connector to Nintex Workflow Cloud (Part 4 of 4)

Overview

This article is part 4 of a 4 part series, where I’ll cover the steps to import your Azure Function as a new connector using the Nintex Workflow Cloud Xtensions framework.  If you haven’t already reviewed the prior posts in this series, here’s what you missed:

My sample project here was to build an Azure Function that wrapped the GSA Per Diem API, making it easy to consume as an Xtension in Nintex Workflow Cloud.  The use case for this would be workflow designers creating a solution for Travel Authorization Requests and/or Expense Reports.  This custom workflow action could validate that the amount of expenses for Meals and Hotels is within the limit allowed by the GSA.

Now that we’ve created our Azure Function, we’re left with the best part of the solution – importing it into Nintex Workflow Cloud as a new new Xtension / Connector.

Create the Xtension in NWC

After logging into your NWC tenant, click on the Xtensions link on the left side of your Dashboard.  The click the orange plus (+) sign on the right side.

image

This will display the Connector Definition dialog.  Simple copy and paste the API Definition URL into the textbox for the OpenAPI specification URL (see bottom of part 2 for how to obtain this URL in Azure Portal).  After pasting the URL, NWC will immediately validate the definition and display a green checkmark on the right.  Then click Next.

image

This will display the Security dialog.  We can accept the default settings here.  These settings indicate that Nintex Xtensions will use an APIKey for security to call our Azure Function (the APIKEY is the token at the end of the API Definition URL) and it will pass the APIKEY value as a parameter named “code” in the URL querystring.  The APIKEY shall be entered by the workflow designer when they use the GSA Per Diem action within a workflow.  Click Next to continue.

image

This will display the Publish dialog as shown below.  Enter a Name and Description for the Connector.

image

Then click on one of the available icons or if you prefer you can upload your own icon.  Then click the Publish button.

image

 

When the Publishing is complete your new Connector will show up in the custom connectors list:

image

Before using the Connector, we’ll need to create a Connection to it.  While you can create the connection after you add the GSA Per Diem action into your workflow.  But in this case well create a new connection first.  Click on the Connections link from the left side of the Dashboard, then click the Add New button:

image

From the Add a new connection screen the name of your new Connector from the Connector list.  In this case, I’m selecting the GSA Per Diem connector.  Then click the Connect button:

image

Enter values for the Connection name and the API Key that you plan to use name for this specific connection, then click the Connect button:

image

The new connection shall be displayed in the connection list:

image

Now, we can actually use our custom Connector in an NWC workflow!  So, lets create a new Workflow and when we review the toolbox, we’ll see our new GSA Per Diem connector. If we expand the GSA Per Diem section, we’ll see the specific action that corresponds to our Azure Function.  Note that the name displayed for this action is defined in our Swagger definition, as the summary.  So if we prefer a different name for the action, we can change that summary value (see part 3 for details).    image

In this case, I created a workflow using the Nintex Public Web Form as the Start Event, with two start variables: ZipCode and TripDate.  I then added my new action GSA Per Diem action onto the workflow design surface.  Lets look at the configuration:

image

For this action there are 6 settings to be provided:

  • Connection – Select the name of the connection that was created previously from the drop down list
  • The API Key – You may use the same API KEY that was provided in the API Definition URL.  Azure Functions also allows you to create and manage additional API KEY for your function app.  This can be done from the Settings screen of your Azure Function App.
  • Gsa Per Diem Input Zip – assign this to the ZipCode start event variable.
  • Gsa Per Diem Input Trip Date – assign this to the TripDate start event variable
  • Results Hotel – Define a new variable of type integer called HotelPerDiem.  This is an output variable that will be populated by this action.
  • Results Meals – Define a new variable of type integer called MealsPerDiem. This is an output variable that will be populated by this action.

Conclusion

So we have no concluded our project to implement a solution using C# & Visual Studio to develop our logic within Azure Functions, and import our API definition as a custom connector for Nintex Workflow Cloud.

The expected scenario with this example, is that we may capture actual Meal and / or Hotel expenses and compare those against the allowed Per Diems.  If the actual expenses exceeds the Per Diem the workflow may taken certain actions like automatically reject or route for special approval.  But you can hopefully adapt the concepts here for any custom business logic or integration scenarios that you can imagine.  Good luck!

If you have questions or comments please contact me on Twitter @TomCastiglia

0

Configuring Swagger Definition for your Azure Functions (Part 3 of 4)

Overview

This article is part 3 of a 4 part series, where I’ll cover editing of the Swagger definition file that we’ll import into NWC in part four in order to create the actual Nintex Xtension.  In part 1, I covered the steps to setup your Azure Functions and Visual Studio environment, and in part 2 I covered the C# code that was developed and published to Azure Functions.

In part 4, we’ll import our Azure Function into Nintex Workflow Cloud as a new Connector using NWC’s Xtensions framework.

Configure Methods

Before creating our API definition, we’ll need to configure the allowed HTTP methods for our new function, so that it only allows POST methods.  First, navigate to your new Function App in the Azure portal, expand the Functions section, then expand the specific function and click on the Integrate option in the portal, as shown below:

image

From the Integrate screen, review the Trigger configuration and find the Allowed HTTP methods drop down field, which may be defaulted to a value of All methods.  Change it to Selected Methods instead and then uncheck all of the HTTP methods except for POST, as shown below:

image

Now that the allowed methods are set, we can create our Swagger API Definition.

Swagger Definition

Creating a Swagger Definition was a new experience for me and there are various tools out there that can help with the process.  One tool is built into the Azure Functions portal site, and that is primarily where I created my definition.  But you can also use the native editor at http://editor.swagger.io/.  Although both editors are very similar, there were a few things that I found easier to figure out on the Swagger site than in the Azure Portal, so I used both of them.  But we’ll start in the Azure Portal.

Regardless of the editor that you use, you create your swagger definition using a language call YAML, which stands for “YAML Ain’t Markup Language”.  Instead its described as a “a human friendly data serialization standard for all programming languages”.  The output of a Swagger definition is JSON, but you edit with YAML.

To generate the API definition for your Azure Function:

Navigate to your Function App in Azure Portal, and click on the API Definition link.

image

This will display the Function API definition (Swagger) page.  For API definition source, click on the Function (preview) button.

image

This will display the Swagger Editor, with a blank definition, as shown below:

image

Click on the button for Generate API definition template.image

This will generate a boilerplate API definition based on the metadata about your function, as shown below.  As you edit the swagger definition, it will validate your changes and provide error details in real time.

image

Here is what the completed API Definition looks like for my GsaPerDiemFunctions app:

image

To review the generated Swagger JSON, click the Copy button next to the API Definition URL:

image

Then test it in your browser, or better yet a REST client tool like Postman.  The resulting JSON should look like this:

{
    "swagger": "2.0",
    "info": {
        "title": "gsaperdiemfunctions.azurewebsites.net",
        "version": "1.0.0"
    },
    "schemes": [
        "https"
    ],
    "host": "gsaperdiemfunctions.azurewebsites.net",
    "basePath": "/api",
    "paths": {
        "/GetPerDiemByZip?code={APIKEY}": {
            "post": {
                "tags": [
                    "Looks up GSA Per Diems for Hotel and Meals by Date and Zipcode."
                ],
                "summary": "Looks up GSA Per Diems for  Hotel and Meals by Date and Zipcode.",
                "operationId": "GetPerDiemByZip",
                "x-ntx-summary": "Looks up GSA Per Diems for Hotel and Meals by Date and Zipcode.",
                "parameters": [
                    {
                        "name": "APIKEY",
                        "in": "path",
                        "description": "The API Key",
                        "x-ntx-summary": "The API Key",
                        "required": true,
                        "type": "string"
                    },
                    {
                        "name": "GsaPerDiemInput",
                        "in": "body",
                        "description": "Contains Zipcode and Tripdate.",
                        "x-ntx-summary": "Contains Zipcode and Tripdate.",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/GsaPerDiemInput"
                        }
                    }
                ],
                "produces": [
                    "application/json"
                ],
                "consumes": [
                    "application/json"
                ],
                "responses": {
                    "200": {
                        "description": "Returns Per Diem Amounts for Hotel and Meals by Zip and TripDate",
                        "schema": {
                            "$ref": "#/definitions/GsaPerDiemOutput"
                        }
                    }
                },
                "security": [
                    {
                        "apikeyQuery": []
                    }
                ]
            }
        }
    },
    "securityDefinitions": {
        "apikeyQuery": {
            "type": "apiKey",
            "name": "code",
            "in": "query"
        }
    },
    "definitions": {
        "GsaPerDiemInput": {
            "description": "Zipcode and tripdate for the trip",
            "properties": {
                "Zip": {
                    "type": "string",
                    "description": "Zipcode for main destination of the trip"
                },
                "TripDate": {
                    "type": "string",
                    "description": "Date of the first day of the trip"
                }
            },
            "required": [
                "Zip",
                "TripDate"
            ]
        },
        "GsaPerDiemOutput": {
            "description": "Max Per Diem amounts for Meals and Hotel",
            "properties": {
                "Hotel": {
                    "type": "integer",
                    "description": "Per Diem amount allowed for Hotel"
                },
                "Meals": {
                    "type": "integer",
                    "description": "Per Diem amount allowed for Meals"
                }
            },
            "required": [
                "Hotel",
                "Meals"
            ]
        }
    }
}
0

C# code for calling GSA Per Diem API from Azure Functions (Part 2 of 4)

Overview

This article is part 2 of a 4 part series, where we’ll cover the C# code for our custom connector for Nintex Xtensions and NWC.

In part 1, I covered the project requirements and how to setup Azure and Visual Studio 2017 with Tools for Azure Functions.  The goal of this project was to allow a workflow designer in NWC to query the GSA Per Diem API to lookup the allowed Per Diem amounts for Hotel and Meals based on the travel date and location of a trip.

Below I’ll cover the GSA Per Diem API and the C# code for a wrapper function to make this API very easy to consume from Nintex Workflow Cloud.

Development

The GSA’s Per Diem API, is a RESTful service with a very simple interface.  The API URL is: https://inventory.data.gov/api/action/datastore_search?resource_id=8ea44bc4-22ba-4386-b84c-1494ab28964b

This URL allows one pass in an additional “filter” parameter with a JSON formatted string containing the parameter values that the API shall use to return the desired results.  The API allows four types of filters and they each require two arguments, with FiscalYear as one of the arguments and one of the following as the other argument:

  • Zip
  • County
  • DestinationID
  • State

For my initial project, I used Zip.  So to call this API, you’ll need to format a REST call as follows:

https://inventory.data.gov/api/action/datastore_search?resource_id=8ea44bc4-22ba-4386-b84c-1494ab28964b&filters={“FiscalYear”:”2017″,”Zip”:”10036″}

The response from this REST API is a fairly lengthy JSON message containing a lot of data.  However, my NWC connector only needs to receive two values (MealsPerDiem & HotelPerDiem).  As noted above, the Azure Function I created was a fairly simple wrapper around this REST API, that enabled the following:

  1. Allow API to be called as an HTTP POST operation
  2. Accept an input value from Nintex Workflow Cloud of a specific date, rather than the FiscalYear.
  3. Return only the two value that we need: MealsPerDiem & HotelPerDiem

So here is the C# code that I used (Since the code is pretty well commented, I’m not providing additional explanations):

using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Text;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Newtonsoft.Json;

namespace GsaPerDiemFunction
{
    ///
<summary>
    /// This class implements behavior for GetPerDiemByZip function in Azure
    /// </summary>

    public static class GetPerDiemByZip
    {
        //Constants
        private const string GSA_REST_URL = "https://inventory.data.gov/api/action/datastore_search";
        private const string RESOURCEID = "?resource_id=8ea44bc4-22ba-4386-b84c-1494ab28964b";

        ///
<summary>
        /// The PerDiemInput class is used to deserialize JSON input data from the HTTP POST
        /// Zip - Zip code of where the travel occured
        /// TripDate - Date that the specifc travel occured on
        /// </summary>

        public class PerDiemInput
        {
            public string Zip { get; set; }
            public string TripDate { get; set; }
        }

        ///
<summary>
        /// The PerDiemOutput class is returned as serialized JSON with the requested values from the GSA Per Diem API
        /// Hotels - Per Diem amount allowed by GSA for Hotel expense on specified date and zip code
        /// Meals - Per Diem amount allowed by GSA for Meals on specified date and zip code
        /// </summary>

        public class PerDiemOutput
        {
            public int Hotel { get; set; }
            public int Meals { get; set; }
        }

        ///
<summary>
        /// This method implements the  core logic for our Azure Function
        /// </summary>

        /// <param name="req">Incoming HTTP Request data as a POST operation</param>
        /// <param name="log">Log provided by Azure functions for debug purposes.  Logged data is visible by admins in the Azure portal.</param>
        /// <returns></returns>
        [FunctionName("GetPerDiemByZip")]
        public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
        {
            try
            {
                log.Info("Received Per Diem Request.");

                //Deserialize the incoming JSON data into an instance of the PerDiemInput
                string jsonInput = await req.Content.ReadAsStringAsync();
                log.Info(jsonInput);
                var perDiemInput = JsonConvert.DeserializeObject<PerDiemInput>(jsonInput);

                //Cast incoming values and check that the inputs are valid
                string zip = perDiemInput.Zip;
                string tripDate = perDiemInput.TripDate;
                string fiscalYear;
                int monthNum;
                string month;
                DateTime travelDate;
                int zipInt;

                //If TripDate is not valid, return BadRequest response, otherwise parse the date into year and month values
                if (!DateTime.TryParse(tripDate, out travelDate))
                {
                    string tripDateInvalidMsg = string.Format("The specified TripDate ({0}) is not valid.", tripDate);
                    log.Info(tripDateInvalidMsg);
                    return req.CreateResponse(HttpStatusCode.BadRequest, tripDateInvalidMsg);
                }
                else
                {
                    fiscalYear = travelDate.Year.ToString();
                    monthNum = travelDate.Month;
                    DateTime monthDate = new DateTime(1, monthNum, 1);

                    month = monthDate.ToString("MMM");
                }

                //If Zip is not valid, return BadRequest response
                if ((zip.Length != 5) || (!int.TryParse(zip, out zipInt)))
                {
                    string zipInvalidMsg = string.Format("The specified Zip ({0}) is not valid.", zip);
                    log.Info(zipInvalidMsg);
                    return req.CreateResponse(HttpStatusCode.BadRequest, zipInvalidMsg);
                }

                log.Info(string.Format("Per Diem Requested for Zip: {0} and Fiscal Year: {1}", zip, fiscalYear));

                //Get per diem data in json format from GSA REST services, based on the specified year and zip code
                //Deserialize the per diem json into an object
                string jsonResponse = GetPerDiemJson(zip, fiscalYear, log);
                var gsaPerDiem = JsonConvert.DeserializeObject<Rootobject>(jsonResponse);
                log.Info("Deserialized jsonResonse");

                //Create the output object that we want to return.  This is a simplied version of what the GSA provides,
                //returning only the max per diem allowed for meals and hotel based on the fiscal year and zip.
                PerDiemOutput perDiemOut = new PerDiemOutput();
                perDiemOut.Meals = int.Parse(gsaPerDiem.result.records[0].Meals);
                perDiemOut.Hotel = GetHotelPerDiemByMonth(ref gsaPerDiem, month);

                return req.CreateResponse(HttpStatusCode.OK, perDiemOut);
            }
            catch (Exception e)
            {
                log.Info(string.Format("ERROR:\n\r Message: {0}\r\n Source: {1}\r\n Stack: {2}\r\n TargetSite: {3}", e.Message, e.Source, e.StackTrace, e.TargetSite));
                return req.CreateResponse(HttpStatusCode.BadRequest, e.Message);
            }
        }

        ///
<summary>
        /// Formats input filters into JSON and Calls the GSA Per Diem REST API
        /// </summary>

        /// <param name="zip">Zip code specified by the caller</param>
        /// <param name="fiscalYear">Fiscal Year specified by the caller</param>
        /// <param name="log">Log provided by Azure functions for debug purposes.  Logged data is visible by admins in the Azure portal.</param>
        /// <returns></returns>
        private static string GetPerDiemJson(string zip, string fiscalYear, TraceWriter log)
        {
            HttpWebResponse response = null;
            Stream receiveStream = null;
            StreamReader streamReader = null;
            try
            {
                //format filter parameter as JSON
                string filters = string.Format("&filters={{\"FiscalYear\":\"{0}\",\"Zip\":\"{1}\"}}", fiscalYear, zip);
                string resourceUrl = string.Format("{0}{1}{2}", GSA_REST_URL, RESOURCEID, filters);

                //Create request to GSA API
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(resourceUrl);

                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
                                        | SecurityProtocolType.Tls11
                                        | SecurityProtocolType.Tls12
                                        | SecurityProtocolType.Ssl3;

                log.Info(string.Format("Created Request for: {0}", resourceUrl));

                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";

                log.Info("Request Host: " + request.Host);

                response = (HttpWebResponse)request.GetResponse();
                log.Info("Received Response from Request");

                receiveStream = response.GetResponseStream();
                streamReader = new StreamReader(receiveStream, Encoding.UTF8);
                string jsonResponse = streamReader.ReadToEnd();

                response.Close();
                streamReader.Close();
                return jsonResponse;
            }
            catch (Exception e)
            {
                throw;
            }
            finally
            {
                if (response != null)
                {
                    response.Close();
                }
                if (streamReader != null)
                {
                    streamReader.Close();
                }
            }
        }

        private static int GetHotelPerDiemByMonth(ref Rootobject gsaPerDiem, string month)
        {
            int perDiem = 0;

            switch(month.ToLower())
            {
                case "jan":
                    perDiem = int.Parse(gsaPerDiem.result.records[0].Jan);
                    break;
                case "feb":
                    perDiem = int.Parse(gsaPerDiem.result.records[0].Feb);
                    break;
                case "mar":
                    perDiem = int.Parse(gsaPerDiem.result.records[0].Mar);
                    break;
                case "apr":
                    perDiem = int.Parse(gsaPerDiem.result.records[0].Apr);
                    break;
                case "may":
                    perDiem = int.Parse(gsaPerDiem.result.records[0].May);
                    break;
                case "jun":
                    perDiem = int.Parse(gsaPerDiem.result.records[0].Jun);
                    break;
                case "jul":
                    perDiem = int.Parse(gsaPerDiem.result.records[0].Jul);
                    break;
                case "aug":
                    perDiem = int.Parse(gsaPerDiem.result.records[0].Aug);
                    break;
                case "sep":
                    perDiem = int.Parse(gsaPerDiem.result.records[0].Sep);
                    break;
                case "oct":
                    perDiem = int.Parse(gsaPerDiem.result.records[0].Oct);
                    break;
                case "nov":
                    perDiem = int.Parse(gsaPerDiem.result.records[0].Nov);
                    break;
                case "dec":
                    perDiem = int.Parse(gsaPerDiem.result.records[0].Dec);
                    break;
            }

            return perDiem;
        }
    }
}

Once the code is complete, you can publish again to Azure.  I should also mention that there are a few ways to test and debug your Azure Function code, both locally (without publishing to Azure) or from Azure Functions.  Many REST API developers use Postman, which is what I did as well.

Before you can consume your Azure Function as a Connector for Nintex Workflow Cloud, you’ll need to define the Swagger definition file, which I’ll cover in part 3.

0

Using Azure Functions to create Xtensions for Nintex Workflow Cloud (Part 1 of 4)

Introduction

Many people are familiar with Nintex as a workflow solution for SharePoint and Office 365, but last year Nintex released their Workflow Cloud offering (NWC), which has no dependency on SharePoint or Office 365. NWC comes with dozens of native workflow actions connecting to many different external cloud services, such as Salesforce, Box, Dropbox, Google, SharePoint, and many others.  Nintex frequently updates NWC with new connectors/actions for additional cloud services.  And more importantly, Nintex recently added an extensibility framework with a published SDK to allow developers to create their own custom NWC connectors.

This new extensibility model is branded as Nintex Xtensions, and leverages the OpenAPI specification, (aka Swagger), which is considered the most popular open source framework for creating RESTful APIs.  While Nintex Xtensions is well documented in their SDK & Help files, the documentation assumes that you (the developer) are familiar with how to write the code for for your custom connector as well as create the necessary Swagger definition file, which you need to import into NWC in order to configure your connector.

I decided the easiest approach for me to create a custom Xtension for NWC was to use Azure Functions, which provides a scalable, “serverless”, compute-on-demand platform to host and execute the code.  Additionally, Azure Functions includes tooling to generate the Swagger definition file for my code.  And you can develop and test Azure Functions for free.  If your Azure Function is used in production, you’ll end up paying for the actual compute time.  But Azure provides a limited amount of compute time per month at no cost, so for testing or a Proof of Concept, it would likely cost you $0/month.

Typically, when developing in Azure Functions, the development is done in the browser, within the Azure Portal, and you can choose between various languages (Javascript, C#, F#, etc.).  However, if you prefer to develop using Visual Studio, that’s possible as well using Visual Studio 2017 Tools for Azure Functions, which is currently available as a preview.  That’s the approach I used.

Since, there is a lot of content for this article, I decided to break it down into four separate posts:

Requirements

For my first NWC Xtension, I wanted to select a project where the coding logic would be routine, since I was learning a few new technologies (Azure Functions & the NWC Xtensions Framework), but also a project that would have some real-world value.

Having implemented various projects in the past for clients to process Travel Authorization Requests and Expense Reports, I figured it would be useful to have an Xtension to calculate the allowed per diem expense amounts for hotels and meals.  The U.S. GSA website allows government employees and contractors to lookup the max per diem amounts that can be reimbursed for hotels and meals.  The GSA also provides a REST based Per Diem API that can be queried by year and location (Zip, County or City & State).

This API returns a JSON message, containing the maximum per diem amount for meal and hotel expenses.  The response contains a single value for the meal per diem and 12 values for hotel per diem (one value for each month, as the hotel per diem changes based on peak months when hotels tend to be more expensive).

To simplify the requirements for my Xtension, I decided it would accept two inputs (Zipcode and TravelDate) and return two values (HotelPerDiem and MealsPerDiem).  I then used Visual Studio 2017 to create my Azure Function as a simple wrapper around the GSA Per Diem API.

Disclaimer

I assume you’re reading this article due to your interest in Azure Functions and/or Xtensions for Nintex Workflow Cloud, not for GSA Per Diem rules.  I expect that anyone reading this will adapt the concepts to whatever business logic or integration requirements that you may have.  Although I’ve provided code samples for the GSA Per Diem integration, that’s only to ensure completeness of the content.

Setup

If you haven’t used Azure Functions before, before starting in Visual Studio I suggest that you Create your first function in the Azure portal to understand the basics of Azure Functions.  Ultimately, you may decide you prefer to develop in the portal rather than in Visual Studio.  But the rest of this article is based on using Visual Studio.

The following steps are needed before you can really begin:

  1. Make sure you have an Azure subscription.  If not, you’ll need to create a free account before you begin.
  2. Download and install the Visual Studio 2017 Tools for Azure Functions, including:
  1. Once your Visual Studio environment is configured per instructions above, you can create your first Azure Functions project in Visual Studio.  From the New Project dialog, select Visual C# as your language and the Azure Functions project template, as shown below:

image

  1. Provide a project and solution name for your new function, select your desired folder location, and click the OK button.  This creates the Visual Studio project which represents a single Azure Function “App”.  We can then add one or more individual Azure Functions to this App / Project.
  2. Once the new Project is created, we’ll need to add an Azure Function to it.  From the Solution Explorer, right click on the project name and select Add> New Item.  This shall display the Add New Item dialog.  Select Azure Function, provide a name for the new class, and click Add, as shown below:

image

  1. You’ll then be prompted to specify the type of trigger that this function should use.  Since NWC will call our Xtension over HTTP, you’ll need to select the HttpTrigger.  At this point, you can select the AccessRights (I used “Function”), and specify the FunctionName.  I suggest using the same name as was used to create the C# class in the prior step.  This will create a new C# so you can start developing your code.  Note that at this point, everything has been done locally.  Later, we’ll Publish our new Function App and any specific Functions to Azure.image

Publish to Azure Functions

Now that our Visual Studio project is created, we can publish the project which will create the new Function App with an empty function in Azure Functions.  From the Visual Studio Solution Explorer, right click on your project name and click the Publish option.  This will display the Publishing screen with options to create a new Function App or select an existing one:image

Click the Publish button, which will display the Create App Service dialog, as shown below:

image

Provide your preferred App Name and select the Subscription, Resource Group, Service Plan and Storage Account.  Then click the Create button, which will provision your new Function App in Azure.  You can then login to your Azure portal to inspect your new Function App and function:

image

As we start and continue to write the code for our function, we can publish our changes as often as needed to Azure.  Now that our Azure Functions project is setup in Visual Studio, we can start on our C#, which I’ll cover in part 2.

0

Welcome to Insights on Workflow and ECM…

So, this is my first Blog post in over 2 years, when I sold my share in Hershey Technologies to Konica Minolta.  I never had a personal Blog before as I had simply used the HersheyTech website for my personal blogging.  Since our acquisition by KM, I simply hadn’t gotten around to launching my own Blog.

As a software consultant/architect with SharePoint and Office 365, I’ve worked on many projects covering many aspects of these products (collaboration, upgrades, migrations, intranets/portals, ECM, workflow, custom development, etc.).

This Blog will cover all of these topics and more.  But as the site name says, my focus here will be mainly related to Workflow (especially Nintex and Microsoft Flow) and Enterprise Content Management (with a focus on leveraging native ECM-related features in SharePoint and Office 365).