This post will show how to create a SharePoint 2013 app leveraging Windows Azure PaaS and the Windows Azure SDK 2.2. 

You can watch the video online of the presentation that I did about this solution at SharePoint Conference 2014, Building SharePoint Apps with Windows Azure Platform as a Service, at http://channel9.msdn.com/Events/SharePoint-Conference/2014/SPC385.

Background

I recently joined the Windows Azure Modern Apps Center of Excellence at Microsoft, and have been focused on Azure.  Since I have done so much work with SharePoint apps, what better way to show readers of my blog how to get started with Windows Azure Platform as a Service than to just create a provider-hosted app using the Windows Azure SDK 2.2. 

Credit to a colleague and friend of mine, Jason Vallery (an awesome Premier Field Engineer at Microsoft), he came up with the overall design of this app.  I took… umm… “inspiration” from his work, gotta give Jason credit and thanks.

This post will just show the highlights.  The code is attached to this post. 

The solution will use a remote event receiver to attach a remote event to a list named Photos in the host web.

image

I’ve already covered this in my post, Attaching Remote Event Receivers to Lists in the Host Web, so I’m not going into much detail here.

When the event receiver fires, we are going to capture the ListID, ListItemID, and WebURL from the ItemAdded event and store it in an Azure Table.

image

When the ItemAdded event receiver fires, we will use an Azure Storage Queue to send a message that will be picked up by a Console application.  The Console application will reach into the Photos list and download the item, make changes to the item, then store the item in Azure Blob Storage.

image

Get Office 365 and a Windows Azure Subscription

If you already have both an Office 365 subscription and a Windows Azure subscription, skip this section.

If you don’t have a Windows Azure subscription, there are multiple ways to obtain one to get started.  As an MSDN subscriber, you get up to $150 (US) credit for an Azure subscription every month.  Yes, you heard me… if you are an MSDN subscriber and you are not leveraging your Windows Azure benefit for MSDN subscribers, you are throwing $150 out the window.  Per month.

If you’re not an MSDN subscriber, you can also create a free trial subscription.  That will give you $200 credit for an Azure subscription to gain full access to Windows Azure for 30 days.

Some MSDN subscription levels also have a benefit for an Office 365 developer subscription for one year.  Check your MSDN benefits to see if you qualify.  If not, you can also create a free trial for Office 365.

Install the SDK

The first thing you need to do is to obtain the Windows Azure SDK.  At the time of this writing, the latest version is 2.2.  The SDK is available at http://www.windowsazure.com/en-us/develop/net, which also includes tutorials and documentation.  Look how easy they made it: just click the link to install the SDK (link is on the front page) and choose between Visual Studio 2012 or Visual Studio 2013.

image

Once installed, Visual Studio 2013 will have a new node in Server Explorer.

image

Right-click the Windows Azure node and choose Connect to Windows Azure.

image

You are prompted to sign into your subscription.

image

That’s it.

Create an Azure Web Site

I showed how to do this in a previous post, so I’m not going into much detail here.  Create a SharePoint 2013 app and publish it to an Azure web site.  Creating an Azure web site is crazy easy… just right-click the Web Sites node and choose Add New Site.

image

Give the new site a name and choose a location closest to you.  We’re not using a SQL Azure database for this post, we’ll look at that in a future post.

image

Next, right-click the new web site and choose Settings.  Turn up logging and click save. 

image

Oh yeah… that just happened.  Control web site logging from Visual Studio.  It still makes me tear up with joy each time I use it.

The rest of the details are covered in my post SharePoint 2013 app and publish it to an Azure web site, showing how to use Office 365 to register the app principal, obtaining the client ID and client secret for your app. 

Create a Storage Account

Go to the Windows Azure Management Portal and go to the Storage add-in.

image

At the bottom-left of the screen, choose the big “New” button.  Using the Quick Create option (that’s the only option currently), give your new account a name and location then click Create Storage Account.

image

Go back to Visual Studio 2013 and refresh the Storage node, you’ll see three new nodes:  Blobs, Queues, and Tables.

image

They’re empty… let’s do something about that.

References and Connection Strings

We’ll use the Item Added event receiver to write to Windows Azure tables.  To set this up, add a new class library project to your project that will serve as a data access layer.  Next, right-click the new class library project and choose Manage NuGet Packages.  Add the packaged for Windows Azure Storage.

image

Add the NuGet package to the web application project as well.

We need to update the configuration files with the connection string to your new storage account.  Right-click the new storage account in the Server Explorer pane and choose Properties.  In the Properties pane, click the elipses.

image

image

Copy the connection string and replace the text “YOUR_AZURE_STORAGE_CONNECTION_STRING” with your connection string value.

image

There is another project, PaaSDemoJobs, that contains an app.config file.  You need to replace the values there as well for the keys paasdemo, AzureJobsData, and AzureJobsRuntime.

image

The Data Library

The data library is really just a class library that provides some methods to make it easy to work with Azure tables, queues, and blobs.  The first class to point out is PhotoLocation, which derives from the TableEntity class.  This class can be used to serialize data into and out of an Azure table.

public class PhotoLocation : TableEntity
{

    public PhotoLocation() { }

    public PhotoLocation(string realm)
    {
        this.Realm = realm;
        this.PartitionKey = realm;
        this.RowKey = Guid.NewGuid().ToString();
    }
    public string WebURL { get; set; }
    public int ListItemId { get; set; }
    public Guid ListId { get; set; }
    public string Realm { get; set; }
    public string BlobUrl { get; set; }
}

The second class is StorageRepository, which will provide the methods to read and write to table storage. Our private helper method will let us obtain a reference to the table, and if it doesn’t already exist, creates it.

CloudStorageAccount _storageAccount;

public StorageRepository(string connectionString)
{
    _storageAccount = CloudStorageAccount.Parse(connectionString);
}

/// <summary>
/// Gets or creates the table reference
/// </summary>
/// <returns></returns>
private CloudTable GetLocationsTable()
{
    CloudTableClient client = _storageAccount.CreateCloudTableClient();
    CloudTable table = client.GetTableReference("locations");
    table.CreateIfNotExists();
    return table;
}

Next, we’ll add methods to save a single PhotoLocation and to retrieve a single PhotoLocation.  Inserting is easy, just use the InsertOrReplace method of the TableOperation class to create a TableOperation class, then tell the table to execute the operation.  Querying is just as easy… Create a new TableQuery class and use a Where operation, using a FilterCondition. 

public void SavePhotoLocation(PhotoLocation location)
{
    var table = GetLocationsTable();
    TableOperation operation = TableOperation.InsertOrReplace(location);
    TableResult result = table.Execute(operation);
}

public PhotoLocation GetLocationByID(string rowkey)
{
    PhotoLocation location = new PhotoLocation();
    var table = GetLocationsTable();
    TableQuery<PhotoLocation> query =
        new TableQuery<PhotoLocation>().Where(
        TableQuery.GenerateFilterCondition(
        "RowKey", QueryComparisons.Equal, rowkey));
    var results = table.ExecuteQuery(query);
    return results.FirstOrDefault();
}

I love it… expressive code that is clear and shows exactly what you want to do.  I think they did a great job designing the classes for the SDK.

If we want to return all records in the table based on a different value, such as any records where the Realm column matches the Realm ID of the current O365 tenancy, we can use the GenerateFilterCondition method again, just using the name of the column we want to use as a filter.

/// <summary>
/// Get all rows for an O365 tenant based on realm
/// </summary>
/// <param name="realm"></param>
/// <returns></returns>
public List<PhotoLocation> GetLocationsForTenant(string realm)
{
    List<PhotoLocation> locations = new List<PhotoLocation>();

    var table = GetLocationsTable();
    TableQuery<PhotoLocation> query = new TableQuery<PhotoLocation>().Where(
        TableQuery.GenerateFilterCondition("Realm", QueryComparisons.Equal, realm));

    var results = table.ExecuteQuery(query);

    if (results.Count() > 0)
    {
        locations = results.ToList();
    }

    return locations;
}

Our remote event receiver will then use our StorageRepository class to save a new record every time the ItemAdded remote event receiver fires.

private void HandleItemAdded(SPRemoteEventProperties properties)
{
    System.Diagnostics.Trace.WriteLine("Handle Item Added");

    string realm = TokenHelper.GetRealmFromTargetUrl(new Uri(properties.ItemEventProperties.WebUrl));
    var p = properties.ItemEventProperties;

    //Add code to save to storage here
    PhotoLocation location = new PhotoLocation(realm)
    {
        ListId = p.ListId,
        ListItemId = p.ListItemId,
        WebURL = p.WebUrl
    };

    string connectionString = CloudConfigurationManager.GetSetting("paasdemo");
    StorageRepository repo = new StorageRepository(connectionString);

    repo.SavePhotoLocation(location);

We can then use the same library from our HomeController class, using the controller to query the data to be displayed on the page.

public ActionResult Index()
{
    System.Diagnostics.Trace.WriteLine("In the Index action");
    var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
    string realm = TokenHelper.GetRealmFromTargetUrl(spContext.SPHostUrl);
    ViewBag.HostUrl = spContext.SPHostUrl;

            
    //Add code for viewing storage here
    string connectionString = CloudConfigurationManager.GetSetting("paasdemo");
    StorageRepository repo = new StorageRepository(connectionString);
    var model = repo.GetLocationsForTenant(realm);
            
    return View(model);
}

The controller is returning the model, so we need to update the View.  I’ve already done this in the same, I’m just showing you how I did this.  I deleted the Index.cshtml view that Visual Studio generates, then right-click the Home folder and choose Add/View.  Give it the name Index, change the template to List, and change the Model class to the PhotoLocation class.

image

I then updated the link to see Details for the item.

        <td>            
            @Html.ActionLink("Details", "Details", new {  id=item.RowKey  })             
        </td>

When clicked, we are routed to the Details action.  That action’s code is:

public ActionResult Details(string id)
{
    var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
    string realm = TokenHelper.GetRealmFromTargetUrl(spContext.SPHostUrl);
    ViewBag.HostUrl = spContext.SPHostUrl;

    //Add code for viewing storage here

    string connectionString = CloudConfigurationManager.GetSetting("paasdemo");
    StorageRepository repo = new StorageRepository(connectionString);

    var model = repo.GetLocationByID(id);

    return View(model);                      
}

Again, we right-click on the Home folder and generate a View to show the model data that is returned.

image

We update it’s view to show the picture from blob storage.

image

To test our work so far, we publish the web site to Azure.  This step includes going to AppRegNew.aspx to obtain a client ID and client secret for the app, downloading the publishing profile for the Azure web site, and publishing the web site to Azure.  More details can be found in my post, Creating a SharePoint 2013 App With Azure Web Sites, here are the highlights.

Download the publishing profile.

image

Provide the client ID and client secret obtained from SharePoint using the AppRegNew.aspx page.

image

Publish to Azure.

image

Package the app, changing the URL to https.

image

Copy the app package to your developer site and deploy it.

image

In the Trust It screen, give it permissions to a pre-existing Document Library named Photos.

image

Click the link to the deployed app, and you see the app in the Azure web site.

image

Click the Back to Site link to go back to SharePoint.  Upload a new picture to the library.

image

Click the link to the app, and see that the data was written to Azure table storage.

image

Go into Visual Studio and select the Storage node in Server Explorer.  There is a new node “locations”, which is the table that our code creates.

image

Double-click the table, and we can see the data in table storage.

image

Queues and Blobs

Our solution simply writes data to table storage so far.  When an item is added, we want to queue up a new message.  Add methods to the StorageRepository class to queue a message and to write to blob storage.

public void SendPhotoLocationQueueMessage(PhotoLocation p)
{
    CloudQueueClient client = _storageAccount.CreateCloudQueueClient();
    CloudQueue queue = client.GetQueueReference("jobs");
    queue.CreateIfNotExists();
    string message = "\"" + p.PartitionKey + ":" + p.RowKey + "\"";
    queue.AddMessage(new CloudQueueMessage(message));
}

public void SaveImageBlob(PhotoLocation p, Stream s)
{
    CloudBlobClient blobClient = _storageAccount.CreateCloudBlobClient();
    CloudBlobContainer container = blobClient.GetContainerReference("images");
    container.CreateIfNotExists();

    //Set public access permissions on the container
    var permissions = container.GetPermissions();
    permissions.PublicAccess = BlobContainerPublicAccessType.Container;
    container.SetPermissions(permissions);

    //Upload the blob
    CloudBlockBlob blockBlob = container.GetBlockBlobReference(p.RowKey + ".png");
    blockBlob.UploadFromStream(s);

    //Set content-type header
    blockBlob.Properties.ContentType = "image/png";
    blockBlob.SetProperties();

    //Update the table storage object
    p.BlobUrl = blockBlob.Uri.ToString();
    SavePhotoLocation(p);


}

Update the HandleItemAdding method in the remote event receiver to queue a new message when an item is added.  The binaries have changed, publish again to the Azure web site.

image

Go back to SharePoint, add another picture to the Photos library.  Now go to Visual Studio, open the Queues node, and we see a new queue named “jobs”. 

image

Double-click the jobs queue, and see a new message has been queued.

image

Web Jobs

Instead of creating a cloud service to process the queue message, we will use a web job.  Scott Hanselman does a fantastic job of introducing web jobs in his post Introducing Windows Azure WebJobs. We create a Console application (yes, a CONSOLE application, like we all learned to code .NET with!) 

In order for this to work, you will have to use an app-only context because there will not be a current user context.  This means the app must request app-only permissions.  For more information, see my post Building a SharePoint App as a Timer Job.  

Update the app.config for the PaaSDemoJob project with your client ID and client secret, and make sure you updated the AzureJobsData and AzureJobsRuntime and paasdemo configurations with your storage account connection string as instructed previously.

image

To use web jobs, we need to add a NuGet package.  Web Jobs are still in preview, so you’ll need to include pre-release in the NuGet manager.

image

Pretend that we have an existing Console application that runs using Windows Scheduler, and we’d like to move this to the cloud as well.  We can made a few modifications to our code.  Update the Main entry point for the console application.

        static void Main(string[] args)
        {
            //Add JobHost code here
            JobHost host = new JobHost();
            host.RunAndBlock();

        }

Next, update the parameter for the ProcessQueueMessage method with an attribute from the Web Jobs SDK to process queue messages from a queue named “jobs”.

image

Our code is going to reach back into SharePoint using the Client Side Object Model (CSOM).  We need all the TokenHelper stuff to make that happen.  Go back to NuGet Package Manager and add the App for SharePoint Web Toolkit.

image

This will add the necessary references and will make sure the references are marked as CopyLocal=true, because we will push the entire console application to Azure in just a bit.

The remaining code will reach back into SharePoint using CSOM, get the image from SharePoint, and overlay some text “#SPC385” as well as the SharePoint Conference logo (obtained as a resource file).

Add a reference to our PaaSDemoData library, which will also require adding the NuGet package to Windows Azure Storage.

Run the Console application locally (right-click and choose Debug / Start New Instance for the console application.  You will see that the Web Jobs SDK picks up the queue message, processes it.

image

Go to Visual Studio 2013, and we see a new blob container named “images”.

image

Double-click the images container, and we see the new image!

image

Let’s go back to the app in SharePoint.  We see the image now appears in the Index view.

image

Click Details, and we see the image was processed, overlaying the hashtag “#SPC385” and the SharePoint Conference logo.

image

Go to the bin directory for the PaaSDemoJob console application and zip its contents.

image

Go to the Azure Management Portal, go to your web site, and go to the Web Jobs tab.

image

Upload the zip file.

image

The web job will use the web.config settings from our web application to connect to storage, so it’s important that you followed directions previously and updated the AzureJobsRuntime and AzureJobsData connection strings with the connection string for your storage account.

Go to the web site dashboard and reset your deployment credentials.

image

Now go back to the Web Jobs tab and click the link next to the job. 

image

Enter your credentials, and you can now see the status of any jobs.

image

Add a photo to the SharePoint document library, and you’ll then see the job picks it up and processes it!

image

image

As a parting gift to the attendees at the session at SharePoint Conference, here is the group photo I took at the beginning of the session as part of the demo.

You can watch the video online of the presentation from SharePoint Conference 2014, Building SharePoint Apps with Windows Azure Platform as a Service, at http://channel9.msdn.com/Events/SharePoint-Conference/2014/SPC385.

For More Information

MSDN subscription benefits

Office 365 Free Trial

Windows Azure Free Trial

Creating a SharePoint 2013 App With Azure Web Sites

Attaching Remote Event Receivers to Lists in the Host Web

Building a SharePoint App as a Timer Job

Introducing Windows Azure WebJobs