Get a Web, List and Library Inventory using PowerShell


A colleague of mine recently asked me “Ryan, can you give me some PowerShell code that can give me a list of all sites and sub sites as well as all lists and libraries within each of those sites – for an entire web application?”

“Of course”, I said…

I had some other scripts and functions that were similarly constructed, so I simply took one that was close and adapted it to make it work.

This function, which I’ve called “Get-SPSiteInventory” will run against either an entire Web Application (using the -WebApplication switch param) or a single Site Collection (using the -SiteCollection switch param).

I’ve tested this both by sending the output straight to a file (using the Out-File cmdlet) as well as just running in the shell – both work pretty nicely.

I’ve excluded comment-based help for better readability, and the syntax is as follows…

To run against a site collection:

Get-SPSiteInventory -Url http://spsite -SiteCollection

To run against a web application:

Get-SPSiteInventory -Url http://spwebapp -WebApplication

The entire function:

function Get-SPSiteInventory {
Param(
[string]$Url,
[switch]$SiteCollection,
[switch]$WebApplication
)
Start-SPAssignment -Global
	if ($SiteCollection) {
		$site = Get-SPSite $Url
		$allWebs = $site.allwebs
		foreach ($spweb in $allWebs) {
			" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "
			$spweb.Url
			$spweb.Lists | select Title, BaseType
			$spweb.dispose()
		}
		$site.dispose()
	} 
	elseif ($WebApplication) {
		$wa = Get-SPWebApplication $Url
		$allSites = $wa | Get-SPSite -Limit all
		foreach ($spsite in $allSites) {
			$allWebs = $spsite.allwebs
			foreach ($spweb in $allWebs) {
			" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "
			$spweb.Url
			$spweb.Lists | select Title, BaseType
			$spweb.dispose()
			}
		}
	}
Stop-SPAssignment -Global
}

Remove SharePoint List Views Using PowerShell


Ever have a need to delete list views using PowerShell?  Of course you do, it’s a great reason to use PowerShell!

Luckily for us, there is great access to the Object Model and we can readily access lists, items, and of course views.

I threw together a nifty little function that accepts three parameters, WebUrl, ListName and ViewName and will remove a single view from a SharePoint list.

The code is quite simple, basically we grab an SPWeb object using the WebUrl parameter – we then grab an SPList using the ListName parameter, and finally we grab an SPListView using the ViewName parameter.  Once we’ve drilled down to the specific SPListView, we can call the Delete() method using the ID of the view.

Here’s my function, enjoy!

function Remove-SPListView {
Param(
[string]$WebUrl,
[string]$ListName,
[string]$ViewName
)
Start-SPAssignment -Global
$SPWeb = Get-SPWeb $WebUrl
$List = $SPWeb.Lists[$ListName]
$View = $List.Views[$ViewName]
$List.Views.Delete($View.ID)
$List.Update()
$SPWeb.Update()
$SPWeb.Dispose()
Stop-SPAssignment -Global
}

Use Find/Replace in PowerShell to fix InfoPath Forms after moving to a new Site Collection


I know I know, that’s a really long title.

I found some pretty good PowerShell how-to’s which described how to use Get-ChildItem to iterate through a folder – checking each item for a string and then using -replace to fix the string.

However, my situation was slightly different as I wanted to walk through a SharePoint 2010 Form Library – fixing InfoPath forms which had been moved into a new site collection and thus did not open in the browser and would not save due to the old reference to the XSN.

I’m sure there may be better methods on moving XSNs and XMLs across site collections where maybe this wouldn’t be necessary, but if you’re reading this I’ll assume you don’t care.

In any case, what I ended up doing (after obviously re-publishing the XSN template to the new site collection so new IP forms wouold work) is:

  1. Copy all of the forms using Windows Explorer (we didn’t care about versions or modified by metadata, just the content type metadata) into the new Form Lib.
  2. Run the following PowerShell code to fix each XML file – replacing the link to the old XSN with a link to the new XSN (Note: I ran this on my desktop, NOT on the server as I didn’t need any SP cmdlets):
$files = Get-ChildItem \\sitecollection\davwwwroot\FormLib -Filter "*.xml"
foreach ($file in $files) {
(Get-Content $file.fullname) | ForEach-Object {$_ -replace "http://sitecollection/library/OLDXSNPATH.xsn", "http://newsitecollection/library/NEWXSNPATH.xsn"} | Set-Content $file.fullname
}

Voila, all IP forms are now fixed and will not only open in the browser as expected – but I can now update these forms in the new location as they now point to the correct XSN template.

If you’re a frequent reader you’re probably thinking “Wow, normally he gives us these nice functions with parameters, help, etc.”… You’re right, this time it was a quick and dirty chunk of code. However, chances are I’ll need to do this again – so I did actually write a function for future needs:

function Reset-InfoPathTemplateLink {
Param(
[string]$FilePath,
[string]$FileExtension,
[string]$OldPath,
[string]$NewPath
)
$files = Get-ChildItem $FilePath -Filter $FileExtension
	foreach ($file in $files) {
		(Get-Content $file.fullname) |
		ForEach-Object {$_ -replace $OldPath,$NewPath} |
		Set-Content $file.fullname
	} #end foreach
} #end function

To run the function, first dot-source it and then use the following syntax:

Reset-InfoPathTemplateLink -FilePath "\\sitecollection\subsite\formlib" -FileExtension "*.xml" -OldPath "http://sitecollection/doclib/template.xsn" -NewPath "http://newsitecollection/newdoclib/newtemplate.xsn"

As always, happy PowerShelling!

SharePoint Saturday Cincinnati 2011


This past weekend I broke new ground in my career – I completed my first public speaking engagement! While I have spoken on various topics in front of my SharePoint Team at ICC – this was my first public speaking engagement and overall I think it went very well.

The topic: The Power is in the Shell, use it wisely!
The story: What is PowerShell – how does it fit into the SharePoint 2010 puzzle? How can we take commands run in the Shell and easily convert them to scripts or functions? How can we use the Get-Content cmdlet and some XML to automate content loading or migrations? What are some best practices around PowerShell as it relates to SharePoint 2010?

This was the first SharePoint Saturday in Cincinnati, 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 three demos. I had a small group of attendees, but all of them were very engaged in dialog throughout. 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 (rdennis at iccohio 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 below:

First demo function – Set-SPWebTitle

function Set-SPWebTitle {
Param(
[string]$SiteUrl,
[string]$SiteTitle
)
$site = Get-SPSite $SiteUrl
$web = $site.RootWeb                                                                                                   
$web.Title = $SiteTitle
Write-Host "Changing title to:" $SiteTitle
$web.Update()                                                                                                          
$web.Dispose()                                                                                                        
$site.Dispose()           
Write-Host "Finished!"
}

Second demo function – New-SPWebFromXml
XML Syntax:

<?xml version="1.0" encoding="utf-8"?>
<Sites>
  	<Site>
    		<SiteTitle>Ryan Dennis</SiteTitle>
	    	<SiteUrl>http://sps.adventureworks.com/rdennis</SiteUrl>
	    	<Page>
			<PageTitle>Home</PageTitle>
			<PageUrl>Home.aspx</PageUrl>
			<PageContent>HTML Content</PageContent>
			<PageLayout>Body Only</PageLayout>
		</Page>
  	</Site>
</Sites>

PowerShell code:

function New-SPWebFromXml {
# Xml Input parameter - accepts string to filename
[CmdletBinding()]
	Param(
    [Parameter(Mandatory=$true)]
	[string]$XmlInput
	)

# Read in list of sites from XML file
[xml]$SitesXml = Get-Content $($XmlInput)
if ($SitesXml -eq $null) {return}
Start-SPAssignment -Global #Stores all objects in the global store for proper disposal
$StartTime = Get-Date #Grab the date & time for further calculation later

# Upload speaker images to the PublishingImages library
Write-Host "Uploading speaker images to the PublishingImages library..."
Add-SPFilesToLibrary -WebUrl http://sps.adventureworks.com -LibraryName PublishingImages -FileExtension "*.jpg" -FolderLocation "C:\SPSDemo\Speakers"

	# Loop through each site node to extract data
	$SitesXml.Sites.Site | ForEach-Object {
	$SiteTitle = [string]$_.SiteTitle
	$SiteUrl = [string]$_.SiteUrl
	$WebTemplate = "CMSPUBLISHING#0"
	$LangId = "1033"
	
	# 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 the SPWeb
	Write-Host "Creating an SPWeb at $($SiteUrl)"
	New-SPWeb -Url $SiteUrl	-Language $LangId -Template $WebTemplate -Name $SiteTitle
	
	# Create page content variables from XML file
	$PageTitle = [string]$_.Page.PageTitle
	$PageUrl = [string]$_.Page.PageUrl
	$PageLayout = [string]$_.Page.PageLayout
	$PageContent = [string]$_.Page.PageContent
	$PageImage = [string]$_.Page.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;
	$item.Update()
	
    # Check-in and publish page
    $item.File.CheckIn("")
    $item.File.Publish("")
	$file = $item.File
	$pWeb.DefaultPage = $file
	$pWeb.Update()
	
	# Get and delete original default.aspx page
	Write-Host "Deleting default.aspx page"
	$list = $web.lists["Pages"]
	$oldPage = $list.items | Where-Object {$_.Name -match "default"}
	$oldPage.Delete();
	$web.Update()
	} #End ForEach-Object loop
	
$EndTime = Get-Date #Grab the ending date & time to get the timespan
$TimeSpan = New-TimeSpan $StartTime $EndTime #Calculate total time taken
Write-Host "$($SitesXml.Sites.Site.Count) sites created in $timespan"
Stop-SPAssignment -Global #Stop assignment, safely disposing all site and web objects
} #End Function

Generic Error on Listedit.aspx page after SP1 and Aug 2011 CU


Wow did this issue get under my skin.

At my current client, where their SharePoint 2010 Enterprise Intranet is about as business critical as a SP environment can be – I performed the SP1 and Aug 2011 CU updates. The updates themselves went about as smooth as could be expected, until we noticed that lists and libraries would give the generic error w/ correlation ID when navigating to the List Settings page (/_layouts/listedit.aspx).

This seemed like not too big of a deal on the first site collection we noticed it on – then we realized it was farm-wide.

After doing a lot of log-reading, re-running PSCONFIG.exe to ensure that everything completed from an upgrade perspective; we were in the dark…

This morning I noticed something while browsing through the layouts folder – listedit.aspx (and some other files) was modified on 6/8/2011 at 11:46 AM. Most of the other aspx files were showing a modified date much further in the past.

On a whim I copied listedit.aspx from a known-good farm into my broken farm and VOILA – lists and libraries work again.

This might not help anybody, because I couldn’t find anyone else having this exact issue – but I didn’t want to lose track of the “fix.”