Using the Add-in model is a no-brainer for most new development efforts, especially if you are developing for SharePoint Online and still need to write server-side code. (At the time of this writing, the new SharePoint Framework is still in Developer Preview but is definitely worth adding to your repertoire for client-side SharePoint development in the years to come!)
But what about your existing solutions? Many organizations using SharePoint maintain large portfolios of custom code solutions that leverage different legacy development approaches advocated by Microsoft through the years, and most of the custom code developed for earlier versions of SharePoint cannot be directly migrated to the cloud. If your organization decided to move to the cloud tomorrow, where would you begin?
Taking inventory of your customizations
I have found that most legacy SharePoint customizations can be placed into the following categories:
- User interface enhancements (master page, branding, themes, custom web parts)
- Declarative items (XML for custom site columns, list instances, and content types)
- Timer jobs (and other administrative extensions to the platform) running in SharePoint Central Administration on-premises
- Farm solutions cannot be deployed to SharePoint Online.
- Sandboxed solutions that contain managed code can no longer be deployed to SharePoint Online (note that sandboxed solutions contain a managed code assembly by default, even if the solution does not contain any managed code).
- Custom master pages, while still supported in SharePoint Online, should be avoided unless absolutely necessary. (Microsoft has a history of making significant changes to the default master page in SharePoint Online, such as the addition of the app launcher/waffle/Hollywood Squares in the upper left corner. Sites with heavily customized master pages may not always have access to these changes as they are introduced in the future.)
The role of the Add-in model
When migrating these customizations, keep in mind that you will not necessarily be building and deploying a SharePoint Add-in to replace each customization. In fact, in many cases, you will simply be leveraging techniques from the Add-in model. These include paradigms that were introduced and/or popularized with the introduction of the Add-in model:
- Using client-side script wherever possible
- Running server-side code outside the SharePoint server that communicates with SharePoint via its APIs
In some cases, all you may need to do is build and deploy a “throwaway” provider-hosted Add-in within a local development environment that performs some one-time remote provisioning tasks, but is not intended to be accessed by end users and can be removed after its work is done.
Add-in model techniques enable us to make our SharePoint customizations with a “lighter touch.” By lighter touch, I am referring to a lighter footprint and/or impact on the SharePoint server. As we decrease the burden on the SharePoint server, we enable SharePoint to run more reliably and efficiently while also making it easier to administer and upgrade (at least on-premises…yes, these techniques all work on-prem as well!) To better explain this concept, I put together the table below contrasting legacy SharePoint development approaches with their modern lighter touch equivalents:
|Legacy Approach||Lighter Touch|
|Farm solutions with custom code running in the SharePoint IIS worker process||Provider-hosted Add-in with custom code running outside of SharePoint|
|Farm or sandboxed solutions that deploy declarative artifacts such as site columns, content types, and list instances||Remote provisioning these artifacts from a provider-hosted Add-in using CSOM|
|Custom timer jobs running in Central Administration using the SharePoint server object model||Remote timer jobs running as Windows scheduled tasks or Azure WebJobs using CSOM|
SharePoint Developer PnP: here to help!
Because the Add-in model represents such a strong departure from the way things were done in the past with full-trust code in SharePoint, Microsoft started the SharePoint Developer Patterns and Practices (or PnP) initiative to assist developers with transforming their existing solutions to be cloud-ready as they migrate to SharePoint Online in Office 365.
The PnP team maintains several Github repositories that include reusable, production-ready components, templates, and solution starters that demonstrate the preferred modern approach to SharePoint development — making customizations with a lighter touch wherever possible.
Eric Overfield just published a great blog post detailing the PnP initiative and how you can get involved.
My Pluralsight course
If you are interested in learning more, I recently published a Pluralsight course with lots of demos: Updating Legacy SharePoint Customizations to the Add-in Model. The course covers the process of migrating a heavily customized on-premises SharePoint site to SharePoint Online from start to finish. The clip below is a demo from the course showing how a legacy timer job customization can be made cloud-ready with the help of the PnP Timer Job Framework and Azure WebJobs.
In this course, you will see me leverage the PnP Core Component to drastically simplify writing .NET Managed CSOM code. I also use several great samples and solution starters from the PnP team, all with the objective of easing the pain associated with migrating legacy SharePoint customizations to the Add-in model.
I was recently faced with the unenviable task of moving the full contents of a custom SharePoint list with about 60 columns (including attachments totaling about 400 MB) between a hosted instance of SharePoint 2007 and SharePoint Online. While there is no shortage of options for getting data from a list in one environment into another, I was constrained by the fact that this was a cloud-hosted instance of SharePoint 2007 where I had no server or administrator-level access. I went through a mental checklist of my options:
- Save the list as a template with contents – Nope. Way too big with those 400 MB worth of attachments.
- Export the list to Excel – Nope. Only includes the fields defined in the list view and does not include attachments.
- Create an empty version of the list in the destination environment, then use datasheet/quick view to copy data between the two lists – Nope. Again, the attachment problem (not to mention how tedious it would be to create identical views that would support datasheet/quick view in both environments).
- Write code to programmatically handle the export from SharePoint 2007 – Eh. With no server-level access in the cloud-hosted SharePoint 2007 environment, writing full trust code with the server object model was off the table. Theoretically I could have written code that leveraged SharePoint 2007’s .asmx web services, but I only wanted to go there as a last resort.
Then, as if by providence, I accidentally clicked to open Access 2013 when I actually meant to open Excel. I knew SharePoint 2013 had the hooks to import and export data using Access, but could I somehow use Access to pull the data out of SharePoint 2007? I clicked to create a new Blank Desktop Database and started my journey: I gave the database a name and pressed Create:
Access then proceeded to lock up my computer for several minutes as it imported the data, but it was worth the wait. Keep in mind that although logically we are exporting the data from SharePoint 2007, from Access’ perspective we are importing this data into Access. From here, we will export this data to a list in our SharePoint Online site.
When the import completed, I clicked Close and noticed in the “All Access Objects” view in the left pane, my database now contained a table named after the SharePoint list I just brought over. I double-clicked it and sure enough, all the data was there including the list item attachments!
I pressed OK and allowed Access to do its thing (it took about 15 minutes). When it was done, my browser opened to the newly imported list in SharePoint Online. Not only was the list schema perfect (if you’ve ever imported a spreadsheet to create a new list, you know how frustrating it can be to have all of your Choice columns converted to Single line of text), but all the attachments to the list items were there as well!
You will notice a few extra columns are created as part of the process:
- Encoded Absolute URL
- Item Type
- URL Path
- Workflow Instance ID
- File Type