Tuesday, 20 March 2012

Sending SharePoint system notification e-mails using PowerShell

The script in this article demonstrates an example of how you could use PowerShell to send system notification e-mails to administrators on a scheduled basis. Of course, there are a number of ways to do this sort of thing, including SharePoint workflows, Microsoft SCOM, and custom code. I’m not saying PowerShell is necessarily better than these methods, but I like the flexibility it brings, and as you have full access to the Object Model, you could send notifications from all sorts of areas – for example, successes and errors from the crawl logs, information from the User Profile service application, timer jobs with last run times, list items, site users, etc.

The example I have chosen here is to send an HTML e-mail listing the currently active items from the SharePoint Health Analyzer. This information is effectively just stored in a SharePoint list on the Central Administration site, so I will need to access the list, query the active items and send this information using the SMTP mail server and reply addresses specified in the farm outbound e-mail settings.

HealthAnalyzerScreenshot

First of all we need to get the Central Administration web application and site objects from the farm. Note that we do not need to specify any URLs here - The command will automatically find Central Administration using options provided in the SharePoint 2010 cmdlets:

#Get Central Admin Web Application and Web objects
$caWebApp = (Get-SPWebApplication -IncludeCentralAdministration) | ? { $_.IsAdministrationWebApplication -eq $true }
$caWeb = Get-SPWeb -Identity $caWebApp.Url

Next we can specify the user or group e-mail address that will receive the message, followed by the sender’s e-mail address and the name of the SMTP server sending the message, which I am taking from the farm outbound e-mail settings stored in the Central Administration web application. If you want to send the message to more than one user or group, you could set up an array and walk through each item.

#Set up from, to and server addresses
$toAddress = "svc_installsp2010@domain2010.lab"
$fromAddress = $caWebApp.OutboundMailReplyToAddress
$serverAddress = $caWebApp.OutboundMailServiceInstance.Server.Address

The following part of the script gets the Health Analyzer list (internal name “HealthReports”) and the default display form URL. We need this URL later when configuring hyperlinks in the HTML, so that users can click on a health report directly from the e-mail to show it in the browser.

#Get Health Analyzer list on Central Admin site
$healthList = $caWeb.GetList("\Lists\HealthReports")
$displayFormUrl = $caWeb.Url + ($healthList.Forms | where { $_.Type -eq "PAGE_DISPLAYFORM" }).ServerRelativeUrl

This is probably the trickiest part of the script as it uses a CAML query to find all items in the Health Analyzer, except for those with the Severity column set to “4 – Success”. In other words, it queries any item that the Health Analyzer considers to be a problem on the farm. The CAML itself is specified in the first line below – I’m not going to go into how to write this stuff as there are quite a number of references available on the Internet already.

$queryString = "<Where><Neq><FieldRef Name='HealthReportSeverity' /><Value Type='Text'>4 - Success</Value></Neq></Where>"
$query = New-Object Microsoft.SharePoint.SPQuery
$query.Query = $queryString
$items = $healthList.GetItems($query)

This next section creates the subject and HTML body for the e-mail message. If you know HTML then you will see what is happening, and if you don’t then that’s another subject for you to learn! The key part from a SharePoint perspective is the foreach ($item in $items) routine, as this is where the script walks through each list item found from the query above and injects the column values into the HTML. Note where the $displayFormUrl variable is used from earlier to set up the URL used for the hyperlink. I’m also using the built-in ConvertTo-Html PowerShell cmdlet to format the HTML.

#Set up e-mail message subject and HTML body
$msgTitle = "Health Analyzer results for farm " + $caWebApp.Farm.Name + " - " + (Get-Date)
#HTML head
$head = "<style type=`"text/css`">.tableStyle { border: 1px solid #000000; }</style>"
$head = $head + "<Title>$msgTitle</Title>"
#Create HTML body by walking through each item and adding it to a table
$body = "<H2>$msgTitle</H2><table cellspacing=`"0`" class=`"tableStyle`" style=`"width: 100%`">"
foreach ($item in $items)
{
    $itemUrl = $displayFormUrl + "?id=" + $item.ID
    [array]$itemValues = @($item["Severity"], $item["Category"], $item["Explanation"], $item["Modified"])
    $body = $body + "<tr>"
    $body = $body + "<td class=`"tableStyle`"><a href=`"" + $itemUrl + "`">" + $item.Title + "</a></td>"
    $itemValues | ForEach-Object {
        $body = $body + "<td class=`"tableStyle`">$_</td>"
    }
    $body = $body + "</tr>"
}
$body = $body + "</table>"
#Create message body using the ConvertTo-Html PowerShell cmdlet
$msgBody = ConvertTo-Html -Head $head -Body $body

Finally, the commands below use the System.Net.Mail .NET class to send the e-mail, using the IsBodyHtml boolean property to ensure it goes as an HTML message.

#Create e-mail message object using System.Net.Mail class
$msg = New-Object System.Net.Mail.MailMessage($fromAddress, $toAddress, $msgTitle, $msgBody)
$msg.IsBodyHtml = $true
#Send message
$smtpClient = New-Object System.Net.Mail.SmtpClient($serverAddress)
$smtpClient.Send($msg)
$caWeb.Dispose()

That’s it! The e-mail will be sent when the script is run and should look similar to the one shown below. As you can see, the items in the e-mail match up to the items shown in the Health Analyzer screenshot at the beginning of this article.

HealthAnalyzerEmail

You can use Task Scheduler in Windows if you want to send the e-mail on a scheduled basis. Please see this article for more details on how to use Task Scheduler for running PowerShell scripts with SharePoint.

Sunday, 26 February 2012

Managing, creating and deleting SharePoint list views with PowerShell

Due to the number of configuration options available, manipulating list views through PowerShell can be quite involved and I certainly wouldn’t recommend it if you only have a few lists to change. However, when there are a large number of views and lists involved, PowerShell can significantly reduce the amount of time needed to manage and configure them.

Manage List Views

The first stage is looking at the commands to manage available views on a list. For this example, I am going to get the “Shared Documents” document library from a root site and assign it to a variable called $list:

$web = Get-SPWeb http://portal
$list = $web.GetList(($web.ServerRelativeUrl.TrimEnd("/") + "/Shared Documents"))

To find the title and URL of the current default view, type the following command:

$list.DefaultView | select Title, Url

DefaultView

If you want to show one of the non-default views then you can call it with the display name, as follows:

$list.Views["Test View"]

To change the default view for the list, type the following set of commands for the view you want to configure as the new default:

$view = $list.Views["Test View"]
$view.DefaultView = $true
$view.Update()

Create New List Views

The easiest way to create a list view using PowerShell is to create one in the UI first with all the settings you need. You can then use the properties of the view you have created to define the parameters required for the view creation script we use later in this section.

For this example, I have created a new view called “Sort by modified date”, which is effectively the same as the default “All Documents” view but with the items ordered by modified date in descending order. I have also added the “File Size” column to the view and specified a limit of 50 items per page.

Once this is done, type the following set of commands to display the properties of the new view:

$web = Get-SPWeb http://portal
$list = $web.GetList(($web.ServerRelativeUrl.TrimEnd("/") + "/Shared Documents"))
$newview = $list.Views["Sort by modified date"]
$newview

ViewPropertiesAndValues

You should now see a long list of property names and associated values for this view, including Title, Query, RowLimit and ViewFields (i.e., columns). You can also take a look at the value of any one of these properties individually by typing $newview.PropertyName and hitting Enter. For example, to get a full list of ViewFields, type $newview.ViewFields.

When creating the view in PowerShell, you will need to use some of these properties as mandatory parameters in the script. These mandatory properties are listed below:

Property Name Description How it looks in the UI
Title Display name of the view in the UI image
ViewFields The columns you wish to show in the view image
Query The CAML query used to sort and filter list items in the view image
RowLimit The number of items shown per page image
Paged Used with the RowLimit to either display the number of items in batches ($true) or limit the number of items shown in total ($false) image
DefaultView The default view for the list being configured ($true or $false) image

You can now start writing the script to create the view in another list. The example below creates the view with the parameters shown in the UI examples above into the Shared Documents library on the site http://portal/sites/testsite. The values were simply copied from the property list displayed using the script earlier in this section:

#Get destination site and list
$web = Get-SPWeb "http://portal/sites/testsite"
$list = $web.GetList(($web.ServerRelativeUrl.TrimEnd("/") + "/Shared Documents"))

$viewTitle = "Sort by modified date" #Title property
#Add the column names from the ViewField property to a string collection
$viewFields = New-Object System.Collections.Specialized.StringCollection
$viewFields.Add("DocIcon") > $null
$viewFields.Add("LinkFilename") > $null
$viewFields.Add("Modified") > $null
$viewFields.Add("Editor") > $null
$viewFields.Add("FileSizeDisplay") > $null
#Query property
$viewQuery = "<OrderBy><FieldRef Name='Modified' Ascending='FALSE'/></OrderBy>"
#RowLimit property
$viewRowLimit = 50
#Paged property
$viewPaged = $true
#DefaultView property
$viewDefaultView = $false

#Create the view in the destination list
$newview = $list.Views.Add($viewTitle, $viewFields, $viewQuery, $viewRowLimit, $viewPaged, $viewDefaultView)
Write-Host ("View '" + $newview.Title + "' created in list '" + $list.Title + "' on site " + $web.Url)
$web.Dispose()

You may have a requirement to add this list view to the “Shared Documents” library on every site in a site collection. To do this, we need to wrap the Add method in a ForEach-Object condition for every site in a specified site collection. This script will also report an issue if the Shared Documents library does not exist on one or more sites:

#Title property
$viewTitle = "Sort by modified date"
#Add the column names from the ViewField property to a string collection
$viewFields = New-Object System.Collections.Specialized.StringCollection
$viewFields.Add("DocIcon") > $null
$viewFields.Add("LinkFilename") > $null
$viewFields.Add("Modified") > $null
$viewFields.Add("Editor") > $null
$viewFields.Add("FileSizeDisplay") > $null
#Query property
$viewQuery = "<OrderBy><FieldRef Name='Modified' Ascending='FALSE'/></OrderBy>"
#RowLimit property
$viewRowLimit = 50
#Paged property
$viewPaged = $true
#DefaultView property
$viewDefaultView = $false

#Get the site collection
Get-SPSite "http://portal/sites/testsite" | Get-SPWeb -Limit All | ForEach-Object {
    $webUrl = $_.Url
    try
    {
        #Get the Shared Documents library
        $list = $_.GetList(($_.ServerRelativeUrl.TrimEnd("/") + "/Shared Documents"))
       
        #Create the view in the destination list
        $newview = $list.Views.Add($viewTitle, $viewFields, $viewQuery, $viewRowLimit, $viewPaged, $viewDefaultView)
        Write-Host ("View '" + $newview.Title + "' created in list '" + $list.Title + "' on site " + $webUrl)
    }
    catch
    {
        Write-Host ("There was a problem trying to create the view in the site " + $webUrl + ": " + $_)
    }
}

By the way, if you create a new view with the same title as one that already exists on a list, it will create an extra view in that list with the same title – not overwrite the previous one.

Modify Existing List Views

The scripts above create list views with a limited set of mandatory properties, but there are probably some extra properties that you will also want to set on views. As mentioned earlier, to help you decide which settings you need to configure on a view using PowerShell, create a view in the UI first and list its properties and values by running this script:

$web = Get-SPWeb http://portal
$list = $web.GetList(($web.ServerRelativeUrl.TrimEnd("/") + "/Shared Documents"))
$newview = $list.Views["Sort by modified date"]
$newview

For a full description of the properties and methods available for configuring views, please see this article on MSDN. Once you have decided which properties you wish to set, look up the values of the view you created in the UI and use this script to modify a view’s settings using PowerShell. In this example, we will be changing the “Sort by modified date” view configured on the Shared Documents library in site http://portal/sites/testsite so that it can be accessed as a mobile view and is set as the default mobile view for the list:

#Get the site and list
$web = Get-SPWeb "http://portal/sites/testsite"
$list = $web.GetList(($web.ServerRelativeUrl.TrimEnd("/") + "/Shared Documents"))
#Get the list view to be changed
$newview = $list.Views["Sort by modified date"]
#Set the mobile and default mobile view properties
$newview.MobileView = $true
$newview.MobileDefaultView = $true
#Update the view configuration
$newview.Update()
$web.Dispose()

Delete List Views

Finally, here is a script to delete a list view. The obvious thing to mention here is being careful not to delete the default view on a list, or if you want to, ensure that you change the default view to another one first before doing so. The name of the view we are going to delete in this example is “Sort by modified date” in the Shared Documents library on site http://portal/sites/testsite:

#Get the site and list
$web = Get-SPWeb "http://portal/sites/testsite"
$list = $web.GetList(($web.ServerRelativeUrl.TrimEnd("/") + "/Shared Documents"))
#Get the list view to be changed
$newview = $list.Views["Sort by modified date"]
#Delete this view from the list
$list.Views.Delete($newview.ID)
$web.Dispose()

Thursday, 16 February 2012

Resetting the SharePoint 2010 search index using PowerShell

The script here resets the SharePoint 2010 search index, which I have posted as there is no out-of-the-box cmdlet for doing this. This effectively does the same thing as the Index Reset option for the search service application in Central Administration:

ResetIndex

Note there are two parameters you can specify in the Reset method:

  • Disable alerts: $true if the Search alerts should be disabled during reset; otherwise, $false. Selecting $true here is the same as ticking the “Deactivate search alerts during reset” checkbox shown in the screenshot above.
  • Ignore unreachable server: $true if the failures to connect to servers are ignored; otherwise, $false.

In the script example I have added below, both options are set to $false. Note that running this script will permanently delete all crawled data in the search service application – you will need to re-crawl your content sources to get it back.

#Get the search service application
#You will need to specify the -Identity switch and a name if you have more than one
$sa = Get-SPEnterpriseSearchServiceApplication

#Reset index with the following options in brackets:
#Disable Alerts $true/$false
#Ignore unreachable server $true/$false
try
{
    Write-Host "Attempting to reset the index...please wait"
    $sa.Reset($false, $false)
    Write-Host "Index successfully reset" -ForegroundColor Blue
}
catch
{
    Write-Host "There was a problem resetting the index:" $_ -ForegroundColor Red
}

Tuesday, 17 January 2012

Perform an IISRESET on multiple servers using PowerShell

This is quite a quick script, but very useful if you need to restart IIS on multiple servers in one hit. The only pre-requisite is to ensure you have local administrator permissions on each server to perform the IISRESET.

#Specify servers in an array variable
[array]$servers = "Server1","Server2","Server3","Server4"
#Step through each server in the array and perform an IISRESET
#Also show IIS service status after the reset has completed
foreach ($server in $servers)
{
    Write-Host "Restarting IIS on server $server..."
    IISRESET $server /noforce
    Write-Host "IIS status for server $server"
    IISRESET $server /status
}
Write-Host IIS has been restarted on all servers

To use the script, simply replace the server names with those from your SharePoint farm and run on one of the servers.

The script will also output the status of all IIS related services from each server using the IISRESET /status switch.

Thursday, 8 December 2011

Manage services on SharePoint 2010 servers using PowerShell

As you may already know, you can use the “Manage Services on Server” option from Central Administration to centrally start and stop service instances across all SharePoint servers in your farm. An example of this administration page is shown below:

image

The issue with this page is that it can be cumbersome to use if there are a few servers in your farm and you keep having to click the “Server” drop-down to select one of them. It is also not very repeatable and there may be issues where you might want to automate the process – for example, stopping the User Profile Synchronization Service before a backup or application update and restarting it once complete.

You can get a similar list to the one shown in Central Admin using PowerShell by typing the following command:

Get-SPServiceInstance -Server PAC-SP2010 | sort TypeName | Format-Table -AutoSize

This will not only provide the service name and status, but also the service instance GUID:

image

There are a couple of ways to stop a service instance. Firstly, the most user friendly method, which is to use the service display name and then call the Stop-SPServiceInstance cmdlet:

Get-SPServiceInstance -Server PAC-SP2010  | where-object {$_.TypeName -eq "Managed Metadata Web Service" } | Stop-SPServiceInstance -Confirm:$false

There are a couple of things to note here:

  • The -Server switch is not required if you are configuring service instances for the server you are currently logged into
  • The -Confirm:$false switch is also optional. Setting this to $false supresses the confirmation prompt that appears when attempting to stop the service instance through PowerShell
  • You can also use the Stop-SPServiceInstance cmdlet directly by specifying the service instance GUID, as shown below for the Managed Metadata Web Service on this farm:

    Stop-SPServiceInstance -Identity 3f36d0f4-ce39-48aa-a776-6300f1e2b58f -Confirm:$false

    image

    Restarting the service is simply a case of typing either of the same commands as before, but this time replacing Stop-SPServiceInstance with Start-SPServiceInstance.

    First example:

    Get-SPServiceInstance -Server PAC-SP2010  | where-object {$_.TypeName -eq "Managed Metadata Web Service" } | Start-SPServiceInstance -Confirm:$false

    Second example:

    Start-SPServiceInstance -Identity 3f36d0f4-ce39-48aa-a776-6300f1e2b58f -Confirm:$false

    That’s great, but you also might want to check a service instance has a particular status before attempting to stop or start it. In the example below, the Managed Metadata Web Service instance is associated with the $serviceinstance variable and the Status is checked to make sure it is “Online” before attempting to stop the service instance:

    $serviceinstance = Get-SPServiceInstance -Server PAC-SP2010  | where-object {$_.TypeName -eq "Managed Metadata Web Service" }
    if ($serviceinstance.Status -eq "Online")
    {
        Write-Host "Stopping Managed Metadata Web Service on PAC-SP2010"
        try
        {
            $serviceinstance | Stop-SPServiceInstance -Confirm:$false
        }
        catch { Write-Host "There was an error:" $_ }
    }
    else
    {
        Write-Host "Managed Metadata Web Service on PAC-SP2010 has already been stopped"
    }

    Wednesday, 2 November 2011

    Use PowerShell to check for illegal characters before uploading multiple files into SharePoint

    If you have done any sort of bulk file uploading into SharePoint, you will be aware of issues with file names containing illegal characters. These files can disrupt the uploading process, potentially causing many hours of frustrating and time consuming tasks examining and repairing file names.

    Files and folders are blocked by SharePoint during the uploading process for the following reasons:

    • They contain the following characters: & { } ~ # % (there are other illegal characters too, but as they are also blocked from use in Windows Explorer, it is assumed you will not have files named with these characters in your file system – if you do, you can adapt the script accordingly)
    • They are 128 characters in length or over
    • They start with a period character
    • They end with a period character
    • They contain consecutive period characters

    There is further information available on this criteria here: http://www.thesug.org/mossasaurus/Wiki%20Pages/SharePoint%20Invalid%20Characters.aspx.

    The PowerShell script in this article allows you to scan an entire folder structure, including subfolders, and report on all files and folders containing one or more of the conditions listed above. There are also options within the script to automatically rename illegal characters in file names with something acceptable to SharePoint – for example, renaming the & symbol with the word ‘and’.

    To use the script, first load the following function in a PowerShell console. Note that loading the function will not actually do anything until you call it later from the command line:

    function Check-IllegalCharacters ($Path, [switch]$Fix, [switch]$Verbose)
    {
        Write-Host Checking files in $Path, please wait...
        #Get all files and folders under the path specified
        $items = Get-ChildItem -Path $Path -Recurse
        foreach ($item in $items)
        {
            #Check if the item is a file or a folder
            if ($item.PSIsContainer) { $type = "Folder" }
            else { $type = "File" }
           
            #Report item has been found if verbose mode is selected
            if ($Verbose) { Write-Host Found a $type called $item.FullName }
           
            #Check if item name is 128 characters or more in length
            if ($item.Name.Length -gt 127)
            {
                Write-Host $type $item.Name is 128 characters or over and will need to be truncated -ForegroundColor Red
            }
            else
            {
                #Got this from
    http://powershell.com/cs/blogs/tips/archive/2011/05/20/finding-multiple-regex-matches.aspx
                $illegalChars = '[&{}~#%]'
                filter Matches($illegalChars)
                {
                    $item.Name | Select-String -AllMatches $illegalChars |
                    Select-Object -ExpandProperty Matches
                    Select-Object -ExpandProperty Values
                }
               
                #Replace illegal characters with legal characters where found
                $newFileName = $item.Name
                Matches $illegalChars | ForEach-Object {
                    Write-Host $type $item.FullName has the illegal character $_.Value -ForegroundColor Red
                    #These characters may be used on the file system but not SharePoint
                    if ($_.Value -match "&") { $newFileName = ($newFileName -replace "&", "and") }
                    if ($_.Value -match "{") { $newFileName = ($newFileName -replace "{", "(") }
                    if ($_.Value -match "}") { $newFileName = ($newFileName -replace "}", ")") }
                    if ($_.Value -match "~") { $newFileName = ($newFileName -replace "~", "-") }
                    if ($_.Value -match "#") { $newFileName = ($newFileName -replace "#", "") }
                    if ($_.Value -match "%") { $newFileName = ($newFileName -replace "%", "") }
                }
               
                #Check for start, end and double periods
                if ($newFileName.StartsWith(".")) { Write-Host $type $item.FullName starts with a period -ForegroundColor red }
                while ($newFileName.StartsWith(".")) { $newFileName = $newFileName.TrimStart(".") }
                if ($newFileName.EndsWith(".")) { Write-Host $type $item.FullName ends with a period -ForegroundColor Red }
                while ($newFileName.EndsWith("."))   { $newFileName = $newFileName.TrimEnd(".") }
                if ($newFileName.Contains("..")) { Write-Host $type $item.FullName contains double periods -ForegroundColor red }
                while ($newFileName.Contains(".."))  { $newFileName = $newFileName.Replace("..", ".") }
               
                #Fix file and folder names if found and the Fix switch is specified
                if (($newFileName -ne $item.Name) -and ($Fix))
                {
                    Rename-Item $item.FullName -NewName ($newFileName)
                    Write-Host $type $item.Name has been changed to $newFileName -ForegroundColor Blue
                }
            }
        }
    }

    As commented in the script, note that I have used a code snippet on the PowerShell.com blog here to find multiple regular expression matches in the file and folder names.

    Once loaded, you can call the script using the following commands as examples:

    Check-IllegalCharacters -Path C:\Files

    The command above will check the folder path specified but will only report file and folder names detected with illegal characters or length.

    Check-IllegalCharacters -Path C:\Files -Verbose

    This command will also only report files and folder names detected with illegal characters or length, but this time it will also tell you names of the files and folders it has checked in the process. This can be used to make sure the script is checking all the locations you are expecting it to.

    Check-IllegalCharacters -Path C:\Files -Fix

    The command here will not only check file and folder names for illegal characters, but will also fix them using the rules specified in the script. You can customise these rules as you see fit, but I have gone with the following criteria:

    • Do not change files and folders with names of 128 characters or over (i.e., manually truncate them)
    • Replace two or more consecutive periods in a file or folder name with a single period
    • If the file or folder name either starts or finishes with a period, remove it
    • File or folder names containing illegal characters are processed as follows:
      • Replace ‘&’ with ‘and’
      • Replace ‘{‘ with ‘(‘
      • Replace ‘}’ with ‘)’
      • Replace “~” with “-“
      • Remove the ‘#’ character
      • Remove the ‘%’ character

    An example running the script on some files and folders containing deliberately illegal characters is shown below:

    The Illegal Files

    The following screenshot shows the output from running the script:

    image

    And evidence that the files were renamed successfully…

    The Proof

    Thursday, 27 October 2011

    Copy SharePoint lists and document libraries from one site to another using PowerShell

    This article is the first in a series on exporting and importing SharePoint objects using the Content Deployment API and PowerShell. I’m not going to cover the API itself, as there are lots of articles doing this already, notably this series from Stefan Goßner and MSDN, which I would urge you to check out for more details.

    You can achieve some of this functionality with the Export-SPWeb and Import-SPWeb cmdlets provided with SharePoint 2010, but a) these are not available with SharePoint 2007 and b) using the Content Management API directly provides access to extra capabilities. I have written these scripts to work on SharePoint 2007 and 2010 (WSS and Foundation included), although to date I have only tested them on SharePoint Server 2010.

    This first article provides a couple of functions for exporting a list or document library and all items contained within it from one site and importing it into another site. The destination site can be in a different site collection, web application and even farm. I have provided command parameters for what I think will be the most popular configuration options, but as we have full access to the API, the scripts can be adapted to suit requirements.

    Exporting a list or document library

    The first function exports a list or document library from a SharePoint site into either a folder or compressed .cmp file on a network or local drive. Before you can run the command, save the following function as a .ps1 file:

    function Export-List
    {
        Param (
               [parameter(Mandatory=$true)][string]$WebUrl,
               [parameter(Mandatory=$true)][string]$ListName,
               [parameter(Mandatory=$true)][string]$Path,
               [parameter(Mandatory=$false)][switch]$ExcludeDependencies,
               [parameter(Mandatory=$false)][switch]$HaltOnWarning,
               [parameter(Mandatory=$false)][switch]$HaltOnNonfatalError,
               [parameter(Mandatory=$false)][switch]$AutoGenerateDataFileName,
               [parameter(Mandatory=$false)][switch]$TestRun,
               [parameter(Mandatory=$false)][string]$IncludeSecurity,
               [parameter(Mandatory=$false)][string]$IncludeVersions,
               [parameter(Mandatory=$false)][int]$FileMaxSize,
               [parameter(Mandatory=$false)][switch]$Overwrite,
               [parameter(Mandatory=$false)][switch]$SuppressLog
               )
       
        #Load SharePoint 2010 cmdlets
        $ver = $host | select version
        if ($ver.Version.Major -gt 1)  {$Host.Runspace.ThreadOptions = "ReuseThread"}
        Add-PsSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
        #Load assemblies (needed for SharePoint Server 2007)
        [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
       
        #Check parameters have the correct values
        if (!$IncludeSecurity)
        {
            $IncludeSecurity = "None"
        }
        else
        {
            if (($IncludeSecurity -ne "All") `
            -and ($IncludeSecurity -ne "WssOnly") `
            -and ($IncludeSecurity -ne "None"))
            {
                Throw "The IncludeSecurity parameter must be set to All, WssOnly or None"
            }
        }
       
        if (!$IncludeVersions)
        {
            $IncludeVersions = "LastMajor"
        }
        else
        {
            if (($IncludeVersions -ne "All") `
            -and ($IncludeVersions -ne "CurrentVersion") `
            -and ($IncludeVersions -ne "LastMajor") `
            -and ($IncludeVersions -ne "LastMajorAndMinor"))
            {
                Throw "The IncludeVersions parameter must be set to All, CurrentVersion, LastMajorAndMinor or LastMajor"
            }
        }
       
        if (!$FileMaxSize)
        {
            $FileMaxSize = 0
        }
           
        $site = New-Object Microsoft.SharePoint.SPSite($WebUrl)
        $web = $site.OpenWeb()
        $list = $web.Lists[$ListName]
        [bool]$FileCompression = $false
       
        #Set file paths for the export file and logs
        [string]$exportPath = $Path.TrimEnd("\")
        if ($exportPath.EndsWith(".cmp"))
        {
            $FileCompression = $true
            $exportFile = $Path.Replace($Path.Remove($Path.LastIndexOf("\")+1),"")
            $exportPath = $Path.Remove($Path.LastIndexOf("\"))
        }
       
        $logFilePath = $exportPath + "\exportlog.txt"
        New-Item -Path $logFilePath -Type File -Force | Out-Null
        Write-Host "Export log file created at" $logFilePath
       
        $exportObject = New-Object Microsoft.SharePoint.Deployment.SPExportObject
        $exportObject.Id = $list.ID
        $exportObject.Type = [Microsoft.SharePoint.Deployment.SPDeploymentObjectType]::List
       
        #Create the export settings from the parameters specified
        $exportSettings = New-Object Microsoft.SharePoint.Deployment.SPExportSettings
        $exportSettings.SiteUrl = $site.Url
        $exportSettings.ExportMethod = [Microsoft.SharePoint.Deployment.SPExportMethodType]::ExportAll
        $exportSettings.FileLocation = $exportPath
        $exportSettings.FileCompression = $FileCompression
        if ($FileCompression) { $exportSettings.BaseFileName = $exportFile }
        $exportSettings.ExcludeDependencies = $ExcludeDependencies
        $exportSettings.OverwriteExistingDataFile = $Overwrite
        $exportSettings.IncludeSecurity = $IncludeSecurity
        $exportSettings.IncludeVersions = $IncludeVersions
        $exportSettings.LogFilePath = $logFilePath
        $exportSettings.HaltOnWarning = $HaltOnWarning
        $exportSettings.HaltOnNonfatalError = $HaltOnNonfatalError
        $exportSettings.AutoGenerateDataFileName = $AutoGenerateDataFileName
        $exportSettings.TestRun = $TestRun
        $exportSettings.FileMaxSize = $FileMaxSize
        $exportSettings.ExportObjects.Add($exportObject)

        #Write the export settings to a log file   
        Out-File -FilePath $logFilePath -InputObject $exportSettings -Append -Encoding utf8
       
        #Run the export procedure
        $export = New-Object Microsoft.SharePoint.Deployment.SPExport($exportSettings)
        $export.Run()
       
        #Load notepad showing the log file
        if (!$SuppressLog) { notepad $logFilePath }
       
        #Dispose of the web and site objects after use
        $web.Dispose()
        $site.Dispose()
    }

    To load the function in a PowerShell console, type . ‘C:\YourPath\YourFilename.ps1’. You should now be able to use the Export-List command to export your list. A list of the parameters available are shown in the following table. Much of this text is a straight copy from the MSDN article referenced at the start of this article.

    Examples

    Optional/ Mandatory

    Description

    -WebUrl “http://portal/”

    Mandatory

    Absolute URL of the site containing the list being exported

    -ListName “Team Contacts” Mandatory Display name of the list to be exported

    -Path “C:\Export”

    -Path “C:\Export\List.cmp”

    Mandatory

    Location on the file system where the exported files will be copied. You can also specify a .cmp file if you want the export to be compressed into files

    -ExcludeDependencies

    Optional

    Exclude dependencies from the export package. Generally, you should always include export dependencies to avoid breaking objects in the export target

    -HaltOnWarning

    Optional

    Stop the export operation if a warning occurs

    -HaltOnNonfatalError

    Optional

    Stop the export operation for a non-fatal error

    -AutoGenerateDataFileName

    Optional

    The file name for the content migration package should be automatically generated. When the export generates multiple .cmp files, the file names are appended numerically. For example, where the file name is "MyList", and where the export operation produces multiple .cmp files, the migration packages are named "MyList1.cmp", "MyList2.cmp", and so forth

    -TestRun

    Optional

    Complete a test run to examine the export process and log any warnings or errors

    -IncludeSecurity All

    Optional

    Site security groups and group membership information is exported.

    The enumeration provide three values:

    All : Specifies the export of user memberships and role assignments such as out of the box roles like Web Designer, plus any custom roles that extend from the out of the box roles. The ACL for each object is exported to the migration package, as well as user information defined in the DAP or LDAP servers.

    None : No user role or security group information is exported. This is the default.

    WssOnly : Specifies the export of user memberships and role assignments such as out of the box roles like Web Designer, plus any custom roles that extend from the out of the box roles. The ACL for each object is exported to the migration package; however, user information defined in the DAP or LDAP servers is not exported.

    The default value when no parameter is specified is None.

    Note: Be careful with this parameter. When exporting objects smaller than a web (for example, a list or list item) you should set IncludeSecurity to None; otherwise, security group and membership information for the entire web is exported.

    -IncludeVersions

    Optional

    Determines what content is selected for export based on version information.

    There are four enumeration values:

    All , which exports all existing versions of selected files.

    CurrentVersion , which exports only the most recent version of selected files.

    LastMajor , which exports only the last major version of selected files. This is the default value.

    LastMajorAndMinor , which exports the last major version and its minor versions.

    Note that LastMajor is the default value when no parameter is specified.

    -FileMaxSize

    Optional

    Maximum size for a content migration package (.cmp) file that is outputted by the export operation.

    By default, the .cmp files are limited to 24 MB in size. If set to zero, the value resets to the default.

    When site data exceeds the specified limit, site data is separated in to two or more migration files. However, if a single site data file exceeds the maximum file size, the operation does not split the source file, but rather it resizes the .cmp file to accommodate the oversize file. You can have any number of .cmp files.

    The range of allowable size values for the .cmp file is from 1 MB to 2GB. If you specify a value that is outside this range, the export operation reverts to the default value of 24 MB.

    -Overwrite

    Optional

    Overwrite an existing content migration package file when running export. If this parameter is not specified, an exception is thrown if the specified data file already exists

    -SuppressLog Optional By default, the command will open Notepad at the end of the export routine, showing an activity list and any warning and errors reported. You can use this switch to prevent the log file from opening in Notepad, which is useful when performing bulk operations

    Here are a few examples of how the Export-List function can be used. First, this command will export a “Team Contacts” list and all list items from the site http://portal/sites/sales to the folder C:\Export\TeamContacts on the server, overwriting any existing export in that folder:

    Export-List -WebUrl “http://portal/sites/sales” -ListName "Team Contacts" -Path "C:\Export\TeamContacts" -Overwrite

    This one will export a “Team Documents” library and all documents contained within it from the site http://portal/sites/sales to the file C:\Export\TeamDocuments.cmp, overwriting any existing .cmp file with the same name. This time, we are also going to export permissions set on the library by using the IncludeSecurity parameter:

    Export-List -WebUrl “http://portal/sites/sales” -ListName "Team Documents" -Path "C:\Export\TeamDocuments.cmp" -Overwrite -IncludeSecurity All

    image

    Importing a list or document library

    Adding your exported list or document library to another SharePoint site can be achieved using the Import-List function below. As with the export function, add the import function to the same or different .ps1 file and load it into the PowerShell console in the same way:

    function Import-List
    {
        Param (
               [parameter(Mandatory=$true)][string]$WebUrl,
               [parameter(Mandatory=$true)][string]$Path,
               [parameter(Mandatory=$false)][switch]$HaltOnWarning,
               [parameter(Mandatory=$false)][switch]$HaltOnNonfatalError,
               [parameter(Mandatory=$false)][switch]$RetainObjectIdentity,
               [parameter(Mandatory=$false)][string]$IncludeSecurity,
               [parameter(Mandatory=$false)][string]$UpdateVersions,
               [parameter(Mandatory=$false)][switch]$SuppressLog
               )
       
        #Load SharePoint 2010 cmdlets
        Add-PSSnapin Microsoft.SharePoint.PowerShell -erroraction SilentlyContinue
        #Load assemblies (needed for SharePoint Server 2007)
        [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
           
        #Check parameters have the correct values
        if (!$IncludeSecurity)
        {
            $IncludeSecurity = "None"
        }
        else
        {
            if (($IncludeSecurity -ne "All") `
            -and ($IncludeSecurity -ne "WssOnly") `
            -and ($IncludeSecurity -ne "None"))
            {
                Throw "The IncludeSecurity parameter must be set to All, WssOnly or None"
            }
        }
       
        if (!$UpdateVersions)
        {
            $UpdateVersions = "Overwrite"
        }
        else
        {
            if (($UpdateVersions -ne "Overwrite") `
            -and ($UpdateVersions -ne "Append") `
            -and ($UpdateVersions -ne "Ignore"))
            {
                Throw "The UpdateVersions parameter must be set to Overwrite, Append or Ignore"
            }
        }
       
        $site = New-Object Microsoft.SharePoint.SPSite($WebUrl)
        $web = $site.OpenWeb()
       
        $importSettings = New-Object Microsoft.SharePoint.Deployment.SPImportSettings
       
        #Set file paths for the import file and logs
        $fileName = ""
        if ($Path.EndsWith(".cmp"))
        {
            $fileName = $Path.Replace($Path.Remove($Path.LastIndexOf("\")+1),"")
            $importPath = $Path.Remove($Path.LastIndexOf("\"))
            $importSettings.FileCompression = $true
            $importSettings.BaseFileName = $fileName
        }
        else
        {
            $importPath = $Path.TrimEnd("\")
            $importSettings.FileCompression = $false
        }
       
        $logFilePath = $importPath + "\importlog.txt"
        New-Item -Path $logFilePath -Type File -Force | Out-Null
        Write-Host "Import log file created at" $logFilePath
       
        #Create the import settings from the parameters specified
        Write-Host "Configuring import settings"
        $importSettings.SiteUrl = $site.Url
        $importSettings.WebUrl = $web.Url
        $importSettings.FileLocation = $importPath
        $importSettings.IncludeSecurity = $IncludeSecurity
        $importSettings.UpdateVersions = $UpdateVersions
        $importSettings.LogFilePath = $logFilePath
        $importSettings.HaltOnWarning = $HaltOnWarning
        $importSettings.HaltOnNonfatalError = $HaltOnNonfatalError
        $importSettings.RetainObjectIdentity = $RetainObjectIdentity
        $importSettings.UserInfoDateTime = "ImportAll"
        if (!$SuppressLog) { $importSettings }
       
        #Write the import settings to a log file
        Out-File -FilePath $logFilePath -InputObject $importSettings -Append -Encoding utf8
       
        #Run the import procedure
        Write-Host "Import running, please wait..."
        $import = New-Object Microsoft.SharePoint.Deployment.SPImport($importSettings)
        $import.Run()
        Write-Host "Import from" $Path "complete"
       
        #Load notepad showing the log file
        if (!$SuppressLog) { notepad $logFilePath }
       
        #Dispose of the web and site objects after use
        $web.Dispose()
        $site.Dispose()
    }

    The parameters available for importing lists and document libraries are shown in the table below:

    Example

    Optional/ Mandatory

    Description

    -WebUrl “http://portal/”

    Mandatory

    Absolute URL of the site into which the list will imported

    -Path “C:\Export”

    -Path “C:\Export\List.cmp”

    Mandatory

    Folder location and file name (if applicable) on the file system where the export files have been copied.

    If a folder only is specified then PowerShell will assume that compression was not used during the export process. If a file with the .cmp extension is specified then compression will be assumed and applied to the import process.

    -HaltOnWarning

    Optional

    Stop the import operation if a warning occurs

    -HaltOnNonfatalError

    Optional

    Stop the import operation for a non-fatal error

    -RetainObjectIdentity

    Optional

    RetainObjectIdentity ensures the same list GUID and item ID’s are used when migrating a list from one environment to another. It is most commonly used when you have two Web sites that are mirror images of each other, or in a publishing scenario to indicate that a list in one database is the same list in another database. An exception error is raised if you try and use the RetainObjectIdentity property for importing a list that already exists within the same SharePoint Web application.

    Note: Because databases do not permit duplicate GUIDs, you should be careful when implementing this property. It is only useful in selective migration scenarios

    -IncludeSecurity All

    Optional

    Site security groups and group membership information is imported.

    The enumeration provide three values:

    All : Specifies the export of user memberships and role assignments such as out of the box roles like Web Designer, plus any custom roles that extend from the out of the box roles. The ACL for each object is exported to the migration package, as well as user information defined in the DAP or LDAP servers.

    None : No user role or security group information is exported. This is the default.

    WssOnly : Specifies the export of user memberships and role assignments such as out of the box roles like Web Designer, plus any custom roles that extend from the out of the box roles. The ACL for each object is exported to the migration package; however, user information defined in the DAP or LDAP servers is not exported.

    The default value when no parameter is specified is None.

    -UpdateVersions

    Optional

    Indicates how versioning is managed in the destination location.

    Allowable values for specifying versioning information on import:

    Append : Appends versioned items on import.

    Overwrite : Deletes the existing item on import, then reinserts the imported version.

    Ignore : Ignores versioning.

    The default value when no parameter is specified is Overwrite.

    -SuppressLog Optional By default, the command will open Notepad at the end of the import routine, showing an activity list and any warning and errors reported. You can use this switch to prevent the log file from opening in Notepad, which is useful when performing bulk operations

    Here are a few examples of how the Import-List function can be used. First, this command will import the “Team Contacts” list and all list items from the exported files in the folder C:\Export\TeamContacts to the site http://intranet/sites/sales:

    Import-List -WebUrl “http://intranet/sites/sales” -Path "C:\Export\TeamContacts"

    This one will import the “Team Documents” library and all documents contained within it from the file C:\Export\TeamDocuments.cmp to the site http://intranet/sites/sales. This time, we are also going to import permissions set on the library by using the IncludeSecurity parameter:

    Import-List -WebUrl "http://intranet/sites/sales” -Path "C:\Export\TeamDocuments.cmp" -IncludeSecurity All

    image

    Note by comparing the screenshot above with the one earlier that the last modified date, time and user of the document has been migrated across with the list, which is set by the $importSettings.UserInfoDateTime = "ImportAll" command in the script.

    The Power in PowerShell

    In addition to having full access to the Content Management API in SharePoint, one of the other advantages with using PowerShell to perform these tasks is the ability for you to reuse previously created scripts or perform operations in bulk to handle repetitive tasks.

    For example, once we have exported our list or document library, we can use these few lines of PowerShell script (SharePoint 2010 only) to walk through every site in a site collection and import the list into each of those sites:

    $site = Get-SPSite “http://intranet/sites/sales”
    $site | Get-SPWeb -Limit All | ForEach-Object {
        Write-Host Importing list to site $_.Url
        Import-List -WebUrl $_.Url -Path "C:\Export\TeamContacts" -IncludeSecurity All -SuppressLog
    }

    Using the Content Management API in this way can be very useful for deploying or migrating lists and libraries that contain a pre-configured standard set of items, columns, permissions and other settings across one or more existing sites in different site collections or farms.

    As always though, make sure you test these scripts in a development environment first before running them in your production farm!