Testing

webTiger Logo Wide

Getting SharePoint Site Templates To Work On Different Environments

SharePoint 2013 Logo

If you work with SharePoint custom site templates regularly, and your organisation has separated development, test, and production environments, then you’ll probably have come across the issue where a custom site template you’ve saved in one environment as a solution package (WSP) uploads and activates fine in another environment but then fails with the familiar obscure message and correlation ID when you attempt to create a new site.

There can be many causes for this, but the most common ones are conflicts with site template prerequisites for ‘required’ features, site columns, content types, or custom master page layouts.

Custom site templates are a great SharePoint capability but they introduce their own share of headaches in some cases. A lot of stuff gets saved in the site template package, and not all of it is actually required for the site template to work on another environment.

DISCLAIMER: most of the fixes here are tantamount to hacking the WSP package to get it to work, which can leave the WSP broken and unusable if you aren’t careful about the changes you make. You follow these instructions completely at your own risk!

First Steps – Finding the Issues

Before you can fix the problems with WSP you need to find out what they are. As with most things SharePoint related, that means digging about in the ULS logs.

I like to merge the farm logs into a single ULS log file before investigating, to make sure I have all the data at hand at the same time. Below is a PowerShell script that will collate the logs into a combined file from when it is run to 3 minutes beforehand – it is purposely kept as a short timeframe as a collated ULS log can get big very quickly if you are merging over a long time!

Add-PSSnapin Microsoft.SharePoint.PowerShell -EA SilentlyContinue
$ErrorActionPreference = "Stop"
$workingFolder = Get-Location
$logFilePath = [System.IO.Path]::Combine($workingFolder, "$(Get-Date -Format "yyyy-MM-dd-HHmmss")-MergedULS.log")
$until = Get-Date
$from = $until.AddMinutes(-3)
Merge-SPLogFile -Path $logFilePath -StartTime $from -EndTime $until
Write-Host "Merged ULS log written to: $logFilePath"Code language: PowerShell (powershell)

In most cases 3 minutes is plenty of time to capture all the log data you need but you could change the $from date/time calculation to -5 or -10 for a larger timeframe if you wanted.

Once you’ve got your merged log file you’ll need to take a look at it, and Microsoft’s ULSViewer utility is the best way. Simply filter the log entries based on the correlation ID you got from the web browser page and then look for error messages.

Missing Features – Part 1

The most common problem with moving custom site templates between environments is missing site features. Something as simple as ‘allow workflows to run with app permissions’ being disabled on the parent site can stop a sub-site being created using the template.

Look for these first as they are the easiest to fix (often just by activating the feature on the parent site and then trying to create the sub-site again). One of the annoying things with this though is that the ULS logs normally quote the ‘internal name’ of the feature and not what might be displayed on the features configuration web page for a particular site.

You can get the ‘display name’ and ‘description’ for all site features using the PowerShell script below:

Add-PSSnapin Microsoft.SharePoint.PowerShell -EA SilentlyContinue
$web = Get-SPWeb "http://myspserver/sites/mysubsite"
$cultureInfo = [System.Globalization.CultureInfo]::CurrentCulture
$results = @()
foreach ($feature in $web.Features)
{
$result = $null
$result = New-Object PSObject
$result | Add-Member -NotePropertyName "Id" -NotePropertyValue $feature.DefinitionId
$result | Add-Member -NotePropertyName "DisplayName" -NotePropertyValue $feature.Definition.DisplayName
$result | Add-Member -NotePropertyName "Description" -NotePropertyValue $feature.Definition.GetDescription($cultureInfo)
$result | Add-Member -NotePropertyName "Status" -NotePropertyValue $feature.Definition.Status
$results += $result
}
$results | Format-Table -AutoSizeCode language: PowerShell (powershell)

Even with the above script, what is displayed as the feature title on the site features web page is often different so I tend to rely on the description as that usually matches!

NOTE: sometimes features need activating at the site collection level and not just the parent (web) site level.

Editing the WSP Package

If enabling site (or site collection) features didn’t work and you know your custom site template does require/use the quoted feature then you can take the more drastic step of editing it out of the WSP package.

DISCLAIMER: once again, a reminder that if you are following these instructions you do so at your own risk, and you, and you alone, own and are responsible for the consequences of your actions!

The first thing we need to do is unpack the WSP file. This can be done simply by changing the file extension from .WSP to .CAB (or by adding a .CAB extension after the existing .WSP one), and then unzipping the package into a sub-folder.

Missing Features – Part 2

Search for a onet.xml file in the unzipped package folder sub-tree. It should be in the ‘PACKAGENAMEWebTemplate’ sub-folder, where PACKAGENAME is normally the ‘safe filename’ of your package (without the .WSP extension). Open this file in a suitable editor (e.g. Visual Studio Code). Each feature is defined with a ‘comment’ line specifying the feature title and then XML tags for the feature definition itself, similar to the example below:

<!--Translation Feature-->
<Feature ID="{FEATURE_GUID}" Name="FeatureDefinition/15/FEATURE_GUID" />Code language: HTML, XML (xml)

You can remove the feature prerequisite simply by deleting those two lines and saving the changes. In some cases there may be opening/closing tags for the feature definition with additional configuration data inside them. Just delete the whole tag section in each case.

Try to only delete the features you know the template doesn’t need (i.e. those that are causing the issues).

NOTE: the WSP package needs to be rebuilt before it can be used, and this is described further on in this article.

Conflicting Content Types

Another common problem can be conflicting content types. How you respond to these issues will depend on how complex your custom site template is and how easy it is to just delete content type definitions from the site template that you know you don’t need.

These content types are typically referenced in a file in the ‘PACKAGENAMEListInstances’ sub-folder, called ElementsContentType.xml.

You can carefully go through that file and remove conflicting or unnecessary content types, as necessary, and then save the changes. Make sure you don’t delete any content types that your custom site template relies on!

Conflicting Site Columns

Site columns can lead to conflicts too. Again, how you respond to these issues will depend on how complex the custom template is and how easy it is to just delete the site column from the template. Remember, site columns are likely to be referenced by content types so do thorough analysis of the interdependencies before blindly removing them from the template specifications.

The site columns are typically reference in a file in the ‘PACKAGENAMEListInstances’ sub-folder, called ElementsFields.xml.

Again, you can carefully go through the file and remove conflicting site columns, as necessary, and then save the changes.

Re-packaging the Site Template

To re-package the site template files, we are going to use Windows’ makecab.exe utility in conjunction with a hand-crafted .DDF file.

The .DDF file is simply a formatted text file that describes how to build a CAB file. Below is a partial example.

.OPTION EXPLICIT
.Set CabinetNameTemplate="MyCustomTemplate.wsp"
.Set DiskDirectoryTemplate=CDROM
.Set CompressionType=MSZIP
.Set MaxDiskSize=CDROM
.Set Cabinet=on
.Set Compress=on
.Set DiskDirectory1="c:\tmp\my-unzipped-package-folder"
"MyPackageNameListInstances\Files\Lists\Orders\DispForm.aspx" "MyPackageNameListInstances\Files\Lists\Orders\DispForm.aspx"
"MyPackageNameListInstances\Files\Lists\Orders\EditForm.aspx" "MyPackageNameListInstances\Files\Lists\Orders\EditForm.aspx"
"MyPackageNameListInstances\Files\Lists\Orders\NewForm.aspx" "MyPackageNameListInstances\Files\Lists\Orders\NewForm.aspx"
"MyPackageNameListInstances\Files\Lists\Orders\Schema.xml" "MyPackageNameListInstances\Files\Lists\Orders\Schema.xml"Code language: plaintext (plaintext)

You will need to re-create lines for each of the files in the unzipped folders sub-tree, complete with sub-path information. Above a single custom list (Orders) with custom forms is specified as an example.

Once you’ve completed the mind-numbing job of building the DDF file, you can use makecab.exe (the Windows built-in utility) to re-build the revised package:

makecab /f filename.ddfCode language: plaintext (plaintext)

Assuming you gave your ‘CabinetNameTemplate’ property a value with a .WSP extension the newly compiled package is ready to go. If you left it with a .CAB extension then you’ll need to rename it to .WSP again first.

The newly re-built package should no longer rely on the features, site columns, or content types it didn’t really need and should be able to be used to create new sites on the target environment from then on!

Other Potential Issues – Workflows

Aside from the above, another common problem I’ve experienced is where workflows can’t be created during the site creation process. All the times I’ve experienced this it was with 2013 Workflows, so I’ve taken to only using 2010 Workflows with site templates. I know this means they won’t be able to be used in SharePoint Online in the near future (when 2010 Workflows reach end of life), but they’ll work just fine on-premise for the foreseeable future!

I’ve never got to the bottom of the reasons that 2013 Workflows cause issues, and where a 2013 Workflow had to be used for functional reasons I’ve tended to manually re-produce it on the newly created site afterwards.