Using an Azure API App Service with a SharePoint Designer Workflow

Microsoft recently announced the Azure App Service (https://azure.microsoft.com/en-us/services/app-service/) as a new service in Azure that lets you create web and mobile apps for any platform. It could be advantageous to be able to use these services from SharePoint (especially API and Logic apps). You could potentially use third-party APIs from SharePoint using the Azure Marketplace. Check out this post for more information:https://blogs.msdn.com/b/visualstudio/archive/2015/03/24/introducing-the-azure-api-apps-tools-for-visual-studio-2013.aspx.

In this post I am going to show you what you need to do to invoke an API service via POST from a SharePoint Designer workflow.

Prerequisites:

Azure SDK 2.51 (available here: https://azure.microsoft.com/en-us/downloads/archive-net-downloads/)
Visual Studio 2013 Update 4
SharePoint Designer 2013

Create your Azure API app

Launch Visual Studio 2013 and create a new ASP.NET Web Application and name it HelloFromSPD:

image

Select Azure API App (Preview)

image

Add a Document Model and a Document Controller to your project. First, right click on the Model folder in your project and select “Add Class”. Your model class will have one property: a DocumentURL:

image

Next, add a Controller to your Controllers folder in your project named DocumentController. On the Add Scaffold screen, select “Web API 2 Controller with read/write actions”. In this example we will just write to an Azure queue. We will need to Install the WindowsAzure.Storage using the Package Manager in Visual Studio. From the Package Manager Console, type Install-Package WindowsAzure.Storage. 

image

For simplicity, I just added a QueueManager class into the DocumentController that wraps the Azure queue operations. You’d probably want a separate file if you were doing this in an actual implementation.

Here’s what your completed DocumentController should look like:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using HelloFromSPD.Models;
using Microsoft.WindowsAzure.Storage.Queue;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure;

namespace HelloFromSPD.Controllers
{
    public class DocumentController : ApiController
    {
        // GET: api/Document
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET: api/Document/5
        public string Get(int id)
        {
            return "value";
        }

        // POST: api/Document
        public object Post([FromBody]Document doc )
        {
            try
            {
                QueueManager qm = new QueueManager();
                qm.QueueDocument(doc.DocumentUrl);
                return Request.CreateResponse(HttpStatusCode.OK);
            }
            catch (Exception ex)
            {
                return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
            }
        }

        // PUT: api/Document/5
        public void Put(int id, [FromBody]string value)
        {
        }

        // DELETE: api/Document/5
        public void Delete(int id)
        {
        }
        class QueueManager
        {
         
            public void QueueDocument(string documentUrl)
            {
                CloudQueue queue = CreateQueue();
                CloudQueueMessage msg = new CloudQueueMessage(documentUrl);            
                queue.AddMessage(msg);
            }

            private CloudQueue CreateQueue()
            {
                // Retrieve storage account information from connection string.
                CloudStorageAccount storageAccount = CreateStorageAccountFromConnectionString(CloudConfigurationManager.GetSetting("StorageConnectionString"));

                // Create a queue client for interacting with the queue service
                CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();

                CloudQueue queue = queueClient.GetQueueReference("documentqueue");
                try
                {
                    queue.CreateIfNotExists();
                }
                catch (StorageException ex)
                {
                    Console.WriteLine(ex.Message);
                    Console.ReadLine();
                    throw;
                }

                return queue;
            }

            /// <summary>
            /// Validate the connection string information in app.config and throws an exception if it looks like
            /// the user hasn't updated this to valid values.
            /// </summary>
            /// <param name="storageConnectionString">The storage connection string</param>
            /// <returns>CloudStorageAccount object</returns>
            private static CloudStorageAccount CreateStorageAccountFromConnectionString(string storageConnectionString)
            {
                CloudStorageAccount storageAccount;
                try
                {
                    storageAccount = CloudStorageAccount.Parse(storageConnectionString);
                }
                catch (FormatException)
                {
                    Console.WriteLine("Invalid storage account information provided. Please confirm the AccountName and AccountKey are valid in the app.config file - then restart the sample.");
                    Console.ReadLine();
                    throw;
                }
                catch (ArgumentException)
                {
                    Console.WriteLine("Invalid storage account information provided. Please confirm the AccountName and AccountKey are valid in the app.config file - then restart the sample.");
                    Console.ReadLine();
                    throw;
                }

                return storageAccount;
            }
        }
    }
}

Add this line to the AppSettings file in your Web.config (modify to match your Azure Blob Storage account settings):

<add key="StorageConnectionString" value="AccountName=yourblobstoragename;AccountKey=yourblobaccountkey;DefaultEndpointsProtocol=https" />

Now build and Publish your Project to Azure:

image

Select Microsoft Azure API Apps(Preview) for the publish target

image

Sign-in to your Azure subscription. Create a new API App Name, App Service plan, etc.:

image

Wait for the Publish process to complete. This may take several minutes.

image

We now need to find the Url for our API App Service. Open up Server Explorer in Visual Studio and navigate to our new App Service:

image

Right click on HelloFromSPD and select Properties. Copy the URL. We will need it later:

image

To verify that your API app is working, use Fiddler (https://www.telerik.com/fiddler). If you go to the “Composer” tab, paste the URL to your API app and append “/api/Document”. Change the method to POST and add a line for Content-Type: application/json. Fiddler will add the Host and Content-length automatically. Finally, in the request body, add the following JSON: {"DocumentUrl":https://mysharepointsite.sharepoint.com/sites/devsite/Shared%20Documents/Microsoft_Press_eBook_Building_Cloud_Apps_with%20Microsoft_Azure_PDF.pdf”}

image

Click the Execute button. You should see a 200 HTTP response. If not, you can re-publish your API app with Debug and then attach the debugger.

image

Put some breakpoints in your code and then hit the Execute button again to step through your code.

Create your SharePoint Designer Workflow

At this point we are ready to work in SharePoint Designer. Launch SharePoint Designer and open your SharePoint site where you want to run the workflow. Click Workflows under the Site Objects and then click new List Workflow

image

Name the workflow AzureAPIWorkflow and click OK.

For the first step, add a dictionary by going under Action and selecting Build Dictionary:

image

Click on the “this” link and then click Add. For the name, use DocumentUrl, leave the Type as String and set the value to CurrentItem:Encoded Absolute URL. Create a new Variable named Document.

image

Add another dictionary by going under Action and selecting Build Dictionary. This time, type Content-Type for the name and set the value to application/json. Change the variable name to HttpDictionary.

Finally, add an action to call our Azure API service. Go to Action and select Call HTTP web service. Type in the URL to your Azure API service (make sure you include /api/Document in the URL) and specify HTTP POST for the HTTP method:

image

Click the request link after “With” and set it to Document. Right click on the URL to your Azure API service and set RequestHeaders to Variable:HttpDictionary:

image

In the Transition to stage, right click and select Go to a stage. Click stage and select “End of Workflow”. At this point, your completed workflow should look like this:

image

Click the Publish button.

Go to your SharePoint site and the Document library where you associated the workflow. Select a document in your library and then click Workflows in the ribbon.

image

Click the link to AzureAPIWorkflow to start your workflow.

Get the messages from the Azure Queue

We now need to create a simple Windows console application to consume messages from the Azure Queue. Create a new Windows console application in Visual Studio.

image

In the Package Manager Console, type Install-Package WindowsAzure.Storage.

Add this line to the AppSettings file in your app.config (modify to match your Azure Blob Storage account settings):

<add key="StorageConnectionString" value="AccountName=yourblobstoragename;AccountKey=yourblobaccountkey;DefaultEndpointsProtocol=https" />

In Program.cs, replace the code with the following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Queue;

namespace GetMessageFromAzureQueue
{
    class Program
    {
        static void Main(string[] args)
        {
             CloudStorageAccount storageAccount = CreateStorageAccountFromConnectionString(CloudConfigurationManager.GetSetting("StorageConnectionString"));

            // Create a queue client for interacting with the queue service
            CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();
            CloudQueue queue = queueClient.GetQueueReference("documentqueue");
           
            while (true)
            {
                CloudQueueMessage msg = queue.GetMessage();
                if (msg == null)
                    break;
                else
                {
                    Console.WriteLine("Got message from queue: " + msg.AsString);
                    queue.DeleteMessage(msg);
                }
            }
            Console.ReadKey();
        }
                

        /// <summary>
        /// Validate the connection string information in app.config and throws an exception if it looks like
        /// the user hasn't updated this to valid values.
        /// </summary>
        /// <param name="storageConnectionString">The storage connection string</param>
        /// <returns>CloudStorageAccount object</returns>
        private static CloudStorageAccount CreateStorageAccountFromConnectionString(string storageConnectionString)
        {
            CloudStorageAccount storageAccount;
            try
            {
                storageAccount = CloudStorageAccount.Parse(storageConnectionString);
            }
            catch (FormatException)
            {
                Console.WriteLine("Invalid storage account information provided. Please confirm the AccountName and AccountKey are valid in the app.config file - then restart the sample.");
                Console.ReadLine();
                throw;
            }
            catch (ArgumentException)
            {
                Console.WriteLine("Invalid storage account information provided. Please confirm the AccountName and AccountKey are valid in the app.config file - then restart the sample.");
                Console.ReadLine();
                throw;
            }

            return storageAccount;
        }        
    }
}

Build and run the console application:

image

Conclusion

In this post I’ve shown you how to invoke an Azure API service from a SharePoint Designer workflow. Notice that I didn’t consider security for the Azure API service so this is something that would need to be addressed later.