Dog Food Conference 2014


Today I had the privilege of presenting on Managing SharePoint Anywhere with Windows PowerShell at Dog Food Conference, 2014!

My presentation was focused on leveraging the Microsoft.SharePoint.Client assembly to write Client Side Object Model (CSOM) code against SharePoint Online. However, the twist is that it’s all done in PowerShell 3.0 – and there is a set of PowerShell functions written specifically to handle automation in SharePoint Online.

While I have shared this topic a time or two at other events, this was the most refined version of the presentation.

Continue reading…

Upgrade multiple SharePoint content databases using PowerShell and XML


As a SharePoint Consultant, a lot of the work I do is around upgrades and migrations. I’ve done quite a few of these, and I’ve done them from 2003 to 2007, 2007 to 2010, and now 2010 to 2013. I’ve done several migrations where a 3rd party tool was used to migrate the content, but a lot of the time I end up using the standard database attach approach to move the content databases from the old farm to the new farm. In doing so, obviously PowerShell is required – but more times than not there are multiple content databases. Manually testing and mounting each one can be extremely time consuming, especially if you’re going to end up deploying the content to multiple farms (Dev, QA, DR, Production). What I’ve decided to do is actually build some PowerShell and XML that can be used both to run Test-SPContentDatabase as well as Mount-SPContentDatabase to allow you to do these in bulk.

When writing this script, I had a few things in mind that I wanted to accomplish:

  1. Provide 100% of the input to the script in the form of XML, allowing the script to be 100% separate from the parameters and input.
  2. Provide options to use Test OR Mount, depending on where I’m at in the migration. (It’s always recommended to run Test-SPContentDatabase BEFORE Mount-SPContentDatabase!)
  3. Provide readable output and progress indication to know where the script is in it’s execution.

To accomplish these 3 goals, I made sure to:

  • Include ALL of the available options when running Test-SPContentDatabase AND Mount-SPContentDatabase in the XML and Script (empty values are simply ignored).
  • Include SWITCH parameters for Test and Mount – to allow the user to specify their intention at runtime.
  • Include Write-Progress and Write-Output to provide clear output to the executing user.

Now that I’ve explained my intentions with writing this code, I’ll share it!

Here is an example of how you could use the XML:

<?xml version="1.0" encoding="utf-8" ?>
<WebApps>
	<WebApp Url="http://portal.adventureworks.com">
		<Databases>
			<Database Name="SP2010_Pub_Content_1" WarningSiteCount="0" MaxSiteCount="1" AssignNewDatabaseId="false" ChangeSyncKnowledge="false" ClearChangeLog="false" DatabaseCredentials="" DatabaseServer="SPSQL\Publishing" NoB2BSiteUpgrade="false" SkipIntegrityChecks="false"/>
			<Database Name="SP2010_Pub_Content_2" WarningSiteCount="5" MaxSiteCount="10" AssignNewDatabaseId="false" ChangeSyncKnowledge="false" ClearChangeLog="false" DatabaseCredentials="" DatabaseServer="SPSQL\Publishing" NoB2BSiteUpgrade="false" SkipIntegrityChecks="false"/>
		</Databases>
	</WebApp>
	<WebApp Url="http://collab.adventureworks.com">
		<Databases>
			<Database Name="SP2010_Collab_Content_1" WarningSiteCount="25" MaxSiteCount="30" AssignNewDatabaseId="false" ChangeSyncKnowledge="false" ClearChangeLog="false" DatabaseCredentials="" DatabaseServer="SPSQL\Collab" NoB2BSiteUpgrade="false" SkipIntegrityChecks="false"/>
			<Database Name="SP2010_Collab_Content_2" WarningSiteCount="50" MaxSiteCount="100" AssignNewDatabaseId="false" ChangeSyncKnowledge="false" ClearChangeLog="false" DatabaseCredentials="" DatabaseServer="SPSQL\Collab" NoB2BSiteUpgrade="false" SkipIntegrityChecks="false"/>
		</Databases>
	</WebApp>
</WebApps>

Here is the PowerShell code:

<#
.Synopsis
    This script leverages an XML file as the input - and based on the XML contents will attach one or more 
	content databases to one or more SharePoint 2010/2013 Web Applications.
.Description
    This PowerShell Script uses Get-Content, ForEach-Object and Mount-SPContentDatabase cmdlets along with 
	some other various code snippets to iterate through one or more web applications.
    While attaching content databases to a SharePoint 2010/2013 farm, it will use Write-Progress to provide an update in the PowerShell window.
.Example
    Mount-SPContentDatabasesFromXml.ps1 -XmlInput C:\ContentDatabases.xml
   
    This example will mount all content databases to all web applications specified in an XML file at C:\ContentDatabases.xml.
   
    Example XML syntax:
    <?xml version="1.0" encoding="utf-8" ?>
    <WebApps>
        <WebApp Url="http://igs">
            <Databases>
                <Database Name="SP2010_Content_1" WarningLevel="5" MaxSites="10"/>
                <Database Name="SP2010_Content_2" WarningLevel="50" MaxSites="100"/>
            </Databases>
        </WebApp>
    </WebApps>
.Notes
    Name: Mount-SPContentDatabasesFromXml.ps1
    Author: Ryan Dennis
    Last Edit: 9/16/2013
    Keywords: Mount-SPContentDatabase, ForEach-Object, Get-Content, SharePoint Upgrade/Migration
.Link
    http://www.sharepointryan.com
    ryan@sharepointryan.com
       
#Requires -Version 2.0
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)][System.String]$XmlInput,
[Parameter(Mandatory=$false,ParameterSetName="Test")][Switch]$Test,
[Parameter(Mandatory=$false,ParameterSetName="Mount")][Switch]$Mount
)
# Look for the SharePoint Snapin and add it if not already added #
if ((Get-PSSnapin Microsoft.SharePoint.PowerShell) -eq $null)
{
    Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
}
$FarmVersion = (Get-SPFarm).BuildVersion
if ($FarmVersion.Major -eq '15') {$FarmVersion = "2013"} 
elseif ($FarmVersion.Major -eq '14') {$FarmVersion = "2010"} 
else {$FarmVersion = ""}

# Load XML document #
[System.Xml.XmlDocument]$xml = Get-Content $($XmlInput)
if ($xml -eq $null)
{
    throw "XML input not found"
}

# Iterate through each WebApps.WebApp in the XML document and mount databases #
$i=0
$dbCount = $xml.GetElementsByTagName("Database").Count
$xml.WebApps.WebApp | ForEach-Object {
$waUrl = $_.Url

    $_.Databases.Database | ForEach-Object {
        $dbName = $_.Name
		if (!([string]::IsNullOrEmpty($_.WarningSiteCount)))
		{$paramWarningSites = @{WarningSiteCount = $_.WarningSiteCount}}
		else{$paramWarningSites = ""}
		
		if (!([string]::IsNullOrEmpty($_.MaxSiteCount)))
		{$paramMaxSites = @{MaxSiteCount = $_.MaxSiteCount}}
		else{$paramMaxSites = ""}
		
		if (!([string]::IsNullOrEmpty($_.AssignNewDatabaseId)))
		{$paramNewDbId = @{AssignNewDatabaseId = $true}}
		else{$paramNewDbId = ""}
		
		if (!([string]::IsNullOrEmpty($_.ChangeSyncKnowledge)))
		{$paramChangeSync = @{ChangeSyncKnowledge = $true}}
		else{$paramChangeSync = ""}
		
		if (!([string]::IsNullOrEmpty($_.ClearChangeLog)))
		{$paramClearLog = @{ClearChangeLog = $true}}
		else{$paramClearLog = ""}
		
		if (!([string]::IsNullOrEmpty($_.DatabaseCredentials)))
		{$paramDbCreds = (Get-Credential -Message "Enter Database Credentials")}
		else{$paramDbCreds = ""}
		
		if (!([string]::IsNullOrEmpty($_.DatabaseServer)))
		{
			if($Test){
				$paramDbServer = @{ServerInstance = $_.DatabaseServer}
			}
			if($Mount){
				$paramDbServer = @{DatabaseServer = $_.DatabaseServer}
			}
		}
		else{$paramDbServer = ""}
				
		if (!([string]::IsNullOrEmpty($_.NoB2BSiteUpgrade)))
		{$paramNoB2B = @{NoB2BSiteUpgrade = $true}}
		else{$paramNoB2B = ""}
		
		if (!([string]::IsNullOrEmpty($_.SkipIntegrityChecks)))
		{$paramSkipChecks = @{SkipIntegrityChecks = $true}}
		else{$paramSkipChecks = ""}
		
        try
        {
            $i++
            # If Mount switch param is included, mount the databases #
            if($Mount){
				Write-Progress -Activity "Mounting $dbName to web application $($waUrl)" -PercentComplete ($i/$dbCount*100) -Status "Mounting content databases to SharePoint $FarmVersion"
				Mount-SPContentDatabase -Name $dbName -WebApplication $waUrl @paramNewDbId @paramChangeSync @paramClearLog @paramDbCreds @paramDbServer @paramMaxSites @paramNoB2B @paramSkipChecks @paramWarningSites
        	}
			# If Test switch param is included, test the databases #
			if($Test){
				Write-Progress -Activity "Testing $dbName" -PercentComplete ($i/$dbCount*100) -Status "Testing content databases with SharePoint $FarmVersion"
				Write-Output "::Performing operation Test-SPContentDatabase on database: $dbName`n"
				Test-SPContentDatabase -Name $dbName -WebApplication $waUrl @paramDbCreds @paramDbServer
			}
		}
        catch
        {
            Write-Error $_
        }
    } #endDbForEach
} #endForEach

Use PowerShell to create a whole bunch of SharePoint content


This post is one that came out of a conversation with another SharePoint buddy of mine. We were discussing how to evenly place SharePoint Site Collections into multiple content databases, and how it’s sort of a nice, relatively unknown ‘feature’ of SharePoint that it will use a round-robin approach of placing them into all available content databases for a web application. While it’s very common to place individual SharePoint Site Collections into their own content database, and that’s certainly a good approach – it is certainly a good option to pre-create several content databases and allow SharePoint to handle the balancing act, as it were…

Having said that, here is what I did to prove it out for the purposes of this blog post. I already had an empty web application at http://testtest.adventureworks.com with no content databases from some other testing I’ve done recently:

1. Store the Web Application in a variable

$wa = Get-SPWebApplication http://testtest.adventureworks.com

2. Create 10 Content Databases – note the 1..10 that I’m piping to the ForEach-Object cmdlet. This is a great shorthand way to quickly create a number of ‘things’ in PowerShell.

1..10 | ForEach-Object {
$name = “SPContent_”+$_
New-SPContentDatabase –Name $name –WebApplication $wa
}

3. Create 100 Site Collections – note again the use of 1..100. Also, these will all be team sites under the /sites managed path.

1..100 | ForEach-Object {
$url = “http://testtest.adventureworks.com/sites/”+$_
New-SPSite –Url $url –Template "STS#0" –Name $_ -OwnerAlias domain\user
}

And once all of that is done, you should be able to see a nicely balanced web application with 100 site collections evenly distributed across 10 content databases:
ContentDBs

Happy SharePointing (and PowerShelling)!