Getting Started with building Azure WebJobs ("Timer Jobs") for your Office 365 sites
One of my new Office 365 projects is all about migrating farm-solutions to a more Office 365 compliant format. This includes converting a lot of functionality to SharePoint Apps and other cloud-friendly formats. One of the things we’ve got plenty of is Timer Jobs.
In this post I’ll talk about how you can build an Azure WebJob to act as a scheduled job for your Office 365 (or on-prem, should you like) SharePoint installation. With Office 365, if you’re running SharePoint, you’ll need to re-think the way you run the things that used to be timer jobs in your traditional Farm-solutions. Let’s check it out.
Introduction to Azure WebJob as a Timer Job for your Office 365 sites
In traditional SharePoint development we have Timer Jobs, which performs scheduled tasks in your SharePoint farms. A commonly used technique is to develop custom timer jobs in order to continuously or iteratively perform certain tasks in your environment.
With Office 365 and SharePoint Online, you don’t have the luxury to deploy your farm solutions, which is where your traditional timer jobs normally live. Instead, we have to find another way to schedule our tasks – this brings us to the concept of an Azure WebJob.
Steps for building the WebJob using Visual Studio 2015 (Preview)
In order to build a new WebJob from scratch, all we need to do is create a new console application and make sure we add the required assemblies to the project. In this sample I’ll use Visual Studio 2015 (preview), which as its name implies is currently in a beta.
Step 1: Create your console application
Start by creating a new project and make sure you’ve selected the “Console Application” template. Also, and this is important, make sure you’ve chosen .NET Framework 4.5!
[.
Step 2: Add the SharePoint-specific assemblies from NuGet
If you’re using Visual Studio 2015 as I’m doing, the NuGet package manager dialog will look slightly different from earlier versions of Visual Studio, but the concept’s the same.
- Go to “Tools” -> “NuGet Package Manager” -> “Manage NuGet Packages for Solution…”
- Search for “App for SharePoint“
- Install the package called “AppForSharePointWebToolkit” which will install the required helper classes for working with the SharePoint Client Object Model.[Make sure the NuGet package worked by making sure there’s these two new classes in your console application project:
[
{
using (ClientContext context = new ClientContext("https://redacted.sharepoint.com"))
{
context.AuthenticationMode = ClientAuthenticationMode.Default; context.Credentials = new SharePointOnlineCredentials(GetSPOAccountName(), GetSPOSecureStringPassword()); // TODO: Add any awesome code here! } }
private static SecureString GetSPOSecureStringPassword()
{
try
{
Console.WriteLine(" --> Entered GetSPOSecureStringPassword()"); var secureString = new SecureString(); foreach (char c in ConfigurationManager.AppSettings["SPOPassword"]) { secureString.AppendChar(c); }
Console.WriteLine(" --> Constructed the secure password");
return secureString;
}
catch
{
throw;
}
}
private static string GetSPOAccountName()
{
try { Console.WriteLine(" --> Entered GetSPOAccountName()"); return ConfigurationManager.AppSettings["SPOAccount"]; } catch { throw; }
}
You can see in my sample application that I’ve added two helper methods for fetching the Account Name and Account Password from the app.config file. These are explained in the authentication-section further down in this article.
As for the main method, that’s all we need to wire things up to our portal. Before we dig deeper into how we can manipulate SharePoint from our code, let’s discuss options for authentication.
Authentication considerations
When it comes to authentication, there’s a few options I’ve tried which makes sense. We’ll check out two options for authentication and see how they differ.
Option 1: Use a Service Account (Username + Password)
This approach is pretty straight forward and enables you to simply enter an account and password to your Office 365 tenant and then use for example CSOM to execute code on your sites. This is what you see in my sample code above as well.
Create a new Service Account in Office 365
In order for this to work and be a valid approach, a specific account should be created that acts as a service account – either for this specific application or a generic service application account that all your jobs and services can use.
For the sake of this demo, I’ve created a new account called SPWebJob
:
[ constructor:
[
{
try
{
Console.WriteLine("Initiating Main()");
using (ClientContext context = new ClientContext("https://redacted.sharepoint.com"))
{
Console.WriteLine("New ClientContext('https://redacted.sharepoint.com') opened. ");
context.AuthenticationMode = ClientAuthenticationMode.Default;
context.Credentials = new SharePointOnlineCredentials(GetSPOAccountName(), GetSPOSecureStringPassword());
Console.WriteLine("Authentication Mode and Credentials set");
List translationlist = context.Web.Lists.GetByTitle("Automatic Translations");
context.Load(translationlist);
context.ExecuteQuery();
Console.WriteLine("TranslationList fetched, loaded and ExecuteQuery'ed");
if (translationlist != null && translationlist.ItemCount > 0)
{
Console.WriteLine("The list exist, let's do some magic");
CamlQuery camlQuery = new CamlQuery();
camlQuery.ViewXml =
@"<View>
<Query>
<Where><Eq><FieldRef Name='IsTranslated' /><Value Type='Boolean'>0</Value></Eq></Where>
</Query>
</View>";
ListItemCollection listItems = translationlist.GetItems(camlQuery);
context.Load(listItems);
context.ExecuteQuery();
Console.WriteLine("Query for listItems executed.");
foreach (ListItem item in listItems)
{
item["Output"] = TranslatorHelper.GetTranslation(item["Title"], item["Target Language"], item["Original Language"]);
item["IsTranslated"] = true;
item.Update();
}
context.ExecuteQuery();
Console.WriteLine("Updated all the list items we found. Carry on...");
}
}
}
catch (Exception ex)
{
Console.WriteLine("ERROR: " + ex.Message);
Console.WriteLine("ERROR: " + ex.Source);
Console.WriteLine("ERROR: " + ex.StackTrace);
Console.WriteLine("ERROR: " + ex.InnerException);
}
}
The logic in the TranslatorHelper
class isn’t published yet but may be part of an article for the future about Translator API’s.
Note: As seen from the code this is a demo and definitely not for production use. However all the Console.WriteLine additions are added in order for us to review the execution of the jobs easily from the Azure Portal. More on logging and monitoring further down in this article.
Publishing your WebJob to Azure
When you’ve developed your WebJob and you’re ready to deploy it to your Azure environment (deploys to an Azure WebSite), you have two main options as described below.
Option 1: Upload a zip file with the WebJob binaries to your Azure Portal
Using the Azure Portal where you keep all of your awesomeness in Azure, you can upload a zip-file containing the output from Visual Studio’s build. This is an easy way for compiling and shipping your code to someone else who will do the deployment for you.
Create the zip file
Simply grab all the output files from your Visual Studio build (normally in your bin/Debug or bin/Release folder):
[.
Okay, done – you can now run your webjob from your Azure Portal:
[.
Option 2: Publish directly to Azure from Visual Studio
This is my favorite one at this point because I can use the tooling in Visual Studio to quickly publish any changes directly to my hosted service. The other benefit will become clear soon, as you can also schedule the job exactly how you want it to execute directly from the dialogs in Visual Studio.
Choose to publish the WebJob from Visual Studio 2015
Note: These dialogs may differ slightly if you’re running an earlier version of Visual Studio. Also, I am already logged in so if you’re doing this for the first time you may get a login-dialog in order to sign in to your Azure account. That’s a pre-requisite.
Simply right-click your project and select “Publish as an Azure WebJob…“:
[ you can configure the schedule directly from the dialogs:
[.
View all job executions and status
If you want to review when the job last ran, what the outcome of every execution of the job was or review what happened during execution of the job, you can click on the link under “Logs” when you’re in the WebJobs overview:
[ project, it started the project up as a Console Application based on .NET Framework 4.5.3.
Running the job locally works fine, since .NET Framework 4.5.3 exist on my dev machine. However, once I deployed the job to My Windows Azure Web Site as a WebJob, it failed with “exit code -2146232576“.
Solution: Make sure you’re on the correct .NET version
It took a while before I realized that Azure didn’t like .NET Framework version 4.5.3, but when I changed to .NET Framework 4.5, it works.
If you bump into that problem, just make sure your job is executing under the correct .NET framework version.
[: Building a SharePoint App as a Timer Job
- Azure Documentation: How to Deploy Azure WebJobs to Azure Websites
- Vesa Juvonen (MSFT): Asynchronous Site Collection Provisioning for Office 365
- Office 365 PnP Code Samples: Core.SimpleTimerJob
- Channel 9 Video with Andrew Connell: Simple remote timer job that interacts with SharePoint Online – Office 365 Developer Patterns and Practices
Enjoy!