SharePoint 2010 to 2013 farm migration war stories: database-attach done right

Field notes from a SharePoint 2010 to 2013 migration: database attach upgrade order, Test-SPContentDatabase triage, claims conversion, deferred site upgrades.

We closed out a SharePoint 2010 to 2013 migration for a state government agency in December — roughly 1.9 TB of content spread across 54 content databases, six web applications, and about 11,000 site collections, most of them small and half of them forgotten. The farm had been running since 2011, which means it had accumulated six years of third-party web parts, one-off solutions written by contractors who no longer exist, and a customization inventory that nobody could produce on day one. This post is the field-notes version of how a database attach upgrade actually goes when the farm is old, big, and politically sensitive.

If you're planning a SharePoint 2010 to 2013 migration this year, the good news is the path is well-worn: database-attach is the only supported upgrade method, and the mechanics are documented. The bad news is that the documentation describes a farm that doesn't exist — one with a clean solution inventory, no orphaned site collections, and users who don't notice UI changes. Yours is not that farm. Neither was ours.

One more framing note before the details: yes, SharePoint 2016 shipped last May. This client is going to 2013 anyway, deliberately. Their third-party ISV stack isn't certified on 2016 yet, and a 2010-to-2013 hop keeps the option of a second, quieter hop later. Skipping a version is not supported with database-attach, so if you're on 2010 and dreaming of 2016, you're doing this migration first regardless. Might as well do it right.

Build the 2013 farm first, and build it boring

Database-attach means you stand up a brand-new SharePoint 2013 farm and bring the content to it. Everything that lives outside content databases — service applications, alternate access mappings, managed paths, web application policies, outgoing email settings, blocked file types, the lot — has to be recreated by hand or by script. We script it. A farm build script in PowerShell serves two purposes: it makes the production build identical to the three test builds that preceded it, and it becomes documentation the client keeps.

Two decisions to make early:

  • Authentication mode. SharePoint 2013 web applications are claims-based by default; classic-mode web applications can only be created via PowerShell and are already flagged as deprecated. If your 2010 farm is classic (most are), you will be converting. More on that below, because it deserves its own section.
  • Service applications. Search, Managed Metadata, and User Profile can have their databases attached and upgraded, but our default position is: upgrade Managed Metadata (the term store IDs matter, content references them), rebuild Search from scratch (a fresh 2013 index is better than an upgraded FAST-era mess), and take the User Profile Service on a case-by-case basis. The 2010 UPS was famously temperamental; carrying its databases forward carries its ghosts forward too.

Patch the new farm to a current cumulative update before you attach anything. Upgrading content onto an RTM-level 2013 farm and then patching is doing the same risky work twice.

The customization audit is the actual project

The database attach itself is a few cmdlets. The reason this engagement ran fourteen weeks instead of four is everything Test-SPContentDatabase told us before we ran them.

Point Test-SPContentDatabase from the new 2013 farm at every 2010 content database while it still lives on the old SQL instance — it's read-only, it doesn't touch the source. Do it early, and pipe the output somewhere you can diff over time:

# One report per database; review MissingFeature, MissingSetupFile,
# MissingWebPart, and MissingAssembly line items before any attach.
Get-Content .\contentdbs.txt | ForEach-Object {
    Test-SPContentDatabase -Name $_ `
        -WebApplication https://portal.agency.example |
        Out-File ".\reports\$_.txt"
}

Across 54 databases we got just over 3,100 line items. Triage collapsed them into three buckets:

BucketCount (approx)Action
Missing features/assemblies from retired solutions~2,400Clean up in source with feature-removal scripting, or accept the orphan rows
Third-party solutions still in use11 vendorsChase 2013-compatible builds; two vendors were gone, so two web parts were rebuilt
Custom farm solutions written in-house23 WSPsRecompile against the 2013 object model; 6 were retired instead

The two dead-vendor web parts were the schedule risk for the entire program. If a page's web part assembly doesn't exist on the new farm, the page throws errors after attach. Find these in week one, not on cutover weekend.

The other pre-flight worth automating: orphaned site collections. Six years of hard site deletions and interrupted stsadm operations left us with 40-odd site collections that existed in the content database but not in the configuration database, or vice versa. Test-SPContentDatabase flags some of this; a pass with Get-SPSite -Limit All against each database and a reconciliation script found the rest. Fix orphans on the 2010 side. Dragging them through the upgrade turns a data-hygiene problem into an upgrade-blocking problem.

Upgrade order, throughput, and the cutover weekend

We sequenced content databases in three waves: low-risk collaboration sites first, departmental portals second, the records-management and legal databases last — after two clean rehearsals. The rehearsals matter more than the plan. We restored production backups to the test farm and ran the full attach sequence twice, with a timed runbook. First rehearsal: 31 hours of wall-clock upgrade time. After moving the SQL instance to faster storage and running four Mount-SPContentDatabase operations in parallel (one per database, never two against the same web application simultaneously), rehearsal two came in at 14 hours. That number is what let us promise a single weekend outage with a straight face.

Mount-SPContentDatabase -Name WSS_Content_Dept01 `
    -DatabaseServer SQLSP13 `
    -WebApplication https://portal.agency.example

Practical notes from the production run:

  • Set the source databases read-only on Friday night rather than taking SharePoint 2010 fully offline. Users could still read documents during the upgrade window, which cut the help desk noise dramatically.
  • Watch the upgrade session log, not just the cmdlet output. Upgrade-SPContentDatabase and mount operations write detailed logs to the 15 hive; the summary status lies by omission.
  • Budget SQL transaction log space at roughly the size of the database being upgraded. We learned this on rehearsal one, at 2 a.m., the hard way.
  • One database failed mount with a schema version mismatch — it turned out to have missed a 2010 CU that the rest of the farm had. Patch-level consistency on the source farm is a pre-flight check now.

Deferred site collection upgrade: use it, but put a date on it

SharePoint 2013 replaced 2010's "visual upgrade" toggle with something more honest: deferred site collection upgrade. After database attach, every site collection keeps running in 14 mode — the 2010 UI, the 2010 feature set — on the 2013 farm, until someone upgrades it to 15 mode. Site collection administrators can do it themselves from a health-check-gated self-service page, or you do it centrally with Upgrade-SPSite.

This is a genuinely good mechanism and also a trap. The good: you can complete the platform migration on one weekend and spread the UI change over months, with Request-SPUpgradeEvaluationSite giving site owners a temporary evaluation copy in 15 mode to preview. The trap: without a deadline, 14-mode sites become permanent. We've now seen farms migrated in 2013 that still run half their sites in 14 mode — meaning they've been paying for a new farm while their users live in the old one, and every support call has two possible UIs.

Our pattern for this client: run Test-SPSite health checks across everything post-attach, fix the failures, upgrade the IT department's own sites in week one (eat your own cooking), then batch-upgrade by department on a published calendar over ten weeks. Throttle settings (SPSiteUpgradeThrottleSettings on the content database and web application) keep self-service upgrades from stampeding the farm. Deadline enforced by removing the compatibility-range allowance at the end. Eleven thousand site collections, all in 15 mode by week twelve.

Classic to claims: the conversion nobody budgets for

The 2010 farm was classic-mode Windows authentication. The 2013 target is claims. There are two supported sequences and we've used both: convert the web application to claims on the 2010 farm before migrating (2010 SP1+ supports claims), or attach the classic databases to a 2013 claims web application and convert there with Convert-SPWebApplication:

Convert-SPWebApplication -Identity https://portal.agency.example `
    -To Claims -RetainPermissions

We converted on the 2013 side. Either way, the conversion rewrites user identities — DOMAIN\jsmith becomes the claims-encoded form — across every site's permission entries. Things that break when this goes wrong, all of which we've now personally witnessed: alerts stop firing because the subscription identity no longer matches; "checked out to" shows a user who can no longer check the file back in; custom code that compares login names with string equality silently fails; and third-party web parts that stored DOMAIN\user in their own lists show empty values. -RetainPermissions handles the SharePoint-owned ACLs. It does not handle the identity strings your customizations squirreled away. Add "search the customization inventory for login-name string handling" to the audit checklist — it's a one-day check that saves a week of post-cutover forensics.

Also in this bucket: re-crawl everything after conversion. Security trimming in search keys off the claims identity, and until the full crawl completes, users will report "search lost my documents." It didn't; the index just doesn't know who they are yet.

What we'd tell our week-one selves

Zero data loss on this engagement — the database-attach method, rehearsed properly, is genuinely safe because the source databases are never modified once you work from copies and read-only sources. The schedule risk is never the attach. Ranked by pain actually experienced: dead-vendor customizations, the claims conversion's long tail, orphaned site collections, SQL log sizing, and — dead last — the upgrade itself. Test the databases in week one. Rehearse twice with timing. Put a public deadline on 14 mode. And write the farm build as a script, because in eighteen months somebody will ask you exactly how that farm was configured, and "it's in the script" is the only answer that ages well.

If you're facing this

We've run this exact playbook across government and enterprise SharePoint estates, alongside the Exchange and directory work that usually travels with it, without losing a document yet. If you've got a 2010 farm and a mandate to move — to 2013, or eventually further — we're happy to compare notes on your Test-SPContentDatabase output before you commit to a timeline. Get in touch; the first conversation is just two teams talking shop.

// related notes
// still relevant?

Facing a migration, platform, or AI build like this one?

This note is part of an archive spanning a decade of infrastructure work. The playbook evolved; the discipline didn't. Tell us what you're trying to ship — we reply within one business day.

Start a project →

← Back to notes