SharePoint Timer Jobs running as Windows Azure Web Jobs

The Office App Model Samples (Office AMS) contain some great examples of writing console applications using CSOM. A console application is the foundation for building timer jobs against SharePoint Online and offers a safer alternative to full-trust timer jobs written for on-premises SharePoint. In this post, I will show you how incredibly easy it is to build a timer job as a console application, package it, deploy it as a Web Job in Windows Azure, and schedule it to periodically run against SharePoint. Kirk Evans authored a fantastic post last month on Building a SharePoint App as a Timer Job that I highly recommend you read to understand the logistics of running a console application using OAuth and the app model.

[View:https://www.youtube.com/watch?v=Z7wHj-bSk0g]

OAuth or Service Account

Kirk’s sample leveraged OAuth and App Only permissions to perform operations on the SharePoint tenant. The alternative is to leverage a service account and explicitly authenticate as this account to perform operations.  This can be done using normal Windows Authentication or using the SharePointOnlineCredentials class with SharePoint Online:

Using SharePointOnlineCredentials for ClientContext

char[] pwdChars = ConfigurationManager.AppSettings["AccountPassword"].ToCharArray();System.Security.SecureString pwd = new System.Security.SecureString();for (int i = 0; i < pwdChars.Length; i++) pwd.AppendChar(pwdChars[i]);ClientContext cc = new ClientContext("https://tenant.sharepoint.com");cc.AuthenticationMode = ClientAuthenticationMode.Default;cc.Credentials = new SharePointOnlineCredentials(ConfigurationManager.AppSettings["AccountUsername"], pwd);

 

I prefer the OAuth approach used by Kirk as it is easier to provide tenant-wide permissions and you don’t have to deal with periodic password changes a service account might encounter.  The OAuth approach requires you to deploy a provider-hosted app and then leverage that app’s client id and client secret in a console application. Kirk’s post covers this in great detail.

The example below is a timer job written to leverage CSOM and OAuth in a console application. This specific job copies OneDrive Usage Guidelines to every OneDrive for Business site (aka – every my site). This is a very common ask from customers and represents a perfect scenario for app model timer jobs.

Console application to upload Usage Guidelines to all OneDrive sites

using Microsoft.SharePoint.Client;using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;

namespace OneDrivePoliciesJob{ class Program { //this is a hack...real solution would enumerate sites or store them in a database private static string[] sites = new string[] { "https://rzna-my.sharepoint.com/personal/ridize_rzna_onmicrosoft_com", "https://rzna-my.sharepoint.com/personal/alexd_rzna_onmicrosoft_com", "https://rzna-my.sharepoint.com/personal/annew_rzna_onmicrosoft_com", "https://rzna-my.sharepoint.com/personal/roby_rzna_onmicrosoft_com"};

        /// <summary> /// This sample shows how to execute a timer job against SharePoint using CSOM. Although built as a console app, /// the executable will be uploaded Azure and run as a WebJob in azurewebsites (ie - scheduled task). This sample /// uses OAuth to authenticate with app-only calls. To do this, you need to first trust the client id and secret with /// SharePoint. You can also use the SharePointOnlineCredentials class to authenticate with credentials instead of OAuth /// </summary> /// <param name="args"></param> static void Main(string[] args) { foreach (var site in sites) { //get site Uri siteUri = new Uri(site);

                //Get the realm for the URL string realm = TokenHelper.GetRealmFromTargetUrl(siteUri);

                //Get the access token for the URL. Requires this app to be registered with the tenant string accessToken = TokenHelper.GetAppOnlyAccessToken(TokenHelper.SharePointPrincipal, siteUri.Authority, realm).AccessToken;

                //Get client context with access token using (var clientContext = TokenHelper.GetClientContextWithAccessToken(siteUri.ToString(), accessToken)) { var folder = clientContext.Web.Lists.GetByTitle("Documents").RootFolder; clientContext.Load(folder); clientContext.ExecuteQuery();

                    //upload the "OneDrive for Business Usage Guidelines.docx" using (var stream = System.IO.File.OpenRead("OneDrive for Business Usage Guidelines.docx")) { Console.WriteLine("Uploading guidelines to " + site); FileCreationInformation fileInfo = new FileCreationInformation(); fileInfo.ContentStream = stream; fileInfo.Overwrite = true; fileInfo.Url = "OneDrive for Business Usage Guidelines.docx"; folder.Files.Add(fileInfo); clientContext.ExecuteQuery(); } } } } }}

 

Packaging

After testing the console application locally, we can look to packaging it for deployment into Windows Azure. The first step in packaging is to ensure all the referenced assemblies will be available in the cloud. By default, Windows Azure won’t have CSOM assemblies (ex: Microsoft.SharePoint.Client, Microsoft.SharePoint.Client.Runtime). Any assembly reference in question should be configured with “Copy Local” set to True.

After configuring these references, rebuild the project to create a new output. A traditional provider-hosted app packages with an .app extension, which is deployed into an app catalog. The console application will package as a .zip of the applications output directory (ex: <project folder>\bin\Debug or <project folder>\bin\Release). It does not matter what you name this zip file. Notice below that I zipped up the Debug output folder that contains the console application executable and referenced assemblies:

Deploying and Scheduling

Azure Websites have a new feature called Web Jobs that can host background operations (ex: console applications) to run on demand, continuously, or on a schedule.  The first step is provisioning an Azure Websites in the Azure Management Portal (https://manage.windowsazure.com). Select “Web Sites” from the side navigation and use the NEW button to create a new web site. Here is an example of using Quick Create to create a Web Site in the North Central Azure data center:

Once the Web Site finishes provisioning (5-10 seconds), open it by clicking it in the Web Site listing. You will notice a new tab across the top for WEBJOBS (currently in Preview). Click on this link to display the list of Web Jobs (should be empty):

Next, click on the “ADD A JOB” link to launch the New Job dialog. Specify a name for the job, browse to the zip file we created of the console application’s output directory, and set the Web Job to “Run on a schedule” before clicking the next button:

On the next screen, you can specify the reoccurrence and schedule for the Web Job. The schedule can easily be changed later and the Web Job can be run on demand (very similar to a typical SharePoint Timer Job):

After configuring and accepting the schedule, the Web Job will deploy into Windows Azure and begin running on the specified reoccurrence. The Web Jobs listing will show the status of the job, the time and outcome of the last run, and links to change the schedule and view the logs. This screen also provides the ability to run Web Jobs on demand by selecting a job and clicking on the RUN ONCE button in the footer:

Clicking on the schedule link will take you to the schedule dashboard, which provides some high-level metrics of jobs and links the tweak the schedule:

Final Thoughts

Kirk Evans and Office AMS have outlined some great patterns for performing operations against SharePoint using console applications and CSOM. If the operation a timer job is performing can be refactored to use CSOM, Windows Azure Web Jobs can help duplicate the SharePoint Timer Service and scheduling/executing interface. This pattern works both for SharePoint Online and SharePoint On-Premises and will help better prepare you for upgrades and the cloud.

Download the OneDrive Usage Guidelines Timer Job used in this post: https://1drv.ms/1jox3Hq