Migrating Host-Named Site Collections
Summary: Guest blogger and Microsoft PFE, Chris Weaver, talks about using Windows PowerShell to migrate host-named site collections.
Microsoft Scripting Guy, Ed Wilson, is here. Welcome back today to guest blogger, Chris Weaver…
This week I was onsite with a customer in North Carolina. They were migrating to SharePoint 2013, but they hit an issue with support. The issue was that they had an excessive number of web applications, and they needed to find a way to reduce those applications but still maintain their URLs.
In SharePoint 2013, the recommended way of doing this was to use host-named site collections. I realize host-named site collections have been around since MOSS 2007/WSSv3, but let's be honest with each other—they were not ready for mainstream at that time.
The product group has put a lot of work into host-named site collections because they realize that this would allow customers great flexibility with URLs and a way to reduce web applications (literally, they all should go in one web application). If you have caught on, the great thing about host-named site collections is that each one has its own URL. For more information about host-named site collections, please check out this article: Host-named site collection architecture and deployment (SharePoint 2013).
So my customer was stuck trying to migrate approximately 70 site collections from path-based to host-named site collection. Now, the steps to do this are fairly simple:
- Backup the site collection.
- Restore the site collection as a host-named site collection (the restore will create the site collection for you).
But each migration could take hours, and of course because there will be downtime, this work has to be done during off hours on weekends and nights. Windows PowerShell to the rescue! We very quickly put together a basic script that performed the following steps:
1. Back up the path-based site collection (this will back it up to a file). This command also sets the site to Read-only while it runs to prevent users from changing it.
Backup-SPSite -Identity "$WebApplicationOriginal/$Site" -Path $Filename -Confirm:$false
2. Lock the site collection to Read-only so that nobody can change it now that we have it backed up.
Set-SPSite -Identity "$WebApplicationOriginal/$Site" -Confirm:$false -LockState ReadOnly
3. Change the default alternate access mapping so that we can reuse the URL when we create the host-named site collection.
Set-SPAlternateUrl -identity $WebApplicationOriginal -url $WebApplicationOther -Zone default -Confirm:$false
4. Then we restore the backup file. This will re-create the site, and because we are using the -HostHeaderWebApplication switch, it will create the site as a host-named site collection under that particular web application. In the command, we also are using the -ContentDatabase switch to insure that the Site collection is created in a specific SQL database.
Restore-SPSite -Identity "$WebApplicationOriginal/$Site" -Path $Filename -Confirm:$false -ContentDatabase $WebAppNetbios$Site$DatabaseName_Post -HostHeaderWebApplication $HostHeaderWebApplication
We could have stopped there. We had a fairly good script that could deal with most situations with minimal interaction from my customer. But I wasn't happy. I knew I could do more. There were a few outlying scenarios that they were going to deal with manually, but I thought they would be great for this script. There were also a few things I wanted to do to make the script better.
First came some very basic error checking to make sure that we don't start breaking things, because a previous command failed:
1. Clear the error variable.
$Error.Clear()
2. Then I wrap each command I need to run in a simple If statement.
If(!$Error) {<#Then do this#>}
Next came the question about managed paths because they had several web applications that had a root site with several sites under a managed path. So I wrote some simple script to create them, and I added some logic to deal with several things. If the $NewManagedPaths variable is $Null, we skip the whole thing (note that it is an array to allow for multiple paths). We also can create paths that are explicit or we can use wildcard characters (sadly, all managed paths will be whatever you choose).
if(($NewManagedPaths -ne $null) -and (!$error))
{
foreach ($Path in $NewManagedPaths)
{
if($Explicit)
{
New-SPManagedPath -RelativeUrl $Path -WebApplication $HostHeaderWebApplication -Explicit
}
Else
{
New-SPManagedPath -RelativeUrl $Path -WebApplication $HostHeaderWebApplication
}
Write-Output "Created managed path $Path..."
}
}
Then we realized that we wanted to be able to work with all the sites within the web applications together, so I wrote two lines to get the relative URL for all site collections in the web application by using the Sites and Names properties:
$WebApplication = Get-SPWebApplication -identity $WebApplicationOriginal
$Sites = $WebApplication.Sites.Names
This introduced an issue in that we wanted to leave behind the office viewing site and possibly other sites. But by using some comparison and an If statement, we quickly worked around that.
The last thing we added to the script was logic to lock the site collection by using the Set-SPSite cmdlet and the -LockState switch . We set to it to Read-only so that users could not accidently introduce new documents after the backup:
Set-SPSite -Identity "$WebApplicationOriginal/$Site" -Confirm:$false -LockState ReadOnly
If you would like to more closely look at the script (or even use it), you can download it from the Script Center Repository: Migrate from Path-Based to Host-Named Site Collections in SharePoint 2013.
~Chris
Thanks, Chris, for being so kind as to share your knowledge and time. Looking forward to seeing you in Charlotte for PowerShell Saturday #007 on Feb 8, 2014. Maybe some of our readers will be there too.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy