SharePoint Saturday Cincinnati 2012


This morning I spoke in front of a great crowd at the second annual SharePoint Saturday Cincinnati, also dubbed ScarePoint Saturday Spookinnati!

I chose to re-use my Build your SharePoint Internet presence with PowerShell presentation, as I still think it’s a good presentation that gives a good introductory view into how you can automate site provisioning with PowerShell. I also try to keep it light from a technical standpoint and tend to focus more on the business case around using PowerShell as an automation tool.

Below is a description of the topic itself along with PowerPoint Slides and the PowerShell and XML code that was used in the demo:

Build your SharePoint Internet presence with PowerShell

The topic: Build your SharePoint Internet presence with PowerShell
The story: Everybody knows PowerShell is powerful, it’s in the name! But did you know that PowerShell can read and understand XML? By leveraging XML among other things, complete builds can be automated – making them efficient and predictable.

In this fun, interactive and demo-filled session – I will show you how you can leverage PowerShell to help you build your branded, company website from the ground up using PowerShell and XML. I will also pass along some tips and tricks that will help you become a PowerShell Rockstar!

Here are my slides, and below are the code snippets:

Demo Script – Setup-SPWebAppAndSite.ps1

XML Syntax:

<?xml version="1.0" encoding="utf-8"?>
<SubSites>
	<Sites SPSiteUrl="http://www.vandelay.com">
		<!-- root site -->
		<Site Create="false">
			<SiteTitle>Home</SiteTitle>
			<SiteUrl>http://www.vandelay.com/</SiteUrl>
			<WebTemplate>CMSPUBLISHING#0</WebTemplate>
			<LangId>1033</LangId>
			<Page>
			   <PageTitle>Home</PageTitle>
			   <PageUrl>Home.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
		   </Page>
			<Page>
			   <PageTitle>Terms of Use</PageTitle>
			   <PageUrl>Terms-of-Use.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
		   </Page>
			<Page>
			   <PageTitle>Privacy Policy</PageTitle>
			   <PageUrl>Privacy-Policy.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
		   </Page>
			<Page>
			   <PageTitle>Linking Policy</PageTitle>
			   <PageUrl>Linking-Policy.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
		   </Page>
			<Page>
			   <PageTitle>Legal Disclaimer</PageTitle>
			   <PageUrl>Legal-Disclaimer.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
		   </Page>
			<Page>
			   <PageTitle>404 - Page Not Found</PageTitle>
			   <PageUrl>Page-Not-Found.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
		   </Page>
		</Site>
		<!-- 2nd level sites -->
		<Site>
			<SiteTitle>Products</SiteTitle>
			<SiteUrl>http://www.vandelay.com/Products</SiteUrl>
			<WebTemplate>CMSPUBLISHING#0</WebTemplate>
			<LangId>1033</LangId>
			<Page>
			   <PageTitle>Awesome Widget</PageTitle>
			   <PageUrl>Awesome-Widget.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
			</Page>
			<Page>
			   <PageTitle>The Spectacular Thingamajig</PageTitle>
			   <PageUrl>The-Spectacular-Thingamajig.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
			</Page>
			<Page>
			   <PageTitle>Store</PageTitle>
			   <PageUrl>Store.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
			</Page>
		</Site>
		<Site>
			<SiteTitle>Solutions</SiteTitle>
			<SiteUrl>http://www.vandelay.com/Solutions</SiteUrl>
			<WebTemplate>CMSPUBLISHING#0</WebTemplate>
			<LangId>1033</LangId>
			<Page>
			   <PageTitle>Customer Engagement</PageTitle>
			   <PageUrl>Customer-Engagement.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
			</Page>
			<Page>
			   <PageTitle>Custom Solutions</PageTitle>
			   <PageUrl>Custom-Solutions.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
			</Page>
		</Site>
		<Site>
			<SiteTitle>Shipping and Returns</SiteTitle>
			<SiteUrl>http://www.vandelay.com/Shipping-and-Returns</SiteUrl>
			<WebTemplate>CMSPUBLISHING#0</WebTemplate>
			<LangId>1033</LangId>
			<Page>
			   <PageTitle>Shipping Policies</PageTitle>
			   <PageUrl>Shipping-Policies.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
			</Page>
			<Page>
			   <PageTitle>Return Policies</PageTitle>
			   <PageUrl>Return-Policies.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
			</Page>
		</Site>
		<Site>
			<SiteTitle>About Us</SiteTitle>
			<SiteUrl>http://www.vandelay.com/About-Us</SiteUrl>
			<WebTemplate>CMSPUBLISHING#0</WebTemplate>
			<LangId>1033</LangId>
			<Page>
			   <PageTitle>Company</PageTitle>
			   <PageUrl>Company.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
			</Page>
			<Page>
			   <PageTitle>History</PageTitle>
			   <PageUrl>History.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
			</Page>
			<Page>
			   <PageTitle>Locations</PageTitle>
			   <PageUrl>Locations.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Blank Web Part page</PageLayout>
			</Page>
			<Page>
			   <PageTitle>Employment Opportunities</PageTitle>
			   <PageUrl>Employment-Opportunities.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
			</Page>
		</Site>
		<Site>
			<SiteTitle>Contact Us</SiteTitle>
			<SiteUrl>http://www.vandelay.com/Contact-Us</SiteUrl>
			<WebTemplate>CMSPUBLISHING#0</WebTemplate>
			<LangId>1033</LangId>
			<Page>
			   <PageTitle>Social Media</PageTitle>
			   <PageUrl>Social-Media.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
			</Page>
			<Page>
			   <PageTitle>Locations</PageTitle>
			   <PageUrl>Locations.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
			</Page>
			<Page>
			   <PageTitle>Service Requests</PageTitle>
			   <PageUrl>Service-Requests.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
			</Page>
			<Page>
			   <PageTitle>Request a Catalog</PageTitle>
			   <PageUrl>Request-a-Catalog.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
			</Page>
		</Site>
		<Site>
			<SiteTitle>What We Do</SiteTitle>
			<SiteUrl>http://www.vandelay.com/What-We-Do</SiteUrl>
			<WebTemplate>CMSPUBLISHING#0</WebTemplate>
			<LangId>1033</LangId>
			<Page>
			   <PageTitle>Engagement</PageTitle>
			   <PageUrl>Engagement.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
			</Page>
			<Page>
			   <PageTitle>Ambassadors</PageTitle>
			   <PageUrl>Ambassadors.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
			</Page>
		</Site>
		<Site>
			<SiteTitle>Our Company</SiteTitle>
			<SiteUrl>http://www.vandelay.com/Our-Company</SiteUrl>
			<WebTemplate>CMSPUBLISHING#0</WebTemplate>
			<LangId>1033</LangId>
			<Page>
			   <PageTitle>Forms</PageTitle>
			   <PageUrl>Forms.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
			</Page>
			<Page>
			   <PageTitle>Travel</PageTitle>
			   <PageUrl>Travel.aspx</PageUrl>
			   <PageContent></PageContent>
			   <PageLayout>Body Only</PageLayout>
			</Page>
		</Site>
		<Site>
			<SiteTitle>Our Customers</SiteTitle>
			<SiteUrl>http://www.vandelay.com/Our-Customers</SiteUrl>
			<WebTemplate>CMSPUBLISHING#0</WebTemplate>
			<LangId>1033</LangId>
		</Site>
		<Site>
			<SiteTitle>News</SiteTitle>
			<SiteUrl>http://www.vandelay.com/News</SiteUrl>
			<WebTemplate>CMSPUBLISHING#0</WebTemplate>
			<LangId>1033</LangId>
		</Site>
	</Sites>
</SubSites>

PowerShell code:

#Region New-SPWebApplicationFromXml
function New-SPWebApplicationFromXml {
<#
.Synopsis
	This advanced PowerShell Function uses XML and PowerShell code to provision
	one or more Web Applications, Site Colections, Subsites and Pages. It can
	also deploy and activate solutions and features.
.Description
	This advanced script uses New-SPWebApplication, New-SPSite and
	Enable-SPFeature to configure web applications and site collections using
	specified Site Templates from the included XML. Features, Managed Paths,
	subsites and even Publishing pages can be created using XML as the input.
.Example
	C:\PS>New-SPWebApplicationFromXml -ConfigXmlInput .\WebApps.xml `
	-SubsiteXmlInput .\Subsites.xml

	This example runs with the default parameter values to configure a Web App
	and Site Collection based on the contents of an XML file called
	WebApps.xml in the current file directory.
.Notes
	Name: New-SPWebApplicationFromXml
	Author: Ryan Dennis
	Last Edit: 9/7/2012
	Keywords: New-SPWebApplication, New-SPSite
.Link
	http://www.sharepointryan.com
 	http://twitter.com/SharePointRyan
#Requires -Version 2.0
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)][System.String]$ConfigXmlInput,
[Parameter(Mandatory=$true)][System.String]$SubsiteXmlInput
)

#Region Miscellaneous stuff at the beginning
# If SharePoint Snapin isn't loaded - load it #
if ((Get-PSSnapin Microsoft.SharePoint.PowerShell)-eq $null)
{
	Write-Warning "Adding SharePoint Snapin"
	try	{ Add-PSSnapin Microsoft.SharePoint.PowerShell }
	catch { throw "Unable to add SharePoint Snapin";return }
}
# Start SP Assignment for proper disposal of objects such as SPSite and SPWeb #
Start-SPAssignment -Global

# Read Web Apps from XML file #
[xml]$WebAppXml = Get-Content $($ConfigXmlInput)
if ($WebAppXml -eq $null) { return }

[xml]$SubsiteXml = Get-Content $($SubsiteXmlInput)
if ($SubsiteXml -eq $null) { return }

#EndRegion

#Region Enable Blob Cache function
function Enable-SPBlobCache {
param(
	[Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)]
	[Microsoft.SharePoint.PowerShell.SPWebApplicationPipeBind]
	$WebApplication,
	[Parameter(Mandatory=$false, ValueFromPipeline=$true, Position=1)]
	$BlobCacheLocation="C:\BlobCache\14"
)

process {
    $WebApp = $WebApplication.Read()
    # SPWebConfigModification to enable BlobCache
    $configMod1 = New-Object Microsoft.SharePoint.Administration.SPWebConfigModification
    $configMod1.Path = "configuration/SharePoint/BlobCache"
    $configMod1.Name = "enabled"
    $configMod1.Sequence = 0
    $configMod1.Owner = "BlobCacheMod"
    ## SPWebConfigModificationType.EnsureChildNode -> 0
    ## SPWebConfigModificationType.EnsureAttribute -> 1
    ## SPWebConfigModificationType.EnsureSection -> 2
    $configMod1.Type = 1
    $configMod1.Value = "true"

    ######################################################################

	# SPWebConfigModification to enable client-side Blob caching (max-age)
    $configMod2 = New-Object Microsoft.SharePoint.Administration.SPWebConfigModification
    $configMod2.Path = "configuration/SharePoint/BlobCache"
    $configMod2.Name = "max-age"
    $configMod2.Sequence = 0
    $configMod2.Owner = "BlobCacheMod"
    ## SPWebConfigModificationType.EnsureChildNode -> 0
    ## SPWebConfigModificationType.EnsureAttribute -> 1
    ## SPWebConfigModificationType.EnsureSection -> 2
    $configMod2.Type = 1
    $configMod2.Value = "86400"

	######################################################################

	# SPWebConfigModification to change the default location for the Blob Cache files
	$configMod3 = New-Object Microsoft.SharePoint.Administration.SPWebConfigModification
	$configMod3.Path = "configuration/SharePoint/BlobCache"
	$configMod3.Name = "location"
	$configMod3.Sequence = "0"
	$configMod3.Owner = "BlobCacheMod"
	## SPWebConfigModificationType.EnsureChildNode -> 0
    ## SPWebConfigModificationType.EnsureAttribute -> 1
    ## SPWebConfigModificationType.EnsureSection -> 2
	$configMod3.Type = 1
	$configMod3.Value = $BlobCacheLocation
    # Add mods, update, and apply
    $WebApp.WebConfigModifications.Add( $configMod1 )
    $WebApp.WebConfigModifications.Add( $configMod2 )
	$WebApp.WebConfigModifications.Add( $configMod3 )
    $WebApp.Update()
    $WebApp.Parent.ApplyWebConfigModifications()
}
}

#EndRegion Blob Cache Function

#Region Load the Form Dialog
# Load Assemblies for System.Windows.Forms and System.Drawing #
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

# Store the 14 Hive in Variable, create an Img variable for Icon #
$14 = 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14'
$Img = $14+"\Template\Images\Favicon.ico"

# Create the Form Object #
$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = "SharePoint Web Application Deployment Wizard"
$objForm.Size = New-Object System.Drawing.Size(500,350)
$objForm.StartPosition = "CenterScreen"
$objForm.Icon = $Img
$objForm.FormBorderStyle = 'FixedDialog'
$objForm.KeyPreview = $True
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter")
    {
		$objForm.DialogResult = [System.Windows.Forms.DialogResult]::OK
		$SelectedApps = $null
		[System.String]$Items=$objListBox.SelectedItems;
		[System.Array]$SelectedApps=$Items.Split(' ');
		if($SelectedApps -eq $null){
		throw "You must select at least one web application!"}
		$objForm.Close()
	}
}
)
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape"){$objForm.Close()}})

# OK Button #
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(330,280)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.Add_Click(
{
	$objForm.DialogResult = [System.Windows.Forms.DialogResult]::OK
	$SelectedApps = $null
	[System.String]$Items=$objListBox.SelectedItems;
	[System.Array]$SelectedApps=$Items.Split(' ');
	if($SelectedApps -eq $null)
	{
	throw "You must select at least one web application!"
	}
	$objForm.Close()
})

# Cancel Button #
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(405,280)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click(
{
	$objForm.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
	$objForm.Close()
}
)

# Listbox Object #
$objListBox = New-Object System.Windows.Forms.ListBox
$objListBox.Location = New-Object System.Drawing.Size(10,40)
$objListBox.Size = New-Object System.Drawing.Size(470,20)
$objListBox.Height = 230
$objListBox.SelectionMode = "MultiExtended"

# Listbox Label #
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,20)
$objLabel.Size = New-Object System.Drawing.Size(470,20)
$objLabel.Text = "Please select the web application(s) you wish to deploy:"

$WebApps = $WebAppXml.WebApplications.WebApplication
$WebApps | ForEach-Object {
	[void] $objListBox.Items.Add($_.WebAppConfig.HostHeader)
}

# Add the controls to the Form Object #
$objForm.Controls.Add($objLabel)
$objForm.Controls.add($objListBox)
$objForm.Controls.Add($OKButton)
$objForm.Controls.Add($CancelButton)
$objForm.Controls.Add($SearchButton)

# Display the form on top of all other windows #
$objForm.Topmost = $True

# Add the dialog box to the screen #
$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()

#EndRegion Form Dialog

#Region Web App ForEach-Object Loop to create everything
# If the dialog result is OK, run the code to create navigation #
if ($objForm.DialogResult -eq "OK" -and $SelectedApps -ne $null)
{
$SelectedApps | ForEach-Object {

	#Region Create Web Application
	# Clear Host for a clean, empty shell #
	Clear-Host

	[System.String]$CurrentWA = $_
	$wa = $WebAppXml.WebApplications.WebApplication |
	Where-Object {$_.WebAppConfig.HostHeader -eq $CurrentWA}

	# Store start time in variable for calculation later #
	$StartTime = Get-Date

	# Read in list of sites from XML file and create variables for use later #
	$Name = $wa.WebAppConfig.Name
	$ApplicationPool = $wa.WebAppConfig.AppPool
	$ApplicationPoolAccount = $wa.WebAppConfig.AppPoolAccount
	$DatabaseServer = $wa.WebAppConfig.DBServer
	$DatabaseName = $wa.WebAppConfig.DBName
	$InetpubPath = $wa.WebAppConfig.InetpubPath
	$HostHeader = $wa.WebAppConfig.HostHeader
	$Port = $wa.WebAppConfig.Port
	$WebsitePath=$InetpubPath+"\"+$HostHeader+$Port
	$Protocol = $wa.WebAppConfig.Protocol
	$Url = $Protocol+"://"+$HostHeader+":"+$Port
	$ManagedPaths = $wa.WebAppConfig.ManagedPaths
	$Solutions = $wa.WebAppConfig.Solutions
	$BlobCacheLocation = $wa.WebAppConfig.BlobCacheLocation
	$AuthType = $wa.WebAppConfig.Authentication.Type
	$AuthMethod = $wa.WebAppConfig.Authentication.AuthMethod
	$AuthProviderName = $wa.WebAppConfig.Authentication.AuthProvider.Name
	$AuthProviderMember = $wa.WebAppConfig.Authentication.AuthProvider.MembershipProviderName
	$AuthProviderRole = $wa.WebAppConfig.Authentication.AuthProvider.RoleProviderName

	# Start SP Assignment for object disposal later #
	Start-SPAssignment -Global

	# Change Shell Title #
	$title = $Host.UI.RawUI.WindowTitle
	$Host.UI.RawUI.WindowTitle = "Provisioning Web Application..."

	$ErrorPref = $ErrorActionPreference
	$ErrorActionPreference = "SilentlyContinue"
	$WarningPref = $WarningPreference
	$WarningPreference = "SilentlyContinue"

	# Tell us the Web App is getting created #
	$iMax = 11 #Number of Steps#
	$i = 0 #Number to start on#

	$i++ #step 1
	Write-Progress -Activity "Step $i of $iMax : Creating Web Application at Url $($HostHeader)" `
	-PercentComplete ($i/$imax*100) -Status "Creating IIS Website, Content Database and Application Pool"
	# Create a Classic App #
	if($AuthType -eq "Classic"){
		try {
			# Create the Web App #
			New-SPWebApplication -Name $Name -ApplicationPool $ApplicationPool `
			-ApplicationPoolAccount $ApplicationPoolAccount -DatabaseName $DatabaseName `
			-DatabaseServer $DatabaseServer -HostHeader $HostHeader -Port $Port `
			-Path $WebsitePath -Url $HostHeader -WarningAction SilentlyContinue | Out-Null
		}
		catch {	throw "Unable to create the web application";return	}
	}
	# Create a Windows Claims App #
	if($AuthType -eq "Claims"){
		try {
			# Create the auth provider #
			$ap = New-SPAuthenticationProvider
			# Create the Web App #
			New-SPWebApplication -Name $Name -ApplicationPool $ApplicationPool `
			-ApplicationPoolAccount $ApplicationPoolAccount -DatabaseName $DatabaseName `
			-DatabaseServer $DatabaseServer -HostHeader $HostHeader -Port $Port `
			-Path $WebsitePath -Url $HostHeader -WarningAction SilentlyContinue `
			-AuthenticationProvider $ap -AuthenticationMethod $AuthMethod | Out-Null
		}
		catch {	throw "Unable to create the web application";return	}
	}
	#EndRegion Create Web Application

	#Region Creating Managed Paths
	$i++ #Step2
	# Create Managed Paths #
	$wa.WebAppConfig.ManagedPaths.ManagedPath | ForEach-Object {
		$path = $_.path
		$type = $_.type
		if($type -eq "explicit"){
			New-SPManagedPath -Explicit -RelativeURL $path -WebApplication $Url `
			-ErrorAction SilentlyContinue | Out-Null
		}
		elseif($type -eq "wildcard"){
			New-SPManagedPath -RelativeURL $path -WebApplication $Url `
			-ErrorAction SilentlyContinue | Out-Null
		}
	}
	#EndRegion Creating Managed Paths

	#Region Installing Solutions
	$i++ #Step3
	# Change Shell Title #
	$Host.UI.RawUI.WindowTitle = "Installing Solutions..."
	# Enable solutions #
	try {
		$Solutions.Solution | ForEach-Object {
		$SolutionId = $_
			if($SolutionId.GACDeployment -eq "true"){
			Write-Progress -Activity "Step $i of $iMax : Installing Solutions..." `
			-PercentComplete ($i/$imax*100) -Status "Activating $($SolutionId) solution"
			Install-SPSolution $($SolutionId) -WebApplication $Url `
			-GACDeployment -ErrorVariable +err -Confirm:$false
			Start-Sleep -Seconds 5
			}
			else{
			Write-Progress -Activity "Step $i of $iMax : Installing Solutions..." `
			-PercentComplete ($i/$imax*100) -Status "Activating $($SolutionId) solution"
			Install-SPSolution $($SolutionId) -WebApplication $Url `
			-ErrorVariable +err -Confirm:$false
			Start-Sleep -Seconds 5
			}
		}
	}
	catch { Write-Error "Error enabling solution $($SolutionId)" }
	#EndRegion Installing Solutions

	#Region Create Site Collections
	# Change Shell Title #
	$Host.UI.RawUI.WindowTitle = "Provisioning Site Collections..."
	$i++ #step4
	$siteCollections = $wa.SiteCollections
	# Create Site Collections using ForEach-Object loop #
	try {
	$SiteCollections.SiteCollection | ForEach-Object {
		$NewSiteUrl = $Url+$_.Path
		$sName = $_.Name
		$sOAlias = $_.OwnerAlias
		$sOEmail = $_.OwnerEmail
		$sSOAlias = $_.SecondaryOwnerAlias
		$sSOEmail = $_.SecondaryOwnerEmail
		$sTemplate = $_.WebTemplate
		$subSites = $_.Sites
		$srchUrl = $_.SearchSettings.SearchCenterUrl
		$srchDD = $_.SearchSettings.DropDownMode
		$srchPage = $_.SearchSettings.TargetResultsPage
		# Tell us the Site Collection is getting created #
		Write-Progress -Activity "Step $i of $iMax : Creating Site Collection at Url $($HostHeader)..." `
		-PercentComplete ($i/$imax*100) -Status "Provisioning $($sTemplate) site"
		$SPSite = New-SPSite -Name $sName -OwnerAlias $sOAlias -OwnerEmail $sOEmail `
		-SecondaryOwnerAlias $sSOAlias -SecondaryEmail $sSOEmail `
		-Template $sTemplate -Url $NewSiteUrl | Out-Null
		Start-Sleep -Seconds 5

		$_.Features.Feature | ForEach-Object {
		# Enable Features #
		try {
			$FeatureId = $_
			Write-Progress -Activity "Step $i of $iMax : Configuring Site Features..." `
			-PercentComplete ($i/$imax*100) -Status "Activating $($FeatureId) feature"
			Enable-SPFeature -Identity $($FeatureId) -Url $NewSiteUrl -ErrorVariable +err `
			-ErrorAction SilentlyContinue -Confirm:$false
			}
		catch { Write-Error "Error activating feature $err" }
		} #end Feature ForEach

		Start-Sleep -Seconds 5

		#Region Search Settings
		.\Set-SPSiteSearchSettings.ps1 -SiteUrl $NewSiteUrl -SearchCenterUrl $srchUrl `
		-SearchDropDownMode $srchDD -SearchResultsUrl $srchPage
		#EndRegion Search Settings

	} #End Site Collection ForEach
	} #End Site Collection Try
	catch { throw "Unable to create site collection at path: $NewSiteUrl";return }

	#EndRegion Create Site Collections

	#Region Configure Blob Caching
	$i++ #step 7
	Write-Progress -Activity "Step $i of $iMax : Configuring Cache Settings..." `
	-PercentComplete ($i/$imax*100) -Status "Configuring BLOB Caching"
	try {
	Enable-SPBlobCache -WebApplication $Url -BlobCacheLocation $BlobCacheLocation
	}
	catch { Write-Error "Unable to configure BLOB caching" }
	#EndRegion Configure Blob Caching

	#Region Run External Scripts
		$location = Get-Location
		$modPath = $PSHOME+"\Modules\PSSAT002\"
		Set-Location $modPath

		#Region Create Web App Policy
		$i++ #step8
		Write-Progress -Activity "Step $i of $iMax : Creating Web Application Policy..." `
		-PercentComplete ($i/$imax*100) -Status "Configuring Full Control Policy"

		.\Configure-SPWebApplicationUserPolicy.ps1 -WebApplicationUrl $Url
		#EndRegion Create Web App Policy

		#Region Meta Fields
		# Create Meta Fields #
		$i++ #step 9
		Write-Progress -Activity "Step $i of $iMax : Configuring SEO Fields..." `
		-PercentComplete ($i/$imax*100) -Status "Provisioning SEO Meta Fields"

		#Write-Host "Configuring SEO fields"
		.\Create-MaventionMetaFields.ps1 -Url $Url
		#EndRegion MetaFields

		Set-Location $location
	#EndRegion Run External Scripts

	#Region Create Subsites

		# Create subwebs from XML input #
		$i++ #step 11
		Write-Progress -Activity "Step $i of $iMax : Creating Subsites from XML..." `
		-PercentComplete ($i/$imax*100) -Status "Retrieving sites from XML file"
		Start-SPAssignment -Global

		$SubsiteXml.SubSites.Sites.Site | ForEach-Object {
		Write-Progress -Activity "Step $i of $iMax : Creating Subsites from XML..." `
		-PercentComplete ($i/$imax*100) -Status "Creating an SPWeb at $($SiteUrl)"
		$SPSiteUrl = $_.SPSiteUrl
		$SiteTitle = [string]$_.SiteTitle
		$SiteUrl = [string]$_.SiteUrl
		$SiteOwner = [string]$_.Owner
		$SiteUser = [string]$_.User
		$WebTemplate = [string]$_.WebTemplate
		$LangId = [string]$_.LangId
		$SiteUrl = $SiteUrl
		# Create the SPWeb #
		if ($_.create -ne "false")
		{
		$Web = New-SPWeb -Url $SiteUrl -Language $LangId -Template $WebTemplate `
		-Name $SiteTitle -ErrorAction Inquire
		}
		else {
		$Web = Get-SPWeb -Identity $SiteUrl -ErrorAction Inquire
		}
		# Get publishing site and web objects
		$site = $Web.Site
		$pWeb = [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($web)

		# Create all Pages using ForEach-Object #
		if ($_.Page -ne $null){
		$_.Page | ForEach-Object {

		# Create page content variables from XML file
		$PageTitle = [string]$_.PageTitle
		$PageUrl = [string]$_.PageUrl
		$PageLayout = [string]$_.PageLayout
		#$PageContent = [xml]$_.PageContent
		$PageImage = [string]$_.PageImage
		$layout = $pWeb.GetAvailablePageLayouts() | Where-Object {$_.Title -match $PageLayout}

	    # Create blank page using Add method
		Write-Progress -Activity "Step $i of $iMax : Creating Subsites from XML..." `
		-PercentComplete ($i/$imax*100) -Status "Creating $($PageTitle).aspx page in $($SiteTitle) site..."
	    $pages = $pWeb.GetPublishingPages($pWeb)
		$page = $pages.Add($PageUrl, $layout)
		$page.Update()

	    # Update the filename to the one specified in the XML, add HTML content to Page Content zone
	    $item = $page.ListItem
		$item["Title"] = $PageTitle;
		$item["Page Content"] = $PageContent;
		$item.Update()

	    # Check-in and publish page
	    $item.File.CheckIn("")
	    $item.File.Publish("")
		$file = $item.File
		$pWeb.Update()

		} #end Page Foreach

		} # End if block
		Start-Sleep -Seconds 2
		} #End Site ForEach

	#EndRegion Create Subsites

	#Region Clean up and launch site
	# Dispose all objects #
	Stop-SPAssignment -Global

	#Region Start SharePoint Warmup
	.\Start-SharePointWarmup.ps1 -WebApp $Url
	#EndRegion

	# Set Error and Warning Preferences back #
	$ErrorActionPreference = $ErrorPref
	$WarningPreference = $WarningPref

	# Open the site in IE #
	$ie = New-Object -ComObject InternetExplorer.Application
	$ie.Navigate($Url)
	$ie.Visible = $true;

	Write-Progress -Activity "Launching the site in Internet Explorer" `
	-Status "Opening IExplore.exe"
	#EndRegion Clean up and launch site
} #end Web App ForEach

#Region Calculate running time and finish up
$EndTime = Get-Date
$TimeSpan = New-TimeSpan $StartTime $EndTime
$Mins = $TimeSpan.Minutes
$Secs = $TimeSpan.Seconds
$Time = $EndTime.ToShortTimeString()
# Tell us it's done #
Write-Host "------------------------------------------------------------------"
Write-Host " Portal Configuration Complete at $($Time)."
Write-Host " Process took $($Mins) minutes and $($Secs) seconds to complete."
Write-Host "------------------------------------------------------------------"
Stop-SPAssignment -Global
#EndRegion Calculate running time and finish up
} #end If DialogResult = OK

#EndRegion Web App ForEach-Object Loop to create everything

#Region Else blocks
# If the Cancel button is clicked, exit #
elseif($objForm.DialogResult -eq "Cancel"){return} #end Else

# If attempting to run without selecting at least one site, output message #
else{Write-Warning "You must select at least one web application!"}
#EndRegion Else blocks

}#endFunction

#EndRegion New-SPWebApplicationFromXml
Advertisements

PowerShell Saturday Charlotte 2012


Today I had the privilege of speaking (twice) at the second PowerShell Saturday, in Charlotte, NC!

Originally I was accepted to speak about advanced functions and XML, so I tweaked my “Build your SharePoint Internet presence with PowerShell” talk to be more of a deep-dive into XML and PowerShell advanced functions (it’s also called “The Power is in the Shell, Use it Wisely!”). However, another speaker was unable to attend and I ended up doing two SharePoint-focused PowerShell presentations. I’ll detail them below, and since this will be a long post, here are links to the different areas within the page:

  1. The Power is in the Shell, Use it Wisely!
  2. Getting Started with SharePoint + PowerShell

The Power is in the Shell, Use it Wisely!

The topic: The Power is in the Shell, Use it Wisely!
The story: Everybody knows PowerShell is powerful, it’s in the name! But did you know that PowerShell can read and understand XML? By leveraging XML among other things, complete builds can be automated – making them efficient and predictable.

In this fun, interactive and demo-filled session – I will show you how you can leverage PowerShell to help you build your branded, company website from the ground up using PowerShell and XML. I will also pass along some tips and tricks that will help you become a PowerShell Rockstar!

This was the second PowerShell Saturday, and the first in Charlotte – and I was very proud to be a part of a great event.

My session went pretty flawlessly, as I lucked out and had zero issues with the presentation or the demos. For those of you who attended, thank you for being a part of it – and if you have any feedback or questions feel free to contact me either on Twitter or via e-mail (Ryan at SharePointRyan dot com).

I promised to upload my slides and demo PowerShell code, so the slides are on Slideshare and the XML and PowerShell code is saved on SkyDrive.

Intro to SharePoint + PowerShell

The topic:  Intro to SharePoint + PowerShell
The story: You may have heard of PowerShell, but do you know what it’s capable of? Gone are the days of long, painful STSADM batch files – we have Windows PowerShell, and it’s here to stay.Learn how you can use Windows PowerShell both to perform simple one-off tasks as well as complex, bulk operations. Leveraging the Object Model gives Administrators and Developers the ability to do in a few lines of code what would’ve taken a lot more work (and probably a Developer or two) in the WSS platform. You’ll see how you can get started with PowerShell, and you will hopefully leave with not only a greater understanding of what PowerShell is – but what it is capable of and how you can start using it to automate tasks in your SharePoint 2010 environment.

View all of the presentations from Ryan Dennis.

Get Available App Templates in SharePoint 2013 (Preview)


I’m starting to dig deeper into SharePoint 2013 Preview, and one of the first things I wanted to do was poke around the Object Model using PowerShell. In doing so, I thought it would be nice to have a quick and easy PowerShell Function to grab all available app templates.

This is actually very simple and is basically identical to the code which would work in SP2010, we simply grab an SPWeb object and then grab the ListTemplates collection.

Here are two examples of how to run it…

This example gets ALL app templates in a web at URL http://sp2013:

Get-SPAppTemplate -WebUrl http://sp2013

This example gets the Asset Library template in a web at URL http://sp2013:

Get-SPAppTemplate -WebUrl http://sp2013 | Where-Object {$_.name -eq "Asset Library"}

Here is the function, which can be run in SP2013 to return all available app/list templates:

function Get-SPAppTemplate {
<#
.Synopsis
	This advanced PowerShell Function retrieves available app/list templates
	in a specified SharePoint Web.
.Description
	This advanced function retrieves an SPWeb object and retrieves all 
	ListTemplates in the collection. Optionally, you can use *-Object cmdlets
	to retrieve a specific template or to select specific fields.
.Example
	C:\PS>Get-SPAppTemplate -WebUrl http://<webUrl> | Where-Object {$_.name -eq "Asset Library"}
	
	This example searches for an App tepmlate with the name "Asset Library."
.Example
	C:\PS>Get-SPAppTemplate -WebUrl http://<webUrl>
	
	This example retrieves all app templates under the webUrl SPWeb.
.Notes
	Name: Get-SPAppTemplate
	Author: Ryan Dennis
	Last Edit: 9/11/2012
	Keywords: Get-SPWeb,Get-SPAppTemplate
.Link
	http://www.sharepointryan.com
 	http://twitter.com/SharePointRyan
#Requires -Version 3.0
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)][System.String]$WebUrl
)
$Web = Get-SPWeb $WebUrl
$Web.ListTemplates
$Web.Dispose()
}

Delete all SharePoint List Items using PowerShell


Sometimes when I’m working on something related to SharePoint, I come across scenarios where I’m constantly building up test data in Lists. Frequently when doing this, I have a need to almost constantly delete and recreate list items.

Rather than using the UI or writing nice PowerShell one-liners (which for the record I love doing), I decided to throw together a little script for this.

This script takes two mandatory parameters, one for URL and one for List Name. Assuming you give it the correct values, it will quickly use Object Model code to call the Delete() method against every item in the list (or library).

Here is the script, hope it works for you! Note: I did not add any kind of confirmation, so if you run this – it WILL literally delete all items, it won’t first make sure you’re serious about it – so make sure you know what you’re doing! 🙂

RD

<#
.Synopsis
	Use this PowerShell Script to delete all list items, very quickly!
.Description
	This advanced script uses SharePoint Cmdlets and the SP Object Model to bulk 
	delete SharePoint list items from a specified SPList.
.Example
	C:\PS>.\Delete-SPListItems.ps1 -Url http://intranet -ListName "Test List"
	This example deletes list items from a list called Test List in the
	http://intranet web.
.Notes
	Name: Delete-SPListItems
	Author: Ryan Dennis
	Last Edit: 8/2/2012
	Keywords: Delete List Items
.Link
	http://www.sharepointryan.com
 	http://twitter.com/SharePointRyan
.Inputs
	None
.Outputs
	None
#Requires -Version 2.0
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)][System.String]$Url,
[Parameter(Mandatory=$true)][System.String]$ListName
)
$Web = Get-SPWeb $Url
$List = $Web.lists[$ListName]
if($List -eq $null){
	Write-Error "The List cannot be found";return
}
Write-Warning "Deleting all list items from $($ListName)"
$Items = $List.GetItems()
$Items | ForEach-Object{
$List.GetItemById($_.Id).Delete()
}
$List.Update()
$Web.Dispose()

SharePoint Saturday Louisville 2012


Over the weekend I spoke in front of a great crowd at the first annual SharePoint Saturday Louisville!

While I left Columbus with the intention of only doing my topic about building WCM sites using PowerShell, due to a cancellation I ended up doing two, back-to-back presentations. I’ll detail them below, and since this will be a long post, here are links to the different areas within the page:

  1. Build your SharePoint Internet presence with PowerShell
  2. Funnel your Info down a new Path

Build your SharePoint Internet presence with PowerShell

The topic: Build your SharePoint Internet presence with PowerShell
The story: Everybody knows PowerShell is powerful, it’s in the name! But did you know that PowerShell can read and understand XML? By leveraging XML among other things, complete builds can be automated – making them efficient and predictable.

In this fun, interactive and demo-filled session – I will show you how you can leverage PowerShell to help you build your branded, company website from the ground up using PowerShell and XML. I will also pass along some tips and tricks that will help you become a PowerShell Rockstar!

This was the first SharePoint Saturday in Louisville, and I was very proud to be a part of a great event.

My session went pretty flawlessly, as I lucked out and had zero issues with the presentation or the demos. For those of you who attended, thank you for being a part of it – and if you have any feedback or questions feel free to contact me either on Twitter or via e-mail (Ryan at SharePointRyan dot com).

I promised to upload my slides and demo PowerShell code, so the slides are on Slideshare (and displayed at the bottom of the post) and the XML and PowerShell code is below:

Demo Script – Setup-SPWebAppAndSite.ps1

XML Syntax:

<?xml version="1.0" encoding="utf-8"?>
<Sites>
	<WebAppConfig>
		<Name>Vandelay Industries</Name>
		<AppPool>SharePoint - VandelayDemo</AppPool>
		<AppPoolAccount>ISG1085\SpAppPool</AppPoolAccount>
		<DBServer>ISG1085\SharePoint</DBServer>
		<DBName>SP2010_Content_Vandelay_Demo</DBName>
		<HostHeader>www.vandelay.com</HostHeader>
		<InetpubPath>C:\inetpub\wwwroot\wss\VirtualDirectories</InetpubPath>
		<Port>80</Port>
		<Protocol>http://</Protocol>
		<OwnerAlias>ISG1085\SharePointRyan</OwnerAlias>
		<OwnerEmail>SharePointRyan@demowebsite.com</OwnerEmail>
		<SecondaryOwnerAlias>ISG1085\SPFarm</SecondaryOwnerAlias>
		<SecondaryOwnerEmail>SPFarm@demowebsite.com</SecondaryOwnerEmail>
		<RootSite>
			<WebTemplate>BLANKINTERNET#0</WebTemplate>
		</RootSite>
	</WebAppConfig>
	<!-- root site -->
	<Site Create="false">
		<SiteTitle>Home</SiteTitle>
		<SiteUrl>/</SiteUrl>
		<Page>
		   <PageTitle>Home</PageTitle>
		   <PageUrl>Home.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
	   </Page>	
		<Page>
		   <PageTitle>Terms of Use</PageTitle>
		   <PageUrl>Terms-of-Use.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
	   </Page>
		<Page>
		   <PageTitle>Privacy Policy</PageTitle>
		   <PageUrl>Privacy-Policy.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
	   </Page>
		<Page>
		   <PageTitle>Linking Policy</PageTitle>
		   <PageUrl>Linking-Policy.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
	   </Page>
		<Page>
		   <PageTitle>Legal Disclaimer</PageTitle>
		   <PageUrl>Legal-Disclaimer.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
	   </Page>	
		<Page>
		   <PageTitle>404 - Page Not Found</PageTitle>
		   <PageUrl>Page-Not-Found.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
	   </Page>			   		   
	</Site>		
	<!-- 2nd level sites -->
	<Site>
		<SiteTitle>Products</SiteTitle>
		<SiteUrl>/Products</SiteUrl>
		<Page>
		   <PageTitle>Awesome Widget</PageTitle>
		   <PageUrl>Awesome-Widget.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
		</Page>
		<Page>
		   <PageTitle>The Spectacular Thingamajig</PageTitle>
		   <PageUrl>The-Spectacular-Thingamajig.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
		</Page>
		<Page>
		   <PageTitle>Store</PageTitle>
		   <PageUrl>Store.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
		</Page>
	</Site>
	<Site>
		<SiteTitle>Solutions</SiteTitle>
		<SiteUrl>/Solutions</SiteUrl>
		<Page>
		   <PageTitle>Customer Engagement</PageTitle>
		   <PageUrl>Customer-Engagement.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
		</Page>
		<Page>
		   <PageTitle>Custom Solutions</PageTitle>
		   <PageUrl>Custom-Solutions.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
		</Page>
	</Site>
	<Site>
		<SiteTitle>Shipping and Returns</SiteTitle>
		<SiteUrl>/Shipping-and-Returns</SiteUrl>
		<Page>
		   <PageTitle>Shipping Policies</PageTitle>
		   <PageUrl>Shipping-Policies.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
		</Page>
		<Page>
		   <PageTitle>Return Policies</PageTitle>
		   <PageUrl>Return-Policies.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
		</Page>
	</Site>
	<Site>
		<SiteTitle>About Us</SiteTitle>
		<SiteUrl>/About-Us</SiteUrl>
		<Page>
		   <PageTitle>Company</PageTitle>
		   <PageUrl>Company.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
		</Page>
		<Page>
		   <PageTitle>History</PageTitle>
		   <PageUrl>History.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
		</Page>
		<Page>
		   <PageTitle>Locations</PageTitle>
		   <PageUrl>Locations.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Blank Web Part page</PageLayout>
		</Page>
		<Page>
		   <PageTitle>Employment Opportunities</PageTitle>
		   <PageUrl>Employment-Opportunities.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
		</Page>
	</Site>
	<Site>
		<SiteTitle>Contact Us</SiteTitle>
		<SiteUrl>/Contact-Us</SiteUrl>
		<Page>
		   <PageTitle>Social Media</PageTitle>
		   <PageUrl>Social-Media.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
		</Page>
		<Page>
		   <PageTitle>Locations</PageTitle>
		   <PageUrl>Locations.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
		</Page>
		<Page>
		   <PageTitle>Service Requests</PageTitle>
		   <PageUrl>Service-Requests.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
		</Page>
		<Page>
		   <PageTitle>Request a Catalog</PageTitle>
		   <PageUrl>Request-a-Catalog.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
		</Page>
	</Site>
	<Site>
		<SiteTitle>What We Do</SiteTitle>
		<SiteUrl>/What-We-Do</SiteUrl>
		<Page>
		   <PageTitle>Engagement</PageTitle>
		   <PageUrl>Engagement.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
		</Page>
		<Page>
		   <PageTitle>Ambassadors</PageTitle>
		   <PageUrl>Ambassadors.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
		</Page>
	</Site>
	<Site>
		<SiteTitle>Our Company</SiteTitle>
		<SiteUrl>/Our-Company</SiteUrl>
		<Page>
		   <PageTitle>Forms</PageTitle>
		   <PageUrl>Forms.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
		</Page>
		<Page>
		   <PageTitle>Travel</PageTitle>
		   <PageUrl>Travel.aspx</PageUrl>
		   <PageContent></PageContent>
		   <PageLayout>Body Only</PageLayout>
		</Page>
	</Site>
	<Site>
		<SiteTitle>Our Customers</SiteTitle>
		<SiteUrl>/Our-Customers</SiteUrl>
	</Site>
	<Site>
		<SiteTitle>News</SiteTitle>
		<SiteUrl>/News</SiteUrl>
	</Site>
</Sites>

PowerShell code:

<#
.Synopsis
	This PowerShell Script will create a new Web Application and root Site 
	Collection.
.Description
	This advanced script uses New-SPWebApplication, New-SPSite and 
	Enable-SPFeature to configure a web application and site collection using
	the Publishing Portal Site Template. Once the SPSite is created, 
	Standard and Enterprise Site and Web Features are activated.
.Example
	C:\PS>.\Setup-SPWebAppAndSite.ps1 -XmlInput .\SiteGenerationXml.xml
	
	This example runs with the default parameter values to configure a Web App
	and Site Collection based on the contents of an XML file called 
	SiteGenerationXml.xml in the current file directory.
.Notes
	Name: Setup-SPWebAppAndSite.ps1
	Author: Ryan Dennis
	Last Edit: 6/27/2012
	Keywords: New-SPWebApplication, New-SPSite
.Link
	http://www.sharepointryan.com
 	http://twitter.com/SharePointRyan
#Requires -Version 2.0
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)][string]$XmlInput,
[Parameter(Mandatory=$true)][string]$BlobCacheLocation
)
# Store start time in variable for calculation later #
$StartTime = Get-Date

# Clear Host for a clean, empty shell #
Clear-Host

# Read in list of sites from XML file and create variables for use later #
[xml]$SitesXml = Get-Content $($XmlInput)
if ($SitesXml -eq $null) { return }
$Name = $SitesXml.Sites.WebAppConfig.Name
$ApplicationPool = $SitesXml.Sites.WebAppConfig.AppPool
$ApplicationPoolAccount = $SitesXml.Sites.WebAppConfig.AppPoolAccount
$DatabaseServer = $SitesXml.Sites.WebAppConfig.DBServer
$DatabaseName = $SitesXml.Sites.WebAppConfig.DBName
$InetpubPath = $SitesXml.Sites.WebAppConfig.InetpubPath
$HostHeader = $SitesXml.Sites.WebAppConfig.HostHeader
$Port = $SitesXml.Sites.WebAppConfig.Port
$WebsitePath=$InetpubPath+"\"+$HostHeader+$Port
$Protocol = $SitesXml.Sites.WebAppConfig.Protocol
$Url = $Protocol+$HostHeader+":"+$Port
$OwnerAlias = $SitesXml.Sites.WebAppConfig.OwnerAlias
$OwnerEmail = $SitesXml.Sites.WebAppConfig.OwnerEmail
$SecondaryOwnerAlias = $SitesXml.Sites.WebAppConfig.SecondaryOwnerAlias
$SecondaryOwnerEmail = $SitesXml.Sites.WebAppConfig.SecondaryOwnerEmail
$WebTemplate = $SitesXml.Sites.WebAppConfig.RootSite.WebTemplate

# Start SP Assignment for object disposal later #
Start-SPAssignment -Global

# Change Shell Title #
$title = $Host.UI.RawUI.WindowTitle
$Host.UI.RawUI.WindowTitle = "Provisioning Employee Portal Web Application..."

# If SharePoint Snapin isn't loaded - load it #
if ((Get-PSSnapin Microsoft.SharePoint.PowerShell)-eq $null)
{
	Write-Host "Adding SharePoint Snapin"
	Add-PSSnapin Microsoft.SharePoint.PowerShell
}
$ErrorPref = $ErrorActionPreference
$ErrorActionPreference = "SilentlyContinue"
$WarningPref = $WarningPreference
$WarningPreference = "SilentlyContinue"

# Tell us the Web App is getting created #
Write-Host "------------------------------------------------------------------"
Write-Host " Step 1 of 10: Creating Web Application at Url $($Url)..."
Write-Host "------------------------------------------------------------------"

# Create the Web App #
New-SPWebApplication -Name $Name -ApplicationPool $ApplicationPool `
-ApplicationPoolAccount $ApplicationPoolAccount -DatabaseName $DatabaseName `
-DatabaseServer $DatabaseServer -HostHeader $HostHeader -Port $Port `
-Path $WebsitePath -Url $HostHeader | Out-Null

# Add the BUILTIN\Administrators group to Full Control Policy #
$userOrGroup = "BUILTIN\Administrators" 
$displayName = "Administrators" 

$WebApp = Get-SPWebApplication $Name 
$policy = $webApp.Policies.Add($userOrGroup, $displayName) 
$policyRole = $webApp.PolicyRoles.GetSpecialRole([Microsoft.SharePoint.Administration.SPPolicyRoleType]::FullControl) 
$policy.PolicyRoleBindings.Add($policyRole) 
$webApp.Update() 

# Change Shell Title #
$Host.UI.RawUI.WindowTitle = "Provisioning Root Site Collection..."

# Tell us the Site Collection is getting created #
Write-Host "------------------------------------------------------------------"
Write-Host " Step 2 of 10: Creating Root Site Collection..."
Write-Host "------------------------------------------------------------------"

# Create the Root Site #
$SPSite = New-SPSite -Name $Name -OwnerAlias $OwnerAlias -OwnerEmail $OwnerEmail `
-SecondaryOwnerAlias $SecondaryOwnerAlias -SecondaryEmail $SecondaryOwnerEmail `
-Template $WebTemplate -Url $Url | Out-Null

# Tell us the Site Collection is getting created #
Write-Host "------------------------------------------------------------------"
Write-Host " Step 3 of 10: Configuring Cache Settings..."
Write-Host "------------------------------------------------------------------"
Write-Host "Configuring BLOB Caching..."
# Enable SP Blob Cache #
$WebApp = Get-SPWebApplication $Url 
# SPWebConfigModification to enable BlobCache 
$configMod1 = New-Object Microsoft.SharePoint.Administration.SPWebConfigModification 
$configMod1.Path = "configuration/SharePoint/BlobCache" 
$configMod1.Name = "enabled" 
$configMod1.Sequence = 0 
$configMod1.Owner = "BlobCacheMod" 
## SPWebConfigModificationType.EnsureChildNode -> 0 
## SPWebConfigModificationType.EnsureAttribute -> 1 
## SPWebConfigModificationType.EnsureSection -> 2 
$configMod1.Type = 1 
$configMod1.Value = "true"
	
######################################################################

# SPWebConfigModification to enable client-side Blob caching (max-age) 
$configMod2 = New-Object Microsoft.SharePoint.Administration.SPWebConfigModification 
$configMod2.Path = "configuration/SharePoint/BlobCache" 
$configMod2.Name = "max-age" 
$configMod2.Sequence = 0 
$configMod2.Owner = "BlobCacheMod" 
## SPWebConfigModificationType.EnsureChildNode -> 0 
## SPWebConfigModificationType.EnsureAttribute -> 1 
## SPWebConfigModificationType.EnsureSection -> 2 
$configMod2.Type = 1 
$configMod2.Value = "86400" 

######################################################################

# SPWebConfigModification to change the default location for the Blob Cache files
$configMod3 = New-Object Microsoft.SharePoint.Administration.SPWebConfigModification
$configMod3.Path = "configuration/SharePoint/BlobCache"
$configMod3.Name = "location"
$configMod3.Sequence = "0"
$configMod3.Owner = "BlobCacheMod"
## SPWebConfigModificationType.EnsureChildNode -> 0 
## SPWebConfigModificationType.EnsureAttribute -> 1 
## SPWebConfigModificationType.EnsureSection -> 2 
$configMod3.Type = 1
$configMod3.Value = $BlobCacheLocation
# Add mods, update, and apply 
$WebApp.WebConfigModifications.Add( $configMod1 ) 
$WebApp.WebConfigModifications.Add( $configMod2 )
$WebApp.WebConfigModifications.Add( $configMod3 )
$WebApp.Update() 
$WebApp.Parent.ApplyWebConfigModifications() 

# Sleep for 5 seconds # 
Start-Sleep -Seconds 5

# Enable Output Cache for Root Site Collection # 
Write-Host "Configuring Output Caching..."
$SPSite = Get-SPSite $Url
$cache = New-Object Microsoft.SharePoint.Publishing.SiteCacheSettingsWriter $SPSite
$cache.EnableCache = $true
$cache.AllowPublishingWebPageOverrides = $true
$cache.EnableDebuggingOutput = $true
$cache.SetAnonymousPageCacheProfileId($SPSite, 1)
$cache.SetAuthenticatedPageCacheProfileId($SPSite, 4)
$cache.SetFarmCacheFlushFlag()
$cache.Update()
$SPSite.Dispose()

# Change Shell Title #
$Host.UI.RawUI.WindowTitle = "Activating Features..."

# Activate Standard and Enterprise Features #
Write-Host "------------------------------------------------------------------"
Write-Host " Step 4 of 10: Activating Features..."
Write-Host "------------------------------------------------------------------"
# Standard #
Write-Host "Enabling Standard Site and Web Features..."
Enable-SPFeature BaseSite -Url $Url
Enable-SPFeature BaseWeb -Url $Url
# Enterprise #
Write-Host "Enabling Enterprise Site and Web Features..."
Enable-SPFeature PremiumSite -Url $Url
Enable-SPFeature PremiumWeb -Url $Url
# Team Collaboration Lists #
Enable-SPFeature TeamCollab -Url $Url
# Mavention Meta Fields #
Write-Host "Enabling Mavention Meta Fields Feature..."
Install-SPSolution Mavention.SharePoint.MetaFields.wsp -WebApplication $Url -GACDeployment
Start-Sleep -Seconds 5
Enable-SPFeature -Identity a6ad433b-0bdf-40aa-aec8-6a091d7a3c26 -Url $Url

# Change Shell Title #
$Host.UI.RawUI.WindowTitle = "Configuring Search Center and Settings..."

# Remove OOTB Search Center Lite Web #
Write-Host "------------------------------------------------------------------"
Write-Host " Step 5 of 10: Removing Search Center Subsite..."
Write-Host "------------------------------------------------------------------"
Remove-SPWeb -Identity $Url"/search" -Confirm:$false -ErrorAction SilentlyContinue

# Create Enterprise Search Center Site Collection #
Write-Host "------------------------------------------------------------------"
Write-Host " Step 6 of 10: Creating Enterprise Search Center Site Collection..."
Write-Host "------------------------------------------------------------------"
$searchUrl = $Url+"/search"
New-SPManagedPath -Explicit -RelativeURL search -WebApplication $Url | Out-Null
New-SPSite -Name "Search Center" -OwnerAlias $OwnerAlias `
-OwnerEmail $OwnerEmail -SecondaryOwnerAlias $SecondaryOwnerAlias `
-SecondaryEmail $SecondaryOwnerEmail -Template "SRCHCEN#0" `
-Url $searchUrl | Out-Null
# Apply MaxSiteCount to content DB #
Set-SPContentDatabase -Identity $DatabaseName -MaxSiteCount 2 `
-WarningSiteCount 1 | Out-Null
Set-SPUser -Identity "NT Authority\Authenticated Users" -Web $searchUrl `
-Group "Search Center Visitors"

# Set Site Collection Search Center Settings #
Write-Host "------------------------------------------------------------------"
Write-Host " Step 7 of 10: Configuring Search Scope Settings..."
Write-Host "------------------------------------------------------------------"
<# These are the possible values for Dropdown
Do Not Show Scopes Dropdown, and default to contextual scope	HideScopeDD_DefaultContextual
Do Not Show Scopes Dropdown, and default to target results page	HideScopeDD
Show scopes Dropdown	ShowDD
Show, and default to ‘s’ URL parameter	ShowDD_DefaultURL
Show and default to contextual scope	ShowDD_DefaultContextual
Show, do not include contextual scopes	ShowDD_NoContextual
Show, do not include contextual scopes, and default to ‘s’ URL parameter	ShowDD_NoContextual_DefaultURL
#>

$SPWeb = Get-SPWeb $Url
$SPWeb.AllProperties["SRCH_ENH_FTR_URL"] = "/search/pages"
$SPWeb.AllProperties["SRCH_SITE_DROPDOWN_MODE"] = "ShowDD"
$SPWeb.AllProperties["SRCH_TRAGET_RESULTS_PAGE"] = "/search/pages/results.aspx"
$SPWeb.Update()
# Search Site #
$searchWeb = Get-SPWeb $searchUrl
$searchWeb.AllProperties["SRCH_ENH_FTR_URL"] = "/search/pages"
$searchWeb.AllProperties["SRCH_SITE_DROPDOWN_MODE"] = "ShowDD"
$searchWeb.AllProperties["SRCH_TRAGET_RESULTS_PAGE"] = "/search/pages/results.aspx"
$searchWeb.Update()

# Create Meta Fields #
Write-Host "------------------------------------------------------------------"
Write-Host " Step 8 of 10: Creating SEO Meta Fields..."
Write-Host "------------------------------------------------------------------"
#Assign fieldXML variable with XML string for site column
# Author
$AuthorFieldXML = '<Field Type="Text"
Name="MetaAuthor"
Description="Author Meta Tag"
DisplayName="Author"
StaticName="MetaAuthor"
Group="Publishing Meta Tags"
Hidden="FALSE"
Required="FALSE"
Sealed="FALSE"
ShowInDisplayForm="TRUE"
ShowInEditForm="TRUE"
ShowInListSettings="TRUE"
ShowInNewForm="TRUE"></Field>'
# Copyright
$CopyrightFieldXML = '<Field Type="Text"
Name="MetaCopyright"
Description="Copyright Meta Tag"
DisplayName="Copyright"
StaticName="MetaCopyright"
Group="Publishing Meta Tags"
Hidden="FALSE"
Required="FALSE"
Sealed="FALSE"
ShowInDisplayForm="TRUE"
ShowInEditForm="TRUE"
ShowInListSettings="TRUE"
ShowInNewForm="TRUE"></Field>'
# Description
$DescriptionFieldXML = '<Field Type="Text"
Name="MetaDescription"
Description="Description Meta Tag"
DisplayName="Description"
StaticName="MetaDescription"
Group="Publishing Meta Tags"
Hidden="FALSE"
Required="FALSE"
Sealed="FALSE"
ShowInDisplayForm="TRUE"
ShowInEditForm="TRUE"
ShowInListSettings="TRUE"
ShowInNewForm="TRUE"></Field>'
# Expiration
$ExpirationFieldXML = '<Field Type="Text"
Name="MetaExpiration"
Description="Expiration Meta Tag"
DisplayName="Expiration"
StaticName="MetaExpiration"
Group="Publishing Meta Tags"
Hidden="FALSE"
Required="FALSE"
Sealed="FALSE"
ShowInDisplayForm="TRUE"
ShowInEditForm="TRUE"
ShowInListSettings="TRUE"
ShowInNewForm="TRUE"></Field>'
# Keywords
$KeywordsFieldXML = '<Field Type="Text"
Name="MetaKeywords"
Description="Keywords Meta Tag"
DisplayName="Keywords"
StaticName="MetaKeywords"
Group="Publishing Meta Tags"
Hidden="FALSE"
Required="FALSE"
Sealed="FALSE"
ShowInDisplayForm="TRUE"
ShowInEditForm="TRUE"
ShowInListSettings="TRUE"
ShowInNewForm="TRUE"></Field>'
# Refresh
$RefreshFieldXML = '<Field Type="Text"
Name="MetaRefresh"
Description="Refresh Meta Tag"
DisplayName="Refresh"
StaticName="MetaRefresh"
Group="Publishing Meta Tags"
Hidden="FALSE"
Required="FALSE"
Sealed="FALSE"
ShowInDisplayForm="TRUE"
ShowInEditForm="TRUE"
ShowInListSettings="TRUE"
ShowInNewForm="TRUE"></Field>'
# Robots
$RobotsFieldXML = '<Field Type="Text"
Name="MetaRobots"
Description="Robots Meta Tag"
DisplayName="Robots"
StaticName="MetaRobots"
Group="Publishing Meta Tags"
Hidden="FALSE"
Required="FALSE"
Sealed="FALSE"
ShowInDisplayForm="TRUE"
ShowInEditForm="TRUE"
ShowInListSettings="TRUE"
ShowInNewForm="TRUE"></Field>'
# Title
$TitleFieldXML = '<Field Type="Text"
Name="MetaTitle"
Description="Title Meta Tag"
DisplayName="Title"
StaticName="MetaTitle"
Group="Publishing Meta Tags"
Hidden="FALSE"
Required="FALSE"
Sealed="FALSE"
ShowInDisplayForm="TRUE"
ShowInEditForm="TRUE"
ShowInListSettings="TRUE"
ShowInNewForm="TRUE"></Field>'

	$SPWeb = Get-SPWeb $Url
	$SPWeb.Fields.AddFieldAsXml($AuthorFieldXML)
	$SPWeb.Fields.AddFieldAsXml($CopyrightFieldXML)
	$SPWeb.Fields.AddFieldAsXml($DescriptionFieldXML)
	$SPWeb.Fields.AddFieldAsXml($ExpirationFieldXML)
	$SPWeb.Fields.AddFieldAsXml($KeywordsFieldXML)
	$SPWeb.Fields.AddFieldAsXml($RefreshFieldXML)
	$SPWeb.Fields.AddFieldAsXml($RobotsFieldXML)
	$SPWeb.Fields.AddFieldAsXml($TitleFieldXML)
	# Grab columns for manipulation
	$Author = $SPWeb.Fields["MetaAuthor"]
	$Copyright = $SPWeb.Fields["MetaCopyright"]
	$Description = $SPWeb.Fields["MetaDescription"]
	$Expiration = $SPWeb.Fields["MetaExpiration"]
	$Keywords = $SPWeb.Fields["MetaKeywords"]
	$Refresh = $SPWeb.Fields["MetaRefresh"]
	$Robots = $SPWeb.Fields["MetaRobots"]
	$Title = $SPWeb.Fields["MetaTitle"]
	# Rename Display Name
	$Author.Title = "Author"
	$Copyright.Title = "Copyright"
	$Description.Title = "Description"
	$Expiration.Title = "Expiration"
	$Keywords.Title = "Keywords"
	$Refresh.Title = "Refresh"
	$Robots.Title = "Robots"
	$Title.Title = "Title"
	# Update All
	$Author.Update()
	$Copyright.Update()
	$Description.Update()
	$Expiration.Update()
	$Keywords.Update()
	$Refresh.Update()
	$Robots.Update()
	$Title.Update()
	$SPWeb.Dispose()

# Add the columns to the Welcome Page content type #
$Web = Get-SPWeb $Url
$Ctype = $Web.ContentTypes["Welcome Page"]
$fields = $web.Fields | Where-Object {$_.group -eq "Publishing Meta Tags"}
$fields | ForEach-Object {
$Ctype.FieldLinks.Add($_)
$Ctype.Update($true)
$Web.Update()
}
$Web.Dispose()

# Change Shell Title #
$Host.UI.RawUI.WindowTitle = "Creating Subsites from XML..."

# Create subwebs from XML input #
Write-Host "------------------------------------------------------------------"
Write-Host " Step 9 of 10: Creating subsites from XML file..."
Write-Host "------------------------------------------------------------------"

Start-SPAssignment -Global

# Loop through each site node to extract data #
$SitesXml.Sites.Site | ForEach-Object {
	Write-Host "---------------------------------------------------------------"
	Write-Host " Step 9 of 10: Creating an SPWeb at $($SiteUrl)"
	Write-Host "---------------------------------------------------------------"
	$SiteTitle = [string]$_.SiteTitle
	$SiteUrl = [string]$_.SiteUrl
	$SiteOwner = [string]$_.Owner
	$SiteUser = [String]$_.User
	$WebTemplate = "CMSPUBLISHING#0"
	$LangId = "1033"
	$SiteUrl = $Url+$SiteUrl
	# Create the SPWeb #
	if ($_.create -ne "false")
	{
	$Web = New-SPWeb -Url $SiteUrl -Language $LangId -Template $WebTemplate `
	-Name $SiteTitle
	}
	# Get publishing site and web objects
	$site = New-Object Microsoft.SharePoint.SPSite($SiteUrl)
	$psite = New-Object Microsoft.SharePoint.Publishing.PublishingSite($site)
	$web = $site.OpenWeb()
	$pWeb = [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($web)

	# Create all Pages using ForEach-Object #
	if ($_.Page -ne $null){
	$pages = $_.Page | ForEach-Object {
	
	# Create page content variables from XML file
	$PageTitle = [string]$_.PageTitle
	$PageUrl = [string]$_.PageUrl
	$PageLayout = [string]$_.PageLayout
	#$PageContent = [xml]$_.PageContent
	$PageImage = [string]$_.PageImage
	$layout = $pWeb.GetAvailablePageLayouts() | Where-Object {$_.Title -match $PageLayout}
	
    # Create blank page using Add method
	Write-Host "Creating $($PageTitle).aspx page"
    $pages = $pWeb.GetPublishingPages($pWeb)
	$page = $pages.Add($PageUrl, $layout)
	$page.Update()
	
    # Update the filename to the one specified in the XML, add HTML content to Page Content zone
    $item = $page.ListItem
	$item["Title"] = $PageTitle;
	$item["Page Content"] = $PageContent;
	
	# Experimenting with adding a CEWP #
#	$page.CheckOut() 
#	$webpartmanager = $web.GetLimitedWebPartManager($page.Url,
#	[System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared) 
#	$webpart = New-Object  Microsoft.SharePoint.WebPartPages.ContentEditorWebPart
#	$webpart.ChromeType = [System.Web.UI.WebControls.WebParts.PartChromeType]::None;
#	$webpart.Title = "Added By PowerShell" 
#	$webpart.Content = $PageContent
#	$webpartmanager.AddWebPart($webpart, "Header", 0); 
	$item.Update()
	
    # Check-in and publish page
    $item.File.CheckIn("")
    $item.File.Publish("")
	$file = $item.File
	$pWeb.Update()
	
	} #end Page Foreach
	
	} # End if block #
	
} # End ForEach-Object loop #

# Upload Footer List and create list #
Write-Host "------------------------------------------------------------------"
Write-Host " Step 10 of 10: Creating Footer List and applying branding..."
Write-Host "------------------------------------------------------------------"
$site = Get-SPSite $Url
$Web = $site.OpenWeb()
$spFolder = $web.GetFolder("List Template Gallery")
$spFileCollection = $spFolder.Files
$file = Get-Item C:\Solutions\ConsumersEnergy\CEEP-FooterDirectoryList-LT.stp
$spFileCollection.Add("_catalogs/lt/CEEP-FooterDirectoryList-LT.stp", $file.OpenRead(), $true) | Out-Null
$listTemplates = $site.GetCustomListTemplates($web)
$Web.Lists.Add("CEEP-FooterDirectoryList","",$listTemplates["CEEP-FooterDirectoryList-LT"]) | Out-Null
$Web.Dispose()
$site.Dispose()
Enable-SPFeature -Identity "8d36bad6-e39a-41be-aff4-1a4be71ddcb6" -Url $Url | Out-Null




$EndTime = Get-Date
$TimeSpan = New-TimeSpan $StartTime $EndTime
$Mins = $TimeSpan.Minutes
$Secs = $TimeSpan.Seconds
$Time = $EndTime.ToShortTimeString()
# Tell us it's done #
Write-Host "------------------------------------------------------------------"
Write-Host " Portal Configuration Complete at $($Time)."
Write-Host " Process took $($Mins) minutes and $($Secs) seconds to complete."
Write-Host "------------------------------------------------------------------"

# Set the Window Title back to default #
$Host.UI.RawUI.WindowTitle = $title

# Hit the site to warm it up #
function get-webpage([string]$url,[System.Net.NetworkCredential]$cred=$null)
{
   $wc = new-object net.webclient
   if($cred -eq $null)
   {
     $cred = [System.Net.CredentialCache]::DefaultCredentials;
   }
   $wc.credentials = $cred;
   return $wc.DownloadString($url);
}

Write-Host "------------------------------------------------------------------"
Write-Host " Warming up the new Sites"
Write-Host "------------------------------------------------------------------" 
$cred = [System.Net.CredentialCache]::DefaultCredentials;
$sites = Get-SPSite -WebApplication $WebApp -Limit ALL
foreach ($site in $sites) {
$siteurl = $site.url
$spsite = New-Object Microsoft.SharePoint.SPSite($siteurl)
$pubsite = New-Object Microsoft.SharePoint.Publishing.PublishingSite($spsite)
foreach ($web in $site.AllWebs) {
$pubweb = [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($web)
$pages = $pubweb.GetPublishingPages($pubweb)
	foreach ($page in $pages) {
	$html = Get-WebPage -Url $page.uri -cred $cred;
	#Write-Host $page.uri
	}
}
}

# Dispose all objects #
Stop-SPAssignment -Global

# Set Error and Warning Preferences back #
$ErrorActionPreference = $ErrorPref
$WarningPreference = $WarningPref

# Open the site in IE #
$ie = New-Object -ComObject InternetExplorer.Application
$ie.Navigate($Url)
$ie.Visible = $true;

Funnel your Info down a new Path

The topic:  Funnel your Info down a new Path
The story:InfoPath provides business users with a familiar, feature rich means of creating dynamic, powerful electronic forms. While it has been around since the 2003 release of Microsoft Office, InfoPath 2010 combined with SharePoint 2010, is a match made in heaven.

InfoPath is heavily integrated into SharePoint 2010, and users can now create engaging forms within minutes and without writing a single line of code. This presentation will show you how you can start leveraging InfoPath to funnel your business information down a new path. Demos, gotchas, tips & tricks will all be a part of this 10,000 foot view of Microsoft InfoPath as it relates to SharePoint 2010.

SPS Louisville Slides

View more presentations from Ryan Dennis.