Tuesday, 22 February 2011

Finding site template names and ID’s in SharePoint using PowerShell

SharePoint 2010 includes the new Get-SPWebTemplate cmdlet for getting a list of site templates from the farm. You can use it by typing the following command (note that I am also sorting the table by template internal name):

Get-SPWebTemplate | Sort-Object "Name"

image

This can be quite useful, especially if you want to use PowerShell to create new sites with the New-SPSite and New-SPWeb cmdlets and need to know an internal name of the template that you wish to base the new site on.

You may also want to extend the use of the Get-SPWebTemplate cmdlet to provide additional information. For example, you may want to include the template ID in the table. This can be done by stepping through each template and adding the results to a PSObject, as follows:

function Get-SPWebTemplateWithId
{
    $templates = Get-SPWebTemplate | Sort-Object "Name"
    $templates | ForEach-Object {
        $templateValues = @{
            "Title" = $_.Title
            "Name" = $_.Name
            "ID" = $_.ID
            "Custom" = $_.Custom
            "LocaleId" = $_.LocaleId
        }
        New-Object PSObject -Property $templateValues | Select @("Name","Title","LocaleId","Custom","ID")
    }
}
Get-SPWebTemplateWithId | Format-Table

Running this script will output the following table:

image

If you looking to find details of the site template used to create an existing site in a site collection, this can be done by attaching to the site through the SPWeb object and getting the WebTemplate and WebTemplateId property values, as follows:

$web = Get-SPWeb http://portal
write-host "Web Template:" $web.WebTemplate " | Web Template ID:" $web.WebTemplateId
$web.Dispose()

This will produce an output similar to the text below:

Web Template: BLANKINTERNET  | Web Template ID: 53

Finally, if you can’t be doing with all these internal names and ID’s and need to get a site template for use in a site creation script, you can use the display name (i.e., Title) of the template with the following command:

$template = Get-SPWebTemplate | where { $_.Title -eq "Team Site" }

image

Putting this all together, the following script will get the “Blank Site” template and create a new sub-site in an existing site collection:

$template = Get-SPWebTemplate | where { $_.Title -eq "Blank Site" }
$newWeb = New-SPWeb -Url http://portal/subsite1/subsite2 -Name "Subsite Test" -Description "This is a test site" -Template $template -AddToTopNav -AddToQuickLaunch -UniquePermissions
write-host "Site" $newWeb.Url "created at" (Get-Date).ToString()
$newWeb.Dispose()

You can get more details on the New-SPSite and New-SPWeb cmdlets from here and here respectively, or type get-help New-SPSite and get-help New-SPWeb from a PowerShell command prompt.

Remove SharePoint or AD group/user assignments from all sites in a site collection using PowerShell

In my last post, I ran through how to use PowerShell for adding SharePoint groups or Active Directory users/groups with a specific permission level to all sites in a site collection. Here, I am going to use the same process for removing assignments on users and groups from all sites.

The script steps through each site and removes the account specified. I have also included an option to skip the root site, should you wish to remove the account from sub-sites only. Note: Using this script will not delete users and groups from the site collection permanently – even if all their permissions are removed from every site. They will still exist in the site collection for future permission assignment, if required.

As before, you will need to run the function first in a PowerShell console with the SharePoint cmdlets loaded (e.g., the SharePoint 2010 Management Shell) before anything can be modified. I have annotated portions of the script so that you can hopefully follow what it is doing:

function RemoveAccountFromAllSites ($siteURL, $accountName, [switch]$skipRootSite)
{
    #Get Site Collection
    $site = Get-SPSite $siteURL
   
    #Check if the accountName variable contains a slash - if so, it is an AD account
    #If not, it is a SharePoint Group
    $rootWeb = $site.RootWeb
    if ($accountName.Contains("\")) { $account = $rootWeb.EnsureUser($accountName) }
    else { $account = $rootWeb.SiteGroups[$accountName] }
    $rootWeb.Dispose()
   
    #Step through each site in the site collection
    $site | Get-SPWeb -limit all | ForEach-Object {
       
        #Check if the user has chosen to skip the root site - if so, do not change permissions on it
        if (($skipRootSite) -and ($site.Url -eq $_.Url)) { write-host "Root site" $_.Url "will be bypassed" }
        else {
            #Check if the current site is inheriting permissions from its parent
            #If not, remove permissions on current site
            if ($_.HasUniqueRoleAssignments) {
                write-host "Removing account" $accountName "from site" $_.Url
                $_.RoleAssignments.Remove($account)
            }
            else {
                write-host "Site" $_.Url "will not be modified as it inherits permissions from a parent site."
            }
        }
    }
    #Display completion message and dispose of site object
    write-host "Operation Complete."
    $site.Dispose()
}

Then, use the function to remove users and group assignments in the site collection, as shown in the following examples:

  • Remove the Active Directory user “PACDOMAIN\Phil” from all sites except the root site
RemoveAccountFromAllSites –siteURL “http://portal” -accountName "PACDOMAIN\Phil" -skipRootSite
  • Remove the Active Directory user “PACDOMAIN\Phil” from all sites including the root site
RemoveAccountFromAllSites –siteURL “http://portal” -accountName "PACDOMAIN\Phil"
  • Remove the Active Directory group “PACDOMAIN\Portal Users” from all sites including the root site
RemoveAccountFromAllSites –siteURL “http://portal” -accountName "PACDOMAIN\Portal Users"
  • Remove the SharePoint group “Test Group” from all sites except the root site

RemoveAccountFromAllSites –siteURL “http://portal” -accountName "Test Group" –skipRootSite

Below is an example output you receive in the console when running the script:

Root site http://portal will be bypassed
Site http://portal/perminherited will not be modified as it inherits permissions from a parent site.
Site http://portal/perminherited/InheritedSite will not be modified as it inherits permissions from a parent site.
Removing account Test New Group from site http://portal/perminherited/UniqueSite
Removing account Test New Group from site http://portal/permuniquesite
Site http://portal/search will not be modified as it inherits permissions from a parent site.
Operation Complete.

Thursday, 17 February 2011

Add a SharePoint or AD group/user to all sites in a site collection using PowerShell

I have written this script based on some feedback I received in this article, which has become one of the more popular posts on the site. I was asked if there was any way of assigning group permissions for all sites in a site collection, bearing in mind that some sites will have unique permissions set on them whereas others will inherit their permissions from a parent site.

The function below provides this functionality, as well as a few other features:

  • Skips sites where permissions are being inherited from a parent site
  • Adds not only SharePoint groups to sites, but also Active Directory users and groups
  • Provides the option to skip the root site of the site collection, should you only wish to set permissions on all sub-sites
  • Will add a new SharePoint group to the site collection, if it doesn’t exist already. The script will allow you to add a description for the group, and it will automatically assign the user running the script as group owner and member

Before you can start assigning permissions to sites using the script, you have to run the function first in a PowerShell console with the SharePoint cmdlets loaded (e.g., the SharePoint 2010 Management Shell). I have annotated portions of the script so that you can hopefully follow what it is doing:

function AddAccountToAllSites ($siteURL, $accountName, $permLevel, [switch]$skipRootSite, $newGroupDescription)
{
    #Get Site Collection
    $site = Get-SPSite $siteURL
   
    #Check if the accountName variable contains a slash - if so, it is an AD account
    #If not, it is a SharePoint Group
    $rootWeb = $site.RootWeb
    if ($accountName.Contains("\")) { $account = $rootWeb.EnsureUser($accountName) }
    else {
        #If the SharePoint Group does not exist, create it with the name and description specified
        if (!$rootWeb.SiteGroups[$accountName])
        {
            $rootWeb.SiteGroups.Add($accountName, $rootWeb.CurrentUser, $rootWeb.CurrentUser, $newGroupDescription)
        }
        $account = $rootWeb.SiteGroups[$accountName]
    }
    $rootWeb.Dispose()
   
    #Step through each site in the site collection
    $site | Get-SPWeb -limit all | ForEach-Object {
       
        #Check if the user has chosen to skip the root site - if so, do not change permissions on it
        if (($skipRootSite) -and ($site.Url -eq $_.Url)) { write-host "Root site" $_.Url "will be bypassed" }
        else {
            #Check if the current site is inheriting permissions from its parent
            #If not, set permissions on current site
            if ($_.HasUniqueRoleAssignments) {

                $assignment = New-Object Microsoft.SharePoint.SPRoleAssignment($account)
                $role = $_.RoleDefinitions[$permLevel]
                $assignment.RoleDefinitionBindings.Add($role)
                $_.RoleAssignments.Add($assignment)
       
                write-host "Account" $accountName "added to site" $_.Url "with" $permLevel "permissions."
            }
            else {
                write-host "Site" $_.Url "will not be modified as it inherits permissions from a parent site."
            }
        }
    }
    #Display completion message and dispose of site object
    write-host "Operation Complete."
    $site.Dispose()
}

Once the script has been run, you can use it to assign permissions to your site collection by calling the function. Here are some scenarios:

  • Add the Active Directory user PACDOMAIN\Phil to all sites except the root site and assign Read permissions to them:

AddAccountToAllSites -siteURL "http://portal" -accountName "PACDOMAIN\Phil" -permLevel "Read" -skipRootSite

  • Add the Active Directory user PACDOMAIN\Phil to all sites including the root site and assign Read permissions to them:

AddAccountToAllSites -siteURL "http://portal" -accountName "PACDOMAIN\Phil" -permLevel "Read"

  • Add the Active Directory group PACDOMAIN\Portal Users to all sites including the root site and assign Read permissions to it:

AddAccountToAllSites -siteURL "http://portal" -accountName "PACDOMAIN\Portal Users" -permLevel "Read"

  • Add the SharePoint group “Test Group” to all sites except the root site and assign Full Control permissions to it. I am also assuming that this group has already been created in the site collection:

AddAccountToAllSites -siteURL "http://portal" -accountName "Test Group" -permLevel "Full Control" -skipRootSite

  • Add the SharePoint group “Test New Group” to all sites except the root site and assign Contribute permissions to it. This time I would like to create the group in the site collection as it doesn’t currently exist, and so I am also specifying the group description to be used during creation:

AddAccountToAllSites -siteURL "http://portal" -accountName "Test New Group" -permLevel "Contribute" -skipRootSite -newGroupDescription "This is a test group"

The screenshot below shows the affect of running these commands on one of the sites configured with unique permissions. All sites inheriting permissions will not be changed, although they will inherit these changes if their parent site has been affected by them.

image

I have posted a follow up article demonstrating how users and group assignments can be removed from sites in a similar way.

Wednesday, 9 February 2011

Export and import/create site content types in SharePoint using PowerShell

In my previous article, I ran through how to export custom site columns from one site collection and import them into another site collection using PowerShell. In this article, I will be following the same process, but this time for copying site content types from one site collection to another.

As described previously, SharePoint Server 2010 does contain the new Content Type Hub feature for distributing site columns and content types across multiple site collections, but there still may be scenarios where the feature cannot be used – e.g., if using SharePoint Foundation, migrating columns and content types between farms, or for documenting an environment.

As with the previous site column article, we can use PowerShell to export the content type schema from a source site collection into an XML file, which can then be used to import the same content types into another site collection.

Please note (Part 1): The script used here assumes that you have already migrated the site columns used in the these content types to the destination site collection. You could, of course, write a combined script that covers exporting/importing both site columns and content types to ensure the whole process is dealt with as a one-click operation.

Please note (Part 2): The script only supports creating the content type in a destination site and associating the appropriate site columns to it. It does not copy across other features of the content type – e.g., workflow associations, information management policies, document template, etc. – although you could expand the script to support extra features, if required.

For this example, I have created the following site content types under the “Custom Content Types” group in my development site collection:

image

I have also added a required “Description” column to the “Company Document” content type (from which the other custom content types inherit) and an optional “Category” column to the “IT Document” content type.

The PowerShell script below will export these content types to an XML file called Script-SiteContentTypes.xml in the C:\Install folder. Note that the script looks at the group called “Custom Content Types” to ensure content types created by a standard SharePoint installation are not included in the export. You will need to ensure that appropriate custom group name(s) are specified in your script when exporting:

$sourceWeb = Get-SPWeb http://portal
$xmlFilePath = "C:\Install\Script-SiteContentTypes.xml"

#Create Export File
New-Item $xmlFilePath -type file -force

#Export Content Types to XML file
Add-Content $xmlFilePath "<?xml version=`"1.0`" encoding=`"utf-8`"?>"
Add-Content $xmlFilePath "`n<ContentTypes>"
$sourceWeb.ContentTypes | ForEach-Object {
    if ($_.Group -eq "Custom Content Types") {
        Add-Content $xmlFilePath $_.SchemaXml
    }
}
Add-Content $xmlFilePath "</ContentTypes>"

$sourceWeb.Dispose()

This script will generate an XML file similar to the one shown below.

image

Once you have the XML file, it can be used to import the content types into another site collection by using the script below. The first part of the script gets the destination Web URL and exported XML file:

$destWeb = Get-SPWeb http://portal/sites/migrationtest
$xmlFilePath = "C:\Install\Script-SiteContentTypes.xml"

The final part of the script cycles through each content type specified in the XML file, creates the content type with the exported description and group name, and finally reads in each column associated with the exported content type and wires them up to the new content type in the destination site collection, specifying if they are hidden or required columns during the process.

#Create Site Content Types
$ctsXML = [xml](Get-Content($xmlFilePath))
$ctsXML.ContentTypes.ContentType | ForEach-Object {

    #Create Content Type object inheriting from parent
    $spContentType = New-Object Microsoft.SharePoint.SPContentType ($_.ID,$destWeb.ContentTypes,$_.Name)
   
    #Set Content Type description and group
    $spContentType.Description = $_.Description
    $spContentType.Group = $_.Group
   
    $_.Fields.Field  | ForEach-Object {
        if(!$spContentType.FieldLinks[$_.DisplayName])
        {
            #Create a field link for the Content Type by getting an existing column
            $spFieldLink = New-Object Microsoft.SharePoint.SPFieldLink ($destWeb.Fields[$_.DisplayName])
       
            #Check to see if column should be Optional, Required or Hidden
            if ($_.Required -eq "TRUE") {$spFieldLink.Required = $true}
            if ($_.Hidden -eq "TRUE") {$spFieldLink.Hidden = $true}
       
            #Add column to Content Type
            $spContentType.FieldLinks.Add($spFieldLink)
        }
    }
   
    #Create Content Type on the site and update Content Type object
    $ct = $destWeb.ContentTypes.Add($spContentType)
    $spContentType.Update()
    write-host "Content type" $ct.Name "has been created"
}

$destWeb.Dispose()

Once the script has been run, the custom site content types exported from the original site collection will be present in the new site collection…

image

…with the “Description” and “Category” columns linked to them as expected.

image