Recommended approach to provisioning content types in SharePoint 2010, 2013 and SharePoint Online

Recommended approach to provisioning content types in SharePoint 2010, 2013 and SharePoint Online

Much has been written and even more has been discussed about content types in SharePoint. Since there are so many ways to provisioning content types, I wanted to capture the essential information for anyone in need of clear advice on this topic. This article is aimed at all roles of a given SharePoint deployment: site collection admins, IT Pros, and developers. And as always with SharePoint, things vary and depend on many factors.

What is a content type?

Instead of rehashing what’s already written very nicely on MSDN, here’s a nice quote explaining what a content type is. Note that this is for SharePoint Foundation 2010 but the same information applies to newer versions.

A content type is a reusable collection of metadata (columns), workflow, behavior, and other settings for a category of items or documents in a Microsoft SharePoint Foundation 2010 list or document library. Content types enable you to manage the settings for a category of information in a centralized, reusable way. (Source)

A content type holds one or more site columns, that make capturing information available for SharePoint document libraries and lists.

How do I provision a content type?

This is a hard question to answer short and simple. Not because it’s technically a complex task but because the provisioning approach you will select varies based on your business requirements and overall SharePoint customization model. You might also want to build your solutions future-proof, which in this context means two things: the solution shouldn’t block you from upgrading to a future version of SharePoint, and it shouldn’t block you from migrating to SharePoint Online.

This is by no means a definitive guide for all things content type, but rather good and comprehensive guidance for anyone wondering what a good approach would be.

If you are a site collection admin..

As a site collection admin, you often need to only worry about a single site collection. Possibly two or three, but rarely you’ll have several content types that need to span tens or hundreds of site collections. Thus, the recommended and easiest provisioning technique for you is via SharePoint’s Site Settings. Navigate to the root of the site collection, select the Gear icon > Site Settings > Site Content Types, and add the necessary content types that best describe your data capture needs.

This approach is the same regardless if you’re on SharePoint 2010, 2013 or SharePoint Online.

The idea is to avoid duplicating content types in multiple places. Thus, if you work with a single site collection and have only a few (less than, say, 10) content types, this is a working approach.

Pros:

  • Suitable for small environments (single or a few site collections, less than 10 content types)
  • Very easy to do

Cons:

  • Unable to control the IDs of the provisioned content types (more on this later in this article)
  • No packaging approach or a deployment model between environments

What if you are an IT Pro?

I consider an IT Pro in 2014 a person who is comfortable when using a keyboard (and sometimes a mouse). You should also be quite familiar with PowerShell-based scripts and PowerShell ISE.

For smaller deployments, it’s perfectly acceptable for an IT Pro to use the same approach a site collection admin uses.

However, you might also need

  • multiple content types in multiple site collections
  • to reuse of content types in different environments (dev, test, QA, production, etc.)
  • to upgrade content types at a later stage when business requirements change
  • gathering of content type usage
  • to avoid repetitive manual work when provisioning content types

As an IT Pro, you can leverage PowerShell for provisioning content types. This approach varies quite heavily whether you’re targeting an on-premises SharePoint 2010/2013 or SharePoint Online. I sometimes hear from IT Pros that they don’t like to dive deep into PowerShell or the provisioning logic of artifacts in SharePoint. This is perfectly acceptable, considering that a lot of IT Pros spend their days far away from SharePoint. More complicated business requirements often require more complicated content types, and for that, you’ll need to use SharePoint APIs.

With SharePoint 2010/2013, using PowerShell to provision content types is fairly simple – a working sample is below.

Add-PSSnappin Microsoft.SharePoint.PowerShell
Start-SPAssignment -Global

# This is your target site collection
$site = "http://sharepoint.domain.local"
$web = Get-SPWeb $site

$ctypename = "My Sample Content Type"

$parentCT = $web.AvailableContentTypes["Document"]

$ctype = New-Object Microsoft.SharePoint.SPContentType($parentCT, $web.contenttypes, $ctypename)
$web.ContentTypes.Add($ctype)

$web.Update()

$web.fields.add("My Sample Site Column", "Text", $false)

$field = $web.Fields.GetField("My Sample Site Column")
$field.Group = "My Sample Site Column Group"

$fieldLink = New-OBject Microsoft.SharePoint.SPFieldLink($field)
$ctype.FieldLinks.Add($fieldLink)

$web = Get-SPWeb $site

$ctype.Update()

Stop-SPAssignment -Global

Note that this script does not control the properties of the content type – such as the content type ID – to keep the script short and simple. If you need more control over the provisioning process, see the developer approach later in this article.

A custom content type is now visible in the site collection:

image

The content type inherits from the Document content type, which is evident when you look into the content type:

image

And it has a single site column that we specified:

image

If you’re working with SharePoint Online, obviously you cannot use the SharePoint 2013 PowerShell Management Shell. The alternative is to use CSOM, or Client-Side Object Model, which you can leverage from PowerShell. This requires a copy of Microsoft.SharePoint.Client.dll. To aid in this, I recommend using the SharePoint Client Browser, which is an open-source tool for analyzing your SharePoint data structures. It’s available here.

The tool shows you the current data structures in a given SPO site collection:

image

This is useful if you need to debug broken content types or verify what has been provisioned already.

By calling CSOM from PowerShell, we can provision content types and custom site columns. It’s a bit cumbersome, and not as straightforward as you might like.

First, you’ll have to deal with the logic of client-side logic: Whenever you want to provision something, you’ll need to load the object, manipulate it, and update it back over the wire. And load whatever assets you need on the way.

The script for provisioning a custom site column and a custom content type is below.

[System.Reflection.Assembly]::LoadFrom("C:\temp\Microsoft.SharePoint.Client.dll") # define target SPO site collection and credentials to connect with $siteUrl = “https://<tenant>.sharepoint.com/” $username = "user@domain.com" $password = Read-Host -Prompt "Enter password" -AsSecureString # connect and authenticate to SPO $ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl) $credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $password) $ctx.Credentials = $credentials # retrieve all site columns (fields) $web = $ctx.Web $fields = $web.Fields $ctx.Load($web) $ctx.Load($fields) $ctx.ExecuteQuery() # define a new site column as XML - use a custom GUID here! If you don't have guidgen.exe, you can use http://guidgenerator.com $fieldOption = [Microsoft.SharePoint.Client.AddFieldOptions]::DefaultValue $fieldAsXML = "<Field ID='{EE7B9DD8-CF49-46B2-B27B-CB561FEB483C}' Name='MySampleSiteColumn' DisplayName='My Sample Site Column' Type='Text' Hidden='False' Group='My Sample Site Columns' Description='My Sample Site Column' />" # add the new site column and commit the change $fld = $fields.AddFieldAsXml($fieldAsXML, $true, $fieldOption) $ctx.Load($fields) $ctx.Load($fld) $ctx.ExecuteQuery() # add a new content type - first, let's get all current ctypes $web = $ctx.Web $contentTypes = $web.ContentTypes $ctx.Load($web) $ctx.Load($contentTypes) $ctx.ExecuteQuery() # then load all content types that match the documents ID (0x0101) $docCT = $contentTypes.GetById("0x0101") $ctx.Load($docCT) $ctx.ExecuteQuery() # create a new content type object $cType = New-Object Microsoft.SharePoint.Client.ContentTypeCreationInformation $cType.Name = "My Sample Content Type" $cType.ParentContentType = $docCT $cType.Group = "My Sample Content Types" $myCType = $contentTypes.Add($cType) $ctx.ExecuteQuery() $ctx.Load($myCType) $ctx.ExecuteQuery() # finally add the custom site column to the content type $web = $ctx.Web $field = $web.Fields.GetByInternalNameOrTitle("My Custom Site Column") $contentType = $web.ContentTypes.GetById($myCType.Id) $ctx.Load($web.Fields) $ctx.Load($field) $ctx.Load($web.ContentTypes) $ctx.Load($contentType.FieldLinks) $ctx.Load($contentType.Fields) $fieldReferenceLink = New-Object Microsoft.SharePoint.Client.FieldLinkCreationInformation $fieldReferenceLink.Field = $field $contentType.FieldLinks.Add($fieldReferenceLink) $contentType.Update($true) $ctx.ExecuteQuery()

After executing this script, we can see a content type has been provisioned in SharePoint Online:

image

It inherits from the parent Document Content Type:

image

And we also have our custom site column as part of the content type:

image

It’s not trivial by any means, but definitely reproducible and doable for an IT Pro not used to scripting.

Pros:

  • Similar PowerShell logic works for on-premises and SPO
  • Easy to document and reuse later
  • Easy to reproduce and add custom logic, such as verifying if a given content type is deployed
  • Allows combining PowerShell and CSOM for powerful solutions and flexibility

Cons:

  • Might require cumbersome calls to CSOM from PowerShell, which some might feel should not be their expertise

But I’m a developer!

image

If you’re a developer, and familiar with Visual Studio and the different aspects of SharePoint-focused development, you should first take a look at the IT Pro-approach of using PowerShell with CSOM. You can wrap the logic in a .NET tool, or simply use PowerShell and embed the necessary logic.

For on-premises deployments, the traditional model has been a full-trust solution package (WSP) which delivers the content type structures either declaratively (with XML-based elements and Features) or by using the server-side object model (in a Feature Receiver, as an example).

I’d like to avoid this approach since whichever technology you use, it would be nice to not add large dependencies of custom code that lives inside SharePoint. A more modern way would be to leave the provisioning logic outside SharePoint and simply call back to SharePoint to manipulate the data structures.

This idea follows the notion that we don’t want to have tangible assets on SharePoint 2010/2013 that might later prove problematic when future SharePoint upgrades or migrations to the cloud are needed.

A sandboxed solution works also, but I wouldn’t recommend either a sandboxed solution or a no-code sandboxed solution (NCSS) for any model, even though it technically works today. This is mainly for the reason that you’d either resort to an XML-based approach, which often proves problematic in terms of maintenance and upgrades, or you’d need to use NCSS which is not supported for SPO anymore.

Then there’s the alternative hack of creating content types as a site collection admin (that is, manually creating a content type via Site Settings), and then exporting the site as a solution package and importing it back to Visual Studio for cleanup and re-use as a new sandboxed solution. This is really a hack, takes time, and is also error-prone.

Thus, as a developer, trying to future-proof your solutions, you’ll end up having the following options:

  • Use the CSOM API via .NET – a command-line tool, Windows NT Service, a custom web service, etc.
  • Use JSOM from a web interface – a lightweight approach
  • Use a SharePoint app to deliver the assets – possibly calling an external provisioning engine or using JavaScript

The first one is the easiest. Here’s an example of using Managed .NET CSOM from an ASP.NET Web Forms page to provision a new content type in SharePoint Online.

var ctx = new ClientContext("https://tenant.sharepoint.com"); var pwd = "pwd"; var uid = "user@domain.com"; SecureString password = new SecureString(); foreach (char c in pwd.ToCharArray()) password.AppendChar(c); ctx.Credentials = new SharePointOnlineCredentials(uid, password); Web web = ctx.Site.RootWeb; web.ContentTypes.Add(new ContentTypeCreationInformation { Id = "0x0101000117B6EDDB654D92961E36B284F48A58", Name = "My Custom Content Type (Dev)" }); ctx.ExecuteQuery();

This is quite easy and straightforward. Notice that you can now also define the exact content type ID if you need a more detailed control of the asset.

The use of a SharePoint app is technically possible but poses some considerations. First, if you provision a content type from a SharePoint-hosted app it will deploy the content type to the app web and not the host web where users typically need the content type. If this is acceptable, a SharePoint-hosted app is as close to a sandbox-based solution, that you should consider switching to this newer approach. This works nicely for SPO but requires the necessary plumbing for App Infrastructure with SP2013. Also, it isn’t supported for SP2010.

Second, if you need to provision items to the host web, consider using remote provisioning. With remote provisioning, you provide your own (external to SharePoint) provisioning engine that provisions content types and other assets to a given site or site collection. This could be a server-side solution, a JavaScript approach, or something else.

Pros:

  • A wide selection of techniques to choose from
  • Allows full freedom to provision content types for most scenarios and needs
  • CSOM works for both SP2013 and SPO
  • The App model evolves rapidly, so this is definitively the preferred way

Cons:

  • Sometimes challenging to decide a future-proof model that is simple
  • A developer can easily end up making too complex frameworks for simple provisioning needs