Content Migration - Export and Import

Script

Name

Description

Last Modified Date

Download

WebExport.ps1

Exports existing content. 

31/01/2012

WebExport.ps1

WebImport.ps1Imports content. Creates sub webs.  Fixes taxonomy field ids.31/01/2012WebImport.ps1

This script has not been tested with SharePoint 2013

 

WebExport.ps1

This script will enumerate all the sub webs within a site collection.  It will export individual libaries into seperate .cmp export files.  It will also generate a manifest.xml file which contains all required information to import into another environment using the WebImport.ps1.

Script

#Script: WebExport.ps1
#Source: https://codemonkeysoftware.atlassian.net
param (
        $SiteCollectionUrl = $(throw "Please specify parameter SiteCollectionUrl"),
        $exportFolder = "C:\ContentExport",
        $RootWebLibraries = "PublishingImages,Documents,SiteCollectionDocuments,SiteCollectionImages,Pages",
        $SubWebLibraries = "Calendar,Documents,Images,Pages",
        $deleteExportFolder = $true
)

if($RootWebLibraries -ne $null)
{
    $RootWebItemsToExport = $RootWebLibraries.Split(',')
}
if($SubWebLibraries -ne $null)
{
    $SubWebItemsToExport  = $SubWebLibraries.Split(',')
}

#region IO Functions

function Get-ScriptDirectory 
{ 
    $Invocation = (Get-Variable MyInvocation -Scope 1).Value 
    Split-Path $Invocation.MyCommand.Path
} 
function Set-Directory($location)
{
    #[Environment]::CurrentDirectory = Get-ScriptDirectory
    [IO.Directory]::SetCurrentDirectory((Convert-Path($location)))
}
#endregion

#region SharePoint Snappin Setup

cls
$snapin="Microsoft.SharePoint.PowerShell"
if (get-pssnapin $snapin -ea "silentlycontinue") {
    write-host -f Green "PSsnapin $snapin is loaded"
}
else {
    if (get-pssnapin $snapin -registered -ea "silentlycontinue") {
        write-host -f Green "PSsnapin $snapin is registered"
        Add-PSSnapin $snapin
        write-host -f Green "PSsnapin $snapin is loaded"
    }
    else {
        write-host -f Red "PSSnapin $snapin not found"
    }
}
#endregion

#region Host Feedback Helpers

$logBuffer = New-Object System.Text.StringBuilder

function WriteToLog($msg, $msg2)
{
[Void] $logBuffer.AppendLine(($msg + " " + $msg2))
}

function WriteErrorToLog($msg, $msg2)
{
[Void] $logBuffer.AppendLine()
[Void] $logBuffer.AppendLine("ERROR:")
[Void] $logBuffer.AppendLine(($msg + " " + $msg2))
[Void] $logBuffer.AppendLine()
}

function WriteSuccess($msg, $msg2)
{
    Write-Host -ForegroundColor Green $msg $msg2
    WriteToLog $msg $msg2
}
function WriteFailure($msg, $msg2)
{
    Write-Host -ForegroundColor Red "ERROR: " $msg $msg2
    WriteErrorToLog ($msg, $msg2)
}
function WriteInfo($msg, $msg2)
{
    Write-Host "    -" $msg $msg2
    WriteToLog $msg $msg2
}
function WriteWarning($msg, $msg2)
{
    Write-Warning ("    -" + $msg + " " + $msg2)
}
function WriteInfoNoLine($msg, $msg2)
{
    Write-Host "    -" $msg $msg2 -NoNewline
    WriteToLog $msg $msg2
}

function Verify-Admin
{
    ## Check if the script is running under elevated administrator privileges
    $CurrentIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
    $CurrentPrincipal = New-Object System.Security.Principal.WindowsPrincipal($CurrentIdentity)

    if (!$CurrentPrincipal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator))
    {
          Write-Host -ForegroundColor Red "- This script should be executed under elevated administrative privileges."
          Write-Host -ForegroundColor Red "- Use ""Run as Administrator"" to execute PowerShell before running this script."
          #Read-Host "Press [Enter] key to exit"
          break
    }
}
Verify-Admin
#endregion

function AddNode([xml] $manifest, $parent, $name, $value)
{
    $node = $manifest.CreateElement($name)
    $node.InnerText = $value
    return $parent.AppendChild($node)
}
function AddAttribute([xml] $manifest, $parent, $name, $value)
{
    $node = $manifest.CreateAttribute($name)
    $node.Value = $value
    $parent.Attributes.Append($node) | Out-Null
}
function GetList($web, $listUrl)
{
    try
    {
        $list = $null    
        $list = $web.GetList($listUrl)
        if($list -ne $null) {return $list}
    }
    catch{    }
    try
    {
        $list = $null    
        $list = $web.Lists.TryGetList($listUrl.Replace("/",""))
        if($list -ne $null) {return $list}
    }
    catch{    }
    return $null
}

function ExportWeb($web, $items, $exportFolder, [xml] $manifest, $manifestParent)
{
    Write-Host "Processing Web: " $web.Url
    foreach($item in $items)
    {
        if([string]::IsNullOrEmpty($item)) { continue }
        
        $exportFileName = "{0}.{1}.{2}.cmp" -f $exportSetName, $web.Id, $item
        $exportFileName = $exportFileName.Replace("/","")
        $exportFullPath = "{0}\{1}" -f $exportFolder, $exportFileName
        
        try
        {

            $list = GetList $web $item
#            if($list -eq $null) 
#            {
#                Write-Host ("No file {1} at {0}" -f $web.Url, $item)
#            }
            if($list -ne $null)
            {
                if($web.ServerRelativeUrl.EndsWith("/"))
                {
                    $url = "{0}{1}" -f $web.ServerRelativeUrl, $list.RootFolder
                }
                else
                {
                    $url = "{0}/{1}" -f $web.ServerRelativeUrl, $list.RootFolder
                }
                Write-Host "Exporting Item: $url" 
                $web | Export-SPWeb -ItemUrl $url -Path $exportFullPath -HaltOnError -IncludeUserSecurity:$false -IncludeVersions:CurrentVersion -Force 
                if($?)
                {
                    WriteSuccess "Created content package $exportFileName"
                    $node = AddNode $manifest $manifestParent "ContentFile" $exportFileName
                    AddAttribute $manifest $node "Url" $web.ServerRelativeUrl
                    AddAttribute $manifest $node "Title" $web.Title
                    AddAttribute $manifest $node "WebTemplate" $web.WebTemplate
                    AddAttribute $manifest $node "Configuration" $web.Configuration
                    AddAttribute $manifest $node "Description" $web.Description
                }
            }
        }
        catch
        {
            Write-Warning $_
        }
    
    }
}
function Export($siteCol, $exportSetName, $exportFolder, $RootWebItemsToExport, $SubWebItemsToExport)
{
    if(![System.IO.Directory]::Exists($exportFolder))
    {
        [System.IO.Directory]::CreateDirectory($exportFolder)
    }
    [Environment]::CurrentDirectory = $exportFolder
    Set-Directory $exportFolder
    [xml] $manifest = New-Object xml 

    $rootNode = $manifest.CreateNode([System.Xml.XmlNodeType]::Element, "Content", "")
    $manifest.AppendChild($rootNode) | Out-Null

    
    if($siteCol -eq $null)
    {
        throw "Unable to locat site collection $SiteToExportUrl"
    }
    foreach($web in $siteCol.AllWebs)
    {
        try
        {
            if($web.IsRootWeb)
            {
                
                ExportWeb $web $RootWebItemsToExport $exportFolder $manifest $rootNode
            }
            else
            {
                ExportWeb $web $SubWebItemsToExport $exportFolder $manifest $rootNode
            }
        }
        finally
        {
            Invoke-Command -ScriptBlock { $web.Dispose() } -ErrorAction SilentlyContinue
        }
    }

    #Save data
    $manifestFileName = "{0}\{1}.xml" -f $exportFolder, $exportSetName 
    $manifest.Save($manifestFileName)
    WriteSuccess "Manifest file written to $manifestFileName"
}

if($deleteExportFolder)
{
    #Delete existing items
    remove-item -path "C:\ContentExport\*.*" -Force -ErrorAction:SilentlyContinue
}
$siteCol = get-spsite -Limit All | where-object {$_.Url -ieq $SiteCollectionUrl}
if($siteCol -ne $null)
{
    Export  $siteCol $siteCol.RootWeb.Title $exportFolder $RootWebItemsToExport $SubWebItemsToExport
    remove-item -path "C:\ContentExport\*.log" -Force
}
else
{
    throw 'Unable to locate site ' + $SiteCollectionUrl
}

Usage

.\WebExport.ps1 http://myportal.url C:\ContentExport 	#Exports using defaults
#or
.\WebExport.ps1 http://myportal.url C:\ContentExport "SiteCollectionDocuments,SiteCollectionImages" "Documents" #Exports 2 libraries from the rootweb and 1 library from each sub web if they exist

 

WebImport.ps1

This script reads in an xml file that is generated by the WebExport.ps1 script.  This manifest contains information about all the packages to import and some values about each SPWeb.  If the SPWeb has not yet been created in the destination site collection this script will create it prior to importing the individual libraries.

This script will also clean up taxonomy fields that have been migrated from another environment. 

For this to work you must have valid content types in the site collection.  (The values from the content type fields will be used to refresh the list instance fields)

 

 

Script

#Script: WebImport.ps1
#Source: https://codemonkeysoftware.atlassian.net
#notes: Run this script in Administrator mode.
#        This script will import content packages gerneated by WebExport.ps1
#        Example invocation: http://portal.com "My Portal.xml" C:\ContentExport
param
(
    [string]$destinationSiteUrl = $(Read-Host -Prompt "Please enter the URL of the destination site eg; http://myserver"),
    [string]$importManifest = $(Read-Host -Prompt "Please enter the content set name (The content manifest xml file) eg; MySet.xml"),
    [string]$importFolder = "C:\ContentExport"
)
$contentSetName = $importManifest
#region IO Functions

function Get-ScriptDirectory 
{ 
    $Invocation = (Get-Variable MyInvocation -Scope 1).Value 
    Split-Path $Invocation.MyCommand.Path
} 
function Set-Directory($location)
{
    #[Environment]::CurrentDirectory = Get-ScriptDirectory
    [IO.Directory]::SetCurrentDirectory((Convert-Path($location)))
}
#endregion

#region SharePoint Snappin Setup

$snapin="Microsoft.SharePoint.PowerShell"
if (get-pssnapin $snapin -ea "silentlycontinue") {
    write-host -f Green "PSsnapin $snapin is loaded"
}
else {
    if (get-pssnapin $snapin -registered -ea "silentlycontinue") {
        write-host -f Green "PSsnapin $snapin is registered"
        Add-PSSnapin $snapin
        write-host -f Green "PSsnapin $snapin is loaded"
    }
    else {
        write-host -f Red "PSSnapin $snapin not found"
    }
}
#endregion

#region Host Feedback Helpers

function WriteSuccess($msg, $msg2)
{
    Write-Host -ForegroundColor Green $msg $msg2
}
function WriteFailure($msg, $msg2)
{
    Write-Host -ForegroundColor Red "ERROR: " $msg $msg2
}
function WriteInfo($msg, $msg2)
{
    Write-Host "    -" $msg $msg2
}
function WriteWarning($msg, $msg2)
{
    Write-Warning ("    -" + $msg + " " + $msg2)
}

function WriteInfoNoLine($msg, $msg2)
{
    Write-Host "    -" $msg $msg2 -NoNewline
}

#endregion

#region Xml Content Manifest

function LoadConfiguration($configFile)
{
    if(![IO.File]::Exists($configFile))
    {
        throw "Could not find configuration file: $configFile. Script Terminated."
    }
    WriteInfo "Found configuration file: $configFile"
    try
    {
        $config = [xml] (get-content -Path $configFile)
        WriteSuccess "Content Manifest Loaded Successfully $configFile"
        return $config.Content
    }
    catch
    {
        WriteFailure "Manifest file found but could not be loaded. May not be valid XML"
        throw
    }
}

#endregion

#region Data Clean Up
# When content in imported into a new environment the Term Store ID is kept on the
#taxonomy fields.  This causes a problem as the new term store has a different id.
#These functions clean up the taxonomy list which has bad entries following an import
#and modify all the items and resets the correct values.
function CleanTaxonomyList($site)
{
    Write-Host "Cleaning /Lists/TaxonomyHiddenList"
    $web = $site.RootWeb
    $taxlist =  $web.GetList("Lists/TaxonomyHiddenList")
    for($count = $taxlist.Items.Count - 1; $count -ge 0; $count--)
    {
         $item = $taxlist.Items[$count]
        if([string]::IsNullOrEmpty($item.Title))
        {
            $item.Delete()
            if($?)
            {
                #Write-Host "Deleted Item with bad title"
            }
            else
            {
                Write-Host -ForegroundColor Red "Failed to deleted Item with bad title"
            }
        }
        
    }
}
#This function will reset the term store id if it is wrong, and will fix the WssId.
#If the correct term cannot be located, the default one will be used.
function ResetTaxonomyDefaults($site, [Microsoft.SharePoint.Publishing.PublishingWeb] $web)
{
    $txs = New-Object "Microsoft.SharePoint.Taxonomy.TaxonomySession" -ArgumentList $site
    $pages = $web.GetPublishingPages()
    foreach($page in $pages)
    {
        Write-Host -ForegroundColor Cyan "Checking publishing page " $page.Title
        if($page.ListItem.File.CheckOutStatus -ne "None")
        {
            $page.CheckIn("Checked in by data clean process");
        }
        $page.CheckOut();
        foreach ($field in $page.ListItem.Fields)
        {
            if($field.GetType().Name -eq "TaxonomyField")
            {
                $taxField = [Microsoft.SharePoint.Taxonomy.TaxonomyField] $field
                
                #Write-Host "Found field to update:" $taxField.Title
                $currentValue = $page.ListItem.Properties[$taxField.InternalName]
                #Write-Host "Current Value is" $currentValue
                $templateField = $page.ListItem.ParentList.ParentWeb.Site.RootWeb.Fields[$field.Id]
                $defaultValue = $templateField.DefaultValue
                $termStore = $txs.TermStores[$templateField.SspId]
                $termSet = $termStore.GetTermSet($templateField.TermSetId)
                if($taxField.SspId -ne $templateField.SspId)
                {
                #    Write-Host "TaxField SspId is not correct, updating"
                    $taxField.SspId = $templateField.SspId
                    $taxField.Update()
                }
                if($taxField.TermSetId -ne $templateField.TermSetId)
                {
                #    Write-Host "TaxField TermSetId is not correct, updating"
                    $taxField.TermSetId = $templateField.TermSetId
                    $taxField.Update()
                }
                $fieldValue = $templateField.GetFieldValue($currentValue)
                if(($fieldValue.GetType().Name -eq "TaxonomyFieldValue") -and  ($fieldValue -eq $null -or [string]::IsNullOrEmpty($fieldValue.TermGuid)))
                {
                    $fieldValue = $templateField.GetFieldValue($defaultValue)
                }
                if(($fieldValue.GetType().Name -ne "TaxonomyFieldValue") -and  ($fieldValue[0] -eq $null -or [string]::IsNullOrEmpty($fieldValue[0].TermGuid)))
                {
                    $fieldValue = $templateField.GetFieldValue($defaultValue)
                }
                if($fieldValue.GetType().Name -eq "TaxonomyFieldValue")
                {
                    try
                    {
                        $term = $termSet.GetTerm($fieldValue.TermGuid)
                    }
                    catch
                    {
                        Write-Host -red ("Failed to update field {0} for page {1} in web {2}"  -f $taxField.InternalName, $page.Title, $web.Url)
                    }
                }
                else
                {
                    try
                    {
                        $term = $termSet.GetTerm($fieldValue[0].TermGuid)
                    }
                    catch
                    {
                        Write-Host -red ("Failed to update field {0} for page {1} in web {2}"  -f $taxField.InternalName, $page.Title, $web.Url)
                    }
                }
                $taxField.SetFieldValue($page.ListItem, $term)
            }
        }
        $page.ListItem.Update()
        $page.CheckIn("Data clean process")
    }

}
function CleanSiteCollection ($siteColUrl)
{
    [Microsoft.SharePoint.SPSite] $site = get-spsite -Limit ALL | where-object {$_.Url -ieq $siteColUrl}
    CleanTaxonomyList $site
    $site | Get-SPWeb -limit all | ForEach-Object {
            #Check to see if site is a publishing site
            if ([Microsoft.SharePoint.Publishing.PublishingWeb]::IsPublishingWeb($_)) 
            {
                Write-Host "Cleaning pages in `"$($_.Title)`" site."
                #Get the Publishing Web and pages within it
                $publishingWeb = [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($_)
                ResetTaxonomyDefaults $site $publishingWeb    
            }
            $_.Dispose()
        }
    $site.Dispose()
}

#endregion

function Import($siteColUrl, $manifest, $importFolder)
{
    $siteCol = get-spsite -Limit ALL | where-object {$_.Url -ieq $siteColUrl}
    if($siteCol -eq $null)
    {
        throw "Unable to locate " + $siteColUrl
    }
    $manifest = LoadConfiguration ($importFolder + "\" + $manifest)
    $importFiles = $manifest.ContentFile
    foreach($node in $importFiles)
    {
        $filename = ($importFolder + "\" + $node.InnerText)
        $url = $node.Url
        $fullUrl = ($siteCol.Url + $url)
        $web = $siteCol | Get-SPWeb -Limit ALL | Where-Object {$_.ServerRelativeUrl -ieq $url}
        if($web -eq $null)
        {
            WriteInfo "Creating new web at url $url"
            try
            {
                            
                $template = Get-SPWebTemplate ($node.WebTemplate + "#" + $node.Configuration)
                if($template -eq $null)
                {
                    throw "Template " + ($node.WebTemplate + "#" + $node.Configuration) + " does not exist in this farm"
                }
                $web = New-SPWeb -Template $template -Url $fullUrl -Name $node.Title -Description $node.Description
            }
            catch
            {
                WriteFailure "Failed to import content at location $url  - " $_
                continue
            }
            WriteInfo "New web created at url $url"
        }
        try
        {
            $web | Import-SPWeb -Path $filename -Force -ActivateSolutions:$false -IncludeUserSecurity:$true -UpdateVersions:Overwrite
            WriteSuccess "Imported $filename to " $web.Url
        }
        catch
        {
            WriteFailure "Error importing file $filename to web $web for site $siteCol" $_
        }
        finally
        {
            $web.Dispose()
        }
    }
}

Import $destinationSiteUrl $contentSetName $importFolder
CleanSiteCollection $destinationSiteUrl

Usage

 .\WebImport.ps1 http://destinationportal "Your Manifest.xml" c:\contentexport  #reads the manfiest from the contentexport folder and imports the files to the destination site collection

CodeMonkey Software is a division of JCHMedia www.jchmedia.com