Create list views across webs using PowerShell or code

Sometimes you’ll want to have one basic list, which is accessible on all your subsites. One way to do this is using the content query web part. This however, blocks the functionalities a standard list view offers. So instead of the content query web part, we’ll use the basic XsltListViewWebPart. An important note: the standard ListViewWebPart class cannot work across your different webs inside your site collection. You have to use the Xslt one.

First, get an instance of the list you want to reference in your subsite:

$webWithList = Get-SPWeb http://weburl
$list = $webWithList.Lists["ListName"]

Then, get the web part manager of the page of your subsite where you want to insert the list view:

$targetWeb = Get-SPWeb http://weburl/subweb
$wpm = $targetWeb.GetLimitedWebPartManager("default.aspx", [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared);

Now that we’ve got both required objects, it’s time to create the XsltListViewWebPart:

$ListViewWebPart = New-Object Microsoft.SharePoint.WebPartPages.XsltListViewWebPart
$ListViewWebPart.ListId = $list.id
$ListViewWebpart.Title = $list.title
$ListViewWebPart.WebId = $list.parentweb.id

It’s very important the WebId is set or otherwise you’ll get a nasty error like this:

“List does not exist. The page you selected contains a list that does not exist. It may have been deleted by another user.”

Last but not least, add the web part to your page:

$wpm.AddWebPart($ListViewWebPart, "Left", 0)

In one flow the script looks like this:

$webWithList = Get-SPWeb http://weburl
$list = $webWithList.Lists["ListName"]
$targetWeb = Get-SPWeb http://weburl/subweb
$wpm = $targetWeb.GetLimitedWebPartManager("default.aspx", [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared);
$ListViewWebPart = New-Object Microsoft.SharePoint.WebPartPages.XsltListViewWebPart
$ListViewWebPart.ListId = $list.id
$ListViewWebpart.Title = $list.title
$ListViewWebPart.WebId = $list.parentweb.id
$wpm.AddWebPart($ListViewWebPart, "Left", 0)

If you would like for instance feature stapling, you can use the following code block to your web feature activation:

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    var web = properties.Feature.Parent as SPWeb;
    using (var rootWeb = web.Site.OpenWeb())
    {
        var list = rootWeb.Lists[Constants.ListName];
        string file = web.RootFolder.WelcomePage;
        var webPartManager = web.GetLimitedWebPartManager(file, PersonalizationScope.Shared);
        var webPart = new XsltListViewWebPart
        {
            ListId = list.ID,
            Title = list.Title,
            WebId = list.ParentWeb.ID
        };
        webPartManager.AddWebPart(webPart, "Left", 0);
    }
}

Setting several SharePoint sites read only

Setting a SharePoint site collection is quite an easy task. Setting several sites and subsites read only is not. There’s no cmdlet available to set one or more SharePoint sites read only.

For me read only means that all the permissions that exist are being put to “Read”. So here’s the script that does all the magic:

There are 3 parameters: The LogFilePath (for instance c:\temp\log.txt) where the old permissions are being written to for later consultation. The SiteUrl of the SharePoint site collection where the sites are located. And the exclusionSitesTitles is an array of titles of sites that should not be put read only. You can also change these to URLs, but then you have to edit the AllWebs line:

$webs = $site.AllWebs | ?{-not ($exclusionSitesTitles -contains $_.Title)}

Param (
  [Parameter(Mandatory=$True, Position=0)]
  [string]$LogFilePath,

  [Parameter(Mandatory=$True, Position=1)]
  [string]$SiteUrl,

  [Parameter(Mandatory=$True, Position=2)]
  [string[]]$exclusionSitesTitles
)

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$site = New-Object Microsoft.SharePoint.SPSite($siteUrl)
$webs = $site.AllWebs | ?{-not ($exclusionSitesTitles -contains $_.Title)}

function checkPermissions([Microsoft.SharePoint.SPRoleAssignmentCollection]$roles)
{
	$roles | Out-File -Append -FilePath $logfilepath
	$roles | %{
		if($_.RoleDefinitionBindings.Count -eq 1 -and $_.RoleDefinitionBindings.Contains($guestPermission))
		{
			$_.RoleDefinitionBindings.RemoveAll();
		}
		else
		{
			$_.RoleDefinitionBindings.RemoveAll();
			$_.RoleDefinitionBindings.Add($readPermission);
		}
		$_.Update();
	}
}

function checkLists($web)
{
	$web.Lists | %{
		if($_.HasUniqueRoleAssignments)
		{
			checkPermissions($_.RoleAssignments)
		}
	}
}

$webs | %{
	$readPermission = $_.RoleDefinitions.GetByType([Microsoft.SharePoint.SPRoleType]::Reader);
	$guestPermission = $_.RoleDefinitions.GetByType([Microsoft.SharePoint.SPRoleType]::Guest);
	$_.Url | Out-File -Append -FilePath $logfilepath
	if($_.HasUniqueRoleAssignments)
	{
		checkPermissions($_.RoleAssignments)
	}
	checkLists($_)
	$_.Dispose();
}
$site.Dispose();

Quick SharePoint farm overview using PowerShell

Getting a quick overview of where your data is can be handy at times. Even to create reports. Using built-in SharePoint tools you can easily get site collection information from your whole farm, and convert that information to a CSV. Once you got that CSV, you can do anything with it.

The following script will export the selected data to a CSV file, called “Export.csv”. It will do this for all of the site collections, besides the central administration, in your farm.

Get-SPSite -limit all | select Url,Owner,@{label="ContentDatabase";Expression={$_.ContentDatabase.Name}},@{label="Size in MB";Expression={$_.Usage.Storage/1MB}} | Export-CSV -Path Export.csv -NoTypeInformation

When you open the CSV, you’ll get an output like this:

"Url","Owner","ContentDatabase","Size in MB"
"http://sharepoint","fondant\administrator","SHP2013_Content_SharePoint80","1.93870639801025"
"http://sharepoint/sites/apps","fondant\administrator","SHP2013_Content_SharePoint80","1.1555118560791"
"http://sharepoint/sites/team","fondant\administrator","SHP2013_Content_SharePoint80","1.85408592224121"
"http://sharepoint:81","fondant\administrator","SP2013_Content_SharePoint81","0.111626625061035"

Now you can do practically anything with the data. If you want more properties, check out the SPSite object, and add these to the select statement.

Modify your SharePoint application pools using PowerShell

Sometimes your application pools need some maintenance. For instance changing their identity or if you wish to bundle some IIS sites to run under the same application pool so you won’t lose memory to overhead.

Changing the identity of the application pool is rather easy. You can use central admin or the Set-SPServiceApplicationPool cmdlet. Modifying an already running web application’s pool is a little bit different. There’s no cmdlet out of the box, so you’ll have to use the object model. There are 2 great scripts already available on technet. There’s one for changing the application pool, and another one for deleting the unused application pool.

If you wish to just copy paste directly into PowerShell, you can use the scripts below:

Note: the target applicationpool has to be created using SharePoint or else it won’t be recognized!

# Changing an application pool
$apppool = [Microsoft.SharePoint.Administration.SPWebService]::ContentService.ApplicationPools | where {$_.Name -eq "Applicationpoolname found in IIS"}
$webapp = get-spwebapplication -Identity http://sharepoint
$webapp.Applicationpool = $apppool
$webApp.Update()
$webApp.ProvisionGlobally()
# Deleting an application pool
$apppool = [Microsoft.SharePoint.Administration.SPWebService]::ContentService.ApplicationPools | where {$_.Name -eq "Applicationpoolname found in IIS"}
$apppool.UnProvisionGlobally()

Topology provisioning failed due to an error

When modifying your search topology, you might end up getting an error like this:

Errors were encountered during the configuration of the Search Service

Application.Microsoft.Office.Server.Search.Administration.SearchConfigWizard+SearchConfigWizardException:
Topology provisioning failed due to an error.Search Service Instance provisioning failed on SHAREPOINT. at
Microsoft.Office.Server.Search.Administration.SearchConfigWizard.WaitForTopologyTimerJobToFinish() at
Microsoft.Office.Server.Search.Administration.SearchConfigWizard.UpdateSearchApp() at
Microsoft.Office.Server.Search.Administration.SearchConfigWizard.ProvisionSearchServiceApplication() at
Microsoft.Office.Server.Search.Administration.SearchConfigurationJobDefinition.ExecuteTimerJob()
1/19/2012 4:39:02 PM

After this error occurs, the SharePoint Server Search service is on status “Error Starting”:

sharepointserversearcherrorstarting

For some reason, there’s a permission mismatch which occurs on the target server. It’s quite easy to fix actually, by manually starting the service using PowerShell. Firstly rdp to the server where the error happened and fire up the SharePoint 2010 Management Shell. The following 2 lines will start the service:

$si = Get-SPEnterpriseSearchServiceInstance -local
$si.Provision()

After the service is started, you may rerun the topology modification which will succeed for that particular server. Make sure, if you’re using a multiserver farm, that you run the PowerShell command on each server which is targeted in the search topology modification.

Copy site collection from source farm to target farm

Copying content allows the replication of production data in another environment. The result will be identical environments, which provides the ideal testing environment.

Prerequisites

In order to successfully restore your data, make sure you fulfil the following requirements:

  1. Your account is in the local admin group of the production environment
  2. Your account is in the local admin group of the target environment
  3. The production environment should have the same or a lesser patch version than the target environment.
  4. Both environments should have the same solutions deployed for the source and target web applications.
  5. Your target site collection should not exist to avoid conflicts.

Database restore

Ask the SQL team nicely to restore a copy of the production content database of your source web application where the site collection resides, to the target SQL instance.

Note: Make sure the dbo of the target database is the web application service account of your target farm.

When the target database is restored, it’s time to bring it into SharePoint. Here a first decision has to be made:

Scenario 1: Different managed paths

In the case of different source and target managed paths, it’s impossible to directly attach the content database to the target web application.

  1. In order to successfully attach the content database, create a new –empty- web application. Empty means that there are no site collections created in the attached content databases of the web application.
  2. Attach the restored contentdatabase using the following command:
    Mount-SPContentDatabase -Name "RestoredDatabase" –WebApplication http://emptywebapp/
    

    When the command is finished you get info about your new database:

  3. If your source farm has a different version than the target farm, you have to run an extra command to upgrade your contentdatabase to the right version:
    $db = Get-SPContentDatabase | ?{$_.Name –eq "RestoredDatabase"}
    Upgrade-SPContentDatabase $db
    

    The upgrade process might generate errors. The most common cause is that the source web application’s solutions aren’t deployed on the empty web application. You may safely ignore this error.

  4. Now that your content database is attached it’s time to move the site to the correct web application and managed path.
    Backup-SPSite http://emptywebapp/ -Path C:\TEMP\backup.bak
    Restore-SPSite http://sharepoint/site -Path C:\TEMP\backup.bak

    Make sure that when you restore your site, you target the right contentdatabase. You can use the –DatabaseName switch to specify where to restore the site collection on the target web application’s content databases.

  5. Make sure that you clean up after your restore was successful.
    • Delete the temporary bak file
    • Delete the restored content database
    • Delete the temporary web application
    • Delete the temporary web application’s content database

backuprestoremanagedpath

Scenario 2: Same managed paths

When the source and target web application’s site collection have the same managed path, it’s possible to directly attach the restored database to the target web application. You do this with the following steps:

  1. Attach the restored contentdatabase using the following command:
    Mount-SPContentDatabase -Name "RestoredDatabase" –WebApplication http://emptywebapp/
    
  2. If your source farm has a different version than the target farm, you have to run an extra command to upgrade your contentdatabase to the right version:
    $db = Get-SPContentDatabase | ?{$_.Name –eq "RestoredDatabase"}
    Upgrade-SPContentDatabase $db
    

Result

When a site is moved from production to another environment, all it’s content is moved. Take into account that data might also reside in other places like the managed metadata term store.

Working with SPWeb’s Propertybag

The most common scenario for working with site settings is using the site’s propertybag. When you look at the SPWeb’s members, you might notice the following properties and methods:

It’s important to note that the methods of SPWeb that handle properties, reflect upon the SPWeb.AllProperties property and not on the SPWeb.Properties property. Only the AllProperties property should be used, the Properties property is added to SharePoint 2010 in order to provide backwards compatibility with older applications.

A big improvement of the SPWeb.AllProperties is that instead of a StringDictionary, you get a HashTable where you can store objects instead of strings. There is a restriction here though: Only String, INT, and DateTime datatypes can be used as the value in AllProperties.

An example of using SPWeb properties in PowerShell, the same can be used in C#:

$web = Get-SPWeb http://sharepoint
$web.AddProperty("SPWebObjectKey", $web)
$web.Update()
$tempWeb = $web.GetProperty("SPWebObjectKey")
Write-Host $tempWeb.Url

This outputs to http://sharepoint

Now that we have used our property and no longer need it, it’s time to delete it:

$web.DeleteProperty("SPWebObjectKey")
$web.Update()