Automating Microsoft SharePoint 2010 with Windows PowerShell 2.0 (book excerpt)

 

Applies to: SharePoint Server 2010, SharePoint Foundation 2010

This book excerpt is from Automating Microsoft SharePoint 2010 Administration with Windows PowerShell 2.0, (Wiley Publishing, Inc., June 2011).

Buy this book

Automating Microsoft SharePoint 2010 Administration with Windows PowerShell 2.0 focuses on the automation of Microsoft SharePoint Server 2010 with specific emphasis on deployment and configuration. This book assumes no prior Windows PowerShell knowledge and provides an in-depth overview of basic and advanced Windows PowerShell concepts.

Authors

Gary Lapointe is a SharePoint MVP who has been planning and implementing SharePoint solutions since early 2007. A developer by trade, Gary spends most of his time developing tools that enable IT professionals to be more productive so that they can focus on solving business problems and satisfying their customers' needs. Gary's blog at Falchion Consulting (https://go.microsoft.com/fwlink/p/?LinkId=225944) has become the definitive source for all things related to the automated deployment and management of Microsoft Office SharePoint Server 2007 and Microsoft SharePoint Server 2010.

Shannon Bray is a SharePoint evangelist and Microsoft Certified Trainer. He is currently employed with Planet Technologies as a Technical Architect and works exclusively with Microsoft Office SharePoint Server 2007 and Microsoft SharePoint Server 2010. Shannon specializes in architecture design and solution development using Microsoft technologies. He is the President of the Colorado SharePoint User's Group and has presented SharePoint topics at conferences that Microsoft sponsors, such as TechReady and TechEd.

Book excerpt -- Chapter 20: Multi-Tenancy

In this excerpt:

  • Overview

  • Create a Tenant Hosting Web Application

  • Configure Service Applications

  • Manage Feature Packs and Site Subscriptions

  • Provision Tenants

Overview

With SharePoint 2007, it was somewhat difficult to isolate one customer, or tenant, from another when hosting those customers within the same Farm. The implementation of the Shared Services Provider (SSP), for example, effectively ruled out any substantial hosting considerations due to the likely need to create a separate SSP for each customer. With SharePoint 2010, in part due to the requirements of large enterprises and hosting providers such as Microsoft Online, we now have true support for hosting multiple tenants in a single Farm, otherwise known as multi-tenancy.

Specifically, many of the Service Applications have been architected to support the provisioning of tenant data specifically without the need to create a separate Service Application for each tenant. We can also associate Site Collections with specific tenants, thereby preventing one tenant from seeing another tenant’s data. We can even restrict what Features are available to each tenant.

It’s important to note that multi-tenancy is not a switch or a single feature that can simply be enabled. Multi-tenancy within SharePoint 2010 is a collection of capabilities that, taken together, make the hosting of multiple tenants in a single Farm possible. To this end, we would be remiss if we didn’t also mention Sandboxed Solutions (made possible by the User Code Service). The tenants’ ability to deploy custom code to various Site Collections solves numerous customization challenges that administrators faced when attempting to host multiple tenants within a SharePoint 2007 Farm. However, there are no specific multi-tenancy settings that we must configure for Sandboxed Solutions to work properly in a multi-tenancy environment, so we will not be covering the User Code Service here.

What we will cover in this chapter are the key things that most medium and large multi-tenancy deployments are likely to need. Namely, we will create a Web Application that will host the Host-Named Site Collections belonging to our tenants.We will then review the key changes to how we provision each of the Service Applications that support the partitioning of tenant data. And finally, we will cover the provisioning of tenant sites, which includes creating and configuring the Site Subscription that represents a tenant, the Features that the tenants will have available to them, and any additional settings necessary for the Service Applications to be fully operational for each tenant.

The definitive source for details on SharePoint 2010 multi-tenancy is Spencer Harbar’s Rational Guide to Multi-Tenancy with SharePoint 2010 (https://go.microsoft.com/fwlink/p/?LinkId=225943). Spence provides scripts along with timely, relevant, and detailed information about all of the known design constraints and issues surrounding this area.

For more information about multi-tenancy, see the SharePoint 2010 for Hosters Whitepaper (https://go.microsoft.com/fwlink/p/?LinkId=190783). Additional information can be found by reviewing the Model: Hosting architectures for SharePoint Server 2010 poster.

Create a Tenant Hosting Web Application

When it comes time to determine where we will be storing our tenants' Site Collections and administrative sites, we have a couple of core options. The first option is to use a standard Web Application and separate each tenant using a series of Managed Paths. The second option is to use Host-Named Site Collections. For most scenarios, we recommend that you use Host-Named Site Collections. This is due to the management overhead and the software boundaries related to how many Managed Paths are supported by SharePoint 2010. (Additionally, this is how the SharePoint product team intended multi-tenant deployments to be configured.)

To help explain this further, suppose we need to create a Managed Path for a Content Type Hub, the Tenant Administration Site, My Sites, and individual tenant Site Collections (or Member Sites). If we use regular addressing (not Host-Named Site Collections), then we will have the following URL structure for each tenant:

Using this approach, we will have, at a minimum, six Managed Paths per tenant. This can result in scalability issues, as the recommended maximum is 20 Managed Paths per Web Application. You can see how we will easily surpass this number in very little time.

However, if, instead, we use Host-Named Site Collections, then we can define one set of Managed Paths that will be used for each tenant. To support this, we will create a tenant hosting Web Application that will have a root Site Collection and each of our tenant's Site Collections will be created using host headers. (The root Site Collection is required to be present but will not be utilized.) This will result in the following URL structure:

Using this approach, we define our Managed Paths only once, resulting in a total of six Managed Paths, regardless of how many tenants we need to support. (This also provides a cleaner URL structure that is more appealing to most customers of any hosted offering.)

Before we configure any of our Service Applications or create our Tenant Administration Site or Member Sites and dependent objects, we must first create our Web Application and associated Managed Paths. Throughout the examples in this chapter, we will assume that Host-Named Site Collections are being utilized.

Note

We can, of course, split tenants across Web Applications and even isolate tenants to a Content Database. However, to keep things simple, we will assume one Web Application and one Content Database. Isolating tenants to a Content Database is possible, but it will require that we disable Self-Service Site Creation and implement a custom solution for creating Site Collections in the appropriate Content Database.

Create the Web Application

If you review Chapter 7, “Managing Web Applications,” you can see the code required to create a new Web Application. For our purposes here, we want to check to see if the Application Pool we need already exists, and if it does not, we need to get the credentials that we will use for the Application Pool that will be created when we call the New-SPWebApplication cmdlet:

$poolName = "SharePoint Hosting Pool"
$poolAcctName = "sharepoint\SPHostingPool"
$pools = [Microsoft.SharePoint.Administration.i
SPWebService]::ContentService.ApplicationPools
if ($pools.Count -gt 0) {
$tempAppPool = $pools | ? {$_.Name -eq $poolName}
}
$poolAcct = $null
if ($tempAppPool -eq $null) {
$poolAcct = Get-SPManagedAccount $poolAcctName `
-ErrorAction SilentlyContinue
if ($poolAcct -eq $null) {
$cred = Get-Credential $poolAcctName
$poolAcct = New-SPManagedAccount -Credential $cred
}
}

Once we have established our variables for the Application Pool and associated Application Pool identity, we can then create the actual Web Application. Because we intend to use Host-Named Site Collections, we will not provide a host header for this Web Application:

$webApp = New-SPWebApplication `
-ApplicationPool $poolName `
-ApplicationPoolAccount $poolAcct `
-Name "SharePoint Hosting Web Application" `
-DatabaseName "SharePoint_Content_Tenants1" `
-Port 80

The preceding command will create a new Web Application on port 80, which will be addressable using the server name: http://<server>:80/. At this point, you could set the values for the number of Site Collections allowed within the Content Database created as well as create additional Content Databases.

The next step we need to perform is to create an empty Site Collection at the root of the Web Application. This Site Collection will never be utilized, but it is required in order to enable Self-Service Site Creation. (If you do not intend to allow tenant administrators to create Site Collections, then you can forego this step.) The following commands create a Site Collection, and then they enable Self-Service Site Creation for the Web Application:

New-SPSite -Url $webApp.Url `
-OwnerAlias "$($env:userdomain)\$($env:username)"
$webApp.SelfServiceSiteCreationEnabled = $true
$webApp.RequireContactForSelfServiceSiteCreation = $true

Notice that we are not setting a template for this Site Collection; we could set a template, but it is entirely unnecessary, so we provide only the minimum required parameters.

Create the Managed Paths

With our Web Application created, we can now configure our Managed Paths. The first thing we want to do is to remove the default "Sites" Managed Path that is created when the Web Application is created. We can do this using the Remove-SPManagedPath cmdlet:

$webApp | Remove-SPManagedPath "sites" -Confirm:$false

Note that this removes the path, making it no longer available when creating Site Collections using the Central Administration site. However, there is still a "sites" Managed Path available by default for Host-Named Site Collections, so we will not need to create this Managed Path explicitly.

To create the Managed Paths that we wish to share between all Host-Named Site Collections, we use the New-SPManagedPath cmdlet and provide the HostHeader parameter. The following code creates the structure we defined earlier:

$webApp | New-SPManagedPath "admin" `
-HostHeader -Explicit
$webApp | New-SPManagedPath "cthub" `
-HostHeader -Explicit
$webApp | New-SPManagedPath "mysites" `
-HostHeader -Explicit
$webApp | New-SPManagedPath "mysites/personal" `
-HostHeader

Configure Service Applications

When it comes to multi-tenancy, not all Service Applications are created alike. There are some Service Applications that can be partitioned to store tenant data in an isolated fashion without the need to create multiple databases or separate Service Applications per tenant.

The following Service Applications can be partitioned and store tenant data:

  • Business Connectivity Services

  • Managed Metadata Services

  • Search Services

  • Secure Store Services

  • User Profile Services

  • Word Automation Services

For simplicity's sake, we will refer to these Service Applications collectively as Tenant Services. The remaining Service Applications either do not store tenant data at all and/or cannot be partitioned, with the exception of one—the Subscription Settings Service. This service manages information about our tenants, such as the Site Subscription that is used to identify a tenant and all Member Sites associated with the tenant. Before we look at the changes that we need to make to how we provision the Tenant Services, we will first detail how to provision the Subscription Settings Service.

Provision the Subscription Settings Service

The Subscription Settings Service Application manages our tenant data and must be provisioned before we can create any of our tenant artifacts. Provisioning the Subscription Settings Service is very similar to what you saw in Part 4 of this book, and most closely follows the steps we followed when provisioning the Business Connectivity Services Service Application, as described in Chapter 13. Here are the steps that you will need to perform to provision the Subscription Settings Service:

  1. Start the Subscription Settings Service Instance.

  2. Create the Subscription Settings Service Application.

  3. Create the Subscription Settings Service Application Proxy.

Unlike some of the other Service Applications we looked at in Part 4, there is no mechanism to have the Service Application Proxy created automatically when the Service Application is created, so we must do so explicitly.

Start the Service Instance

Before we create the Subscription Settings Service Application, we will start the Service Instance associated with the Service Application. To retrieve the Subscription Settings Service Instance, we’ll use Microsoft SharePoint Foundation Subscription Settings Service for the TypeName property (this value will be different with different language versions of SharePoint):

$svc = Get-SPServiceInstance -Server $env:computername | i
where {$_.TypeName -eq i
"Microsoft SharePoint Foundation Subscription i
Settings Service"}
if ($svc.Status -ne "Online") {
$svc | Start-SPServiceInstance
}

Create the Service Application

You can create the Subscription Settings Service Application using the New-SPSubscriptionSettingsServiceApplication cmdlet. Table 20.1 describes the relevant parameters of the New-SPSubscriptionSettingsServiceApplication cmdlet.

Table 20.1

Parameter Description

Name

Specifies the display name of the Subscription Settings Service Application to create. The name can contain a maximum of 128 characters and can contain the comma (,), equal sign (=), and colon (:) characters provided that they are enclosed in quotation marks.

ApplicationPool

Specifies the existing Service Application Pool in which to run the web service for the new Subscription Settings Service Application.

DatabaseName

Specifies the name for the Subscription Settings Service database.

DatabaseServer

Specifies the name of the host server for the database specified by the DatabaseName parameter.

FailoverDatabaseServer

Specifies the name of the host server for the failover database server.

There are no parameters for the New-SPSubscriptionSettingsServiceApplication cmdlet that affect the behavior of the Service Application. The only required parameter is -ApplicationPool, though it is recommended that you provide a meaningful name and not let SharePoint choose a name for you. The following code demonstrates how to create the Service Application:

$appName = "Subscription Settings Service Application"
$app = Get-SPServiceApplication | where {
$_.Name -eq $appName
}
if ($app -eq $null) {
$poolName = "SharePoint Services App Pool"
$pool = Get-ServicePool $poolName
$app = New-SPSubscriptionSettingsServiceApplication `
-ApplicationPool $pool `
-Name $appName `
-DatabaseName "SharePoint_Subscription_Settings" `
-DatabaseServer "spsql1"
}

Note that before attempting to create the Service Application, we first check to see if it already exists by using the Get-SPServiceApplication cmdlet. We then use the Get-ServicePool helper function we created in Chapter 11 to retrieve the Service Application Pool that we will associate with the Service Application.

Note

Unlike most other Service Applications, you cannot provision more than one instance of the Subscription Settings Service Application in a single Farm. The creation of an additional Service Application itself will not cause an error; however, when you attempt to create the Service Application Proxy for the Service Application, you will receive an error. This effectively prevents you from being able to associate the Service Application with a Web Application.

Create the Service Application Proxy

After creating the Subscription Settings Service Application, we must next create the Service Application Proxy. To create the Service Application Proxy, we use the New-SPSubscriptionSettingsServiceApplicationProxy cmdlet, passing in the Service Application previously created. Unlike virtually every other Service Application Proxy creation cmdlet that we looked at in Part 4, this cmdlet does not allow us to specify a name and, as such, will set the name to Microsoft SharePoint Foundation Subscription Settings Service Application Proxy:

$proxy = New-SPSubscriptionSettingsServiceApplicationProxy `
-ServiceApplication $app

Finally, we need to associate the Service Application Proxy with a specific Proxy Group. This can be done using the Add-SPServiceApplicationProxyGroupMember cmdlet:

$pg = Get-SPServiceApplicationProxyGroup -Identity ""
$pg | Add-SPServiceApplicationProxyGroupMember `
-Member $proxy

Configure Tenant Services

In Part 4, we detailed how to provision each of the many Service Applications that SharePoint 2010 supports. In this section, to keep things as simple as possible, we will highlight only the Service Application and Service Application Proxy creation cmdlets that are relevant to the Tenant Services listed earlier. (Refer back to Part 4 for detailed instructions on provisioning the Service Applications.)

For most of our Service Applications, we merely need to provide a -PartitionMode parameter to the appropriate cmdlets. However, there are a couple of cases that vary slightly. The following sections highlight the changes you will need to make to how you are calling the respective cmdlets.

Warning

For those Service Applications that allow for the explicit creation of a Service Application Proxy, if the Service Application itself was created in partition mode, then the Service Application Proxy must also be created in partition mode. You cannot mix and match this setting, and you cannot change this setting after creation.

Business Connectivity Services

Partitioning Business Connectivity Services (BCS) allows us to store and manage different Models, External Systems, and External Content Types per tenant, within the same Service Application. (No tenant will see another tenant's data.) To partition BCS, we merely have to set the -PartitionMode parameter when creating the Service Application using the New-SPBusinessDataCatalogServiceApplication cmdlet (refer to Chapter 13 for the remainder of the necessary commands):

$app = New-SPBusinessDataCatalogServiceApplication `
-ApplicationPool $pool `
-Name "BCS Hosting Service Application" `
-DatabaseName "SharePoint_Hosting_BCS" `
-DatabaseServer "spsql1" `
-PartitionMode

In this particular case, the Service Application Proxy will be created automatically for us and will be set to be partitioned. Note that there is a cmdlet to create a Service Application Proxy manually; however, the release version of SharePoint 2010 does not allow you to set the -PartitionMode property unless you are connecting to a published Service Application in another Farm. This means that, if you delete the Service Application Proxy that is created automatically, you must consequently delete the Service Application as well; recreating the Service Application is the only way to create a Service Application Proxy in partition mode.

Note

After provisioning the Business Connectivity Services Service Application in partition mode, you will no longer be able to manage the service from the Central Administration site. Instead, you must navigate to the BCS Management page from the Tenant Administration site.

Managed Metadata Services

Partitioning Managed Metadata Services allows us to create a separate Content Type Hub for each of our tenants. Additionally, we are able to create and manage Term Sets per tenant without worrying about exposing those Terms to other tenants, all within a single Service Application.

Unlike Business Connectivity Services, Managed Metadata Services provides uswith the means to create both the Service Application and Service Application Proxy in partition mode, using the -PartitionMode switch parameter. (For partitioning to work correctly, both the Service Application and Service Application Proxy must be configured for partition mode.)

Additionally, the Managed Metadata Services Service Application and Service Application Proxy creation cmdlets contain several parameters that alter the way the service is configured. Some of these parameters are not relevant in a multi-tenancy scenario, and as such, the corresponding settings will be disabled in the Central Administration site if configured for partition mode. Specifically, when creating the Service Application, it is not necessary to set the -HubUri or -SyndicationErrorReportEnabled parameters because these parameters are not relevant when in partition mode and will be set per tenant (refer to Chapter 15 for the remainder of the necessary commands):

$app = New-SPMetadataServiceApplication `
-ApplicationPool $pool `
-Name "Managed Metadata Hosting Service Application" `
-DatabaseName "SharePoint_Hosting_Metadata" `
-PartitionMode

For the Service Application Proxy, we can set any of the parameters described in Chapter 15. However, Content Type syndication will always be enabled when in partition mode. If you fail to provide the -ContentTypeSyndicationEnabled parameter, you will receive a warning stating that it will be enabled (we recommend you provide the parameter to avoid generating the warning):

$proxy = $app | New-SPMetadataServiceApplicationProxy `
-Name "$($app.Name) Proxy" `
-ContentTypeSyndicationEnabled `
-DefaultProxyGroup `
-PartitionMode

When it comes time to configure your tenants, you will use the Set-SPSiteSubscriptionMetadataConfig cmdlet to configure the tenant-specific properties. Table 20.2 lists the parameters of the Set-SPSiteSubscriptionMetadataConfig cmdlet. (You will see an example of how to call this cmdlet later in this chapter.)

Table 20.2

Parameter Description

Identity

Specifies the Site Subscription for which to set the Service Application settings.

ServiceProxy

Specifies the local Metadata Service Application Proxy for the Service Application that contains the Site Subscription–specific settings. This is the Service Application Proxy that we just created.

DoNotUnpublishAllPackages

If this flag is set, the published Content Type packages will not be unpublished. If the -HubUri parameter is changed, all Content Type packages will be unpublished by default. If the -HubUri parameter is not changed, this flag has no effect.

HubUri

Specifies the URI of the syndication hub. The type must be a valid URI, in the form http://sitename.

SyndicationErrorReportEnabled

Enables error reporting for Content Type syndication.

Note

After provisioning the Managed Metadata Services Service Application in partition mode, you will no longer be able to manage the Term Store from the Central Administration site. Instead, you must navigate to the Term Store from the Tenant Administration site.

Search Services

Partitioning Search Services does not provide any additional tenant-based functionality or management screens, and you can continue to manage it directly from within the Central Administration site; however, by provisioning Search Services, you are able to expose search results seamlessly only to the tenant to whom the results pertain. (It's not even necessary to add the Host-Named Site Collections to the content source—as long as the hosting Web Application is set as a content source, all tenant sites will be indexed and their data property isolated when querying.)

Like the Managed Metadata Services, the Search Services Service Application and Service Application Proxy creation cmdlets have a parameter that allows us to configure the Service Application for partition mode. However, unlike all of the other Service Applications we will look at in this chapter, the switch parameter that we will use is called -Partitioned, as opposed to -PartitionMode.

The Search Services Service Application is one of the more complex Service Applications to configure using PowerShell, requiring significantly more PowerShell than any other Service Application. As with the other Tenant Services we cover in this chapter, we will show only the cmdlets that must have the -Partitioned parameter set (refer to Chapter 14 for the remainder of the necessary commands):

$app = New-SPEnterpriseSearchServiceApplication `
-Name "Enterprise Search Hosting Service Application `
-DatabaseServer "spsql1" `
-DatabaseName "SharePoint_Hosting_Search" `
-ApplicationPool $pool `
-Partitioned

We can now create our Service Application Proxy using the New-SPEnterpriseSearchServiceApplicationProxy cmdlet:

$proxy = New-SPEnterpriseSearchServiceApplicationProxy `
-Name "$($app.Name) Proxy" `
-SearchApplication $app `
-Partitioned

As noted previously, there is a considerable amount of additional PowerShell that must be run to provision Search Services successfully—refer to Chapter 14 for more information.

Secure Store Service

By partitioning the Secure Store Services, we are able to create and manage Secure Store Target Applications at the tenant level. This means that a tenant can create a target application for use with BCS and not worry about their unique data connection settings being exposed to other tenants.

The Secure Store Services Service Application creation cmdlet, New-SPSecureStoreServiceApplication, contains our standard-PartitionMode parameter that we must set to enable partition mode. However, the Service Application Proxy creation cmdlet, New-SPSecureStoreServiceApplicationProxy, does not contain this parameter. When the Service Application Proxy is created, it looks at what was set for the Service Application and uses that for its configuration (refer to Chapter 12 for the remainder of the necessary commands):

$app = New-SPSecureStoreServiceApplication `
-Name "Secure Store Hosting Service Application" `
-ApplicationPool $pool `
-DatabaseServer "spsql1" `
-DatabaseName "SharePoint_Hosting_SecureStore" `
-AuditingEnabled `
-AuditLogMaxSize 30 `
-Sharing:$false `
-PartitionMode

Note that even though the -AuditingEnabled parameter is a switch parameter and should not be required, it is necessary to provide this parameter due to how the cmdlet was implemented. If you wish not to enable auditing, specify the parameter as -AuditingEnabled:$false. However, we recommend that you enable auditing.

Note

After provisioning the Secure Store Services Service Application in partition mode, you will no longer be able to manage target applications from the Central Administration site. Instead, you must navigate to the Secure Store Management page from the Tenant Administration Site. However, before tenants can create target applications, you must generate a master key—this can be done using PowerShell, as described in Chapter 12 or via the Secure Store Management screen in the Central Administration site.

User Profile Services

Partitioning User Profile Services means that you will be able to store user profile data and My Sites, as well as the various types of social data, at the tenant level—all within the same Service Application. When it comes to selecting users, you can even control things such as the People Picker to prevent tenants from seeing users corresponding with other tenants. (Note that the People Picker settings are actually a property of a Site Subscription and not the User Profile Services, but you configure this setting along with other tenant-specific User Profile settings.)

We recommend that you create a separate Organizational Unit (OU) for each tenant and group, with each of these OUs in another OU. For our purposes, we will create an OU called Tenants and place each tenant in their own OU within the Tenants OU. For example, for a tenant named Customer1 in the domain domain.com, we will create a containment structure that will have the following distinguished name (DN):OU=Customer1,OU=Tenants,DC=domain,DC=com

This information will be necessary when we provision our individual tenants. For now, all we need to worry about is provisioning the User Profile Services Service Application and Service Application Proxy in partition mode. As with the other creation cmdlets, we need simply to provide the -PartitionMode parameterwhen creating the Service Application and Service Application Proxy. Note that, as with the Managed Metadata Service Application creation cmdlet, there are some parameters that are not applicable when creating the Service Application in partition mode. Specifically, the -MySiteHostLocation, -MySiteManagedPath, and -SiteNamingConflictResolution parameters are not applicable as they are set per tenant using the Set-SPSiteSubscriptionProfileConfig cmdlet.

As shown in Chapter 15, we first declare a hash table containing all of our properties, which we then pass to the Start-Job cmdlet along with the call to the New-SPProfileServiceApplication cmdlet (we have included the relevant portions here; refer to Chapter 15 for the remainder of the necessary commands):

$params = @{
Name="User Profile Hosting Service Application";
ApplicationPool=$pool.Id;
ProfileDBName="SharePoint_Hosting_UserProf_Profile";
ProfileDBServer="spsql1";
ProfileSyncDBName="SharePoint_Hosting_UserProf_Sync";
ProfileSyncDBServer="spsql1";
SocialDBName="SharePoint_Hosting_UserProf_Social"; SocialDBServer="spsql1";
PartitionMode=$true
}
...
$sb = {
[System.Collections.Hashtable]$params = $input | %{$_}
New-SPProfileServiceApplication @params
}
...
$proxy = New-SPProfileServiceApplicationProxy `
-Name "$($app.Name) Proxy" `
-ServiceApplication $app `
-DefaultProxyGroup `
-PartitionMode

When it comes time to configure your tenants, you will use the Add-SPSiteSubscriptionProfileConfig cmdlet to configure the tenant-specific properties. Table 20.3 lists the parameters of the Add-SPSiteSubscriptionProfileConfig cmdlet. (You will see an example of how to call this cmdlet later in this chapter.)

Table 20.3

Parameter Description

Identity

Specifies the Site Subscription for which to set the Service Application settings.

ProfileServiceApplicationProxy

Specifies the name of the proxy for the User Profile Service Application.

MySiteHostLocation

Specifies the Site Collection where the My Site Web Host Site will be created.

MySiteManagedPath

Specifies the managed path where personal sites will be created.

SiteNamingConflictResolution

Specifies the format to use to name personal sites. A value of Block will just add the username, Resolve will conditionally add the domain to avoid conflicts, and None will always add the domain to avoid conflicts.

SynchronizationOU

Specifies the Organizational Unit that serves the Site Subscription. This is where we use the Organizational Unit that we previously defined.

Word Automation Service

The final Service Application that we can configure for partitioning is the Word Automation Services Service Application. As described in Chapter 13, this particular Service Application has no cmdlet to enable us to create the Service Application Proxy explicitly; it is created automatically when the Service Application is created. As with the previous Service Applications, the New-SPWordConversionServiceApplication cmdlet has a -PartitionMode parameter that we must set to enable partitioning (refer to Chapter 13 for the remainder of the necessary commands):

$app = New-SPWordConversionServiceApplication `
-ApplicationPool $pool `
-Name "Word Automation Hosting Service Application" `
-Default `
-DatabaseName "SharePoint_Hosting_WordAutomation1" `
-DatabaseServer "spsql1" `
-PartitionMode

This completes the changes that we must make to how we provision our Service Applications. The next step to complete, before we can begin to provision our tenants' sites, is to configure the Feature Packs that will define what Features will be accessible to a tenant and the Site Subscriptions that identify individual tenants.

Manage Feature Packs and Site Subscriptions

In many hosted scenarios, there is a need to provide different licensing options to tenants. For example, Tenant A may require the full SharePoint 2010 Enterprise license and all the Features that go with it, whereas Tenant B may need only the core SharePoint 2010 Foundation Features. This ability to provide only the Features that are relevant to the tenant is a great addition to SharePoint 2010 because it not only accommodates traditionally hosted scenarios, it also solves a longstanding issue with large enterprises that wish to provide a blended licensing experience. In other words, it provides only the Feature for which a business unit is licensed, thus negating the need to purchase licenses that are not required. This capability is also not limited to out-of-the-box Features; you can selectively expose custom and third-party Features to applicable tenants only.

SharePoint 2010 implements this functionality using what is known as Feature Packs. A Feature Pack is nothing more than a collection of Features that can be associated with a Site Subscription. So, what is a Site Subscription? In its simplest form, a Site Subscription is just a unique identifier (a GUID) that represents a single tenant. You will need to create a Site Subscription to identify every tenant you wish to create. Once it’s created, you can associate Feature Packs, the Tenant Administration Site, and any new Site Collections with the Site Subscription. So, before we look at how to create Tenant Administration Sites and Tenant Site Collections, we will configure our Feature Packs and Site Subscriptions.

Manage Feature Packs

To create a new Feature Pack, you use the New-SPSiteSubscriptionFeaturePack cmdlet. This cmdlet takes in no parameters and returns a SPSiteSubscriptionFeaturePack object that contains an Id and a FeatureDefinitions property. What appears to be missing with this is a human-readable way to identify the Feature Pack. For example, if you use the Get-SPSiteSubscriptionFeaturePack cmdlet to retrieve the list of Feature Packs, you will see something similar to the following (in this case, we’ve created the Feature Packs but have not yet associated any Features):

PS C:\> Get-SPSiteSubscriptionFeaturePack
FeatureDefinitions Id

{}

05422264-6923-497f-9286-7...

{}

35d11b2c-6814-42c5-9b44-7...

{}

831e6882-7171-4ab9-87bf-f...

When it comes time to retrieve a Feature Pack for association with a Site Subscription, it can be very difficult to tell which GUID value corresponds to what set of Features. To address this issue, we recommend that you store the Feature Pack ID in a Farm-level Property Bag. This can be done simply by retrieving an instance of the SPFarm object, using Get-SPFarm, and then adding a key/value pair to theProperties collection:

$farm = Get-SPFarm
$farm.Properties.Add("FeaturePack_Foundation", $ffp.Id)
$farm.Properties.Add("FeaturePack_Standard", $sfp.Id)
$farm.Properties.Add("FeaturePack_Enterprise", $efp.Id)
$farm.Update()

In the following function, New-FeaturePacks, we are creating three Feature Packs: one for SharePoint Foundation Features, one for SharePoint Standard Features, and one for SharePoint Enterprise Features. We then store the Feature Pack ID in the Farm’s Property Bag with a human-understandable key name. Next, we use the Add-SPSiteSubscriptionFeaturePackMember cmdlet to add Feature Definitions to the Feature Pack. This cmdlet takes in two parameters, the Feature Pack, which we pass in via the pipeline, and the folder name or ID of the Feature Definition to add. (For brevity, we have aliased the cmdlet as AddFeature using the Set-Alias cmdlet and omitted the repetitious calls to the cmdlet.)

function New-FeaturePacks() {
$ffp = New-SPSiteSubscriptionFeaturePack
$sfp = New-SPSiteSubscriptionFeaturePack
$efp = New-SPSiteSubscriptionFeaturePack
$farm = Get-SPFarm
$farm.Properties.Add("FeaturePack_Foundation", $ffp.Id)
$farm.Properties.Add("FeaturePack_Standard", $sfp.Id)
$farm.Properties.Add("FeaturePack_Enterprise", $efp.Id)
$farm.Update()
Set-Alias AddFeature Add-SPSiteSubscriptionFeaturePackMember
#Add Foundation Features
$ffp | AddFeature -FeatureDefinition AdminLinks
...
$ffp | AddFeature -FeatureDefinition XmlFormLibrary
#Add Standard Features
$ffp.FeatureDefinitions | % {
AddFeature -Identity $sfp -FeatureDefinition $_}
$sfp | AddFeature `
-FeatureDefinition AccSrvRestrictedList
...
$sfp | AddFeature -FeatureDefinition Workflows
#Add Enterprise Features
$sfp.FeatureDefinitions | % {
AddFeature -Identity $efp -FeatureDefinition $_}
$efp | AddFeature -FeatureDefinition AccSrvMSysAso...
$efp | AddFeature -FeatureDefinition WACustomReports
Remove-Item alias:\AddFeature
$packs = @{
"Foundation"=$ffp;
"Standard"=$sfp;
"Enterprise"=$efp}
return $packs
}

The New-FeaturePacks function then returns a hash table containing each Feature Pack that was created. When it comes time to create our Site Subscription, we will most likely need to retrieve the Feature Packs again. To make this easier, we create a Get-FeaturePacks function that returns the same hash table as the New-FeaturePacks function:

function Get-FeaturePacks() {
$farm = Get-SPFarm
$ffp = $farm.Properties["FeaturePack_Foundation"] | `
Get-SPSiteSubscriptionFeaturePack
$sfp = $farm.Properties["FeaturePack_Standard"] | `
Get-SPSiteSubscriptionFeaturePack
$efp = $farm.Properties["FeaturePack_Enterprise"] | `
Get-SPSiteSubscriptionFeaturePack
$packs = @{
"Foundation"=$ffp;
"Standard"=$sfp;
"Enterprise"=$efp}
return $packs
}

Note

The New-FeaturePacks and Get-FeaturePacks functions can be found in the New-FeaturePacks.ps1 file, which is included in the Chapter20Scripts.zip file. It can be obtained from this book’s download site.

If you need to remove a Feature Definition from a Feature Pack, you can do so using the Remove-SPSiteSubscriptionFeaturePackMember cmdlet. This cmdlet takes in the Feature Pack to edit and either the folder name (or ID) of the Feature Definition to remove from the Feature Pack, or the -AllFeatureDefinitions switch parameter that will cause all Feature Definitions to be removed from the Feature Pack:

$packs = Get-FeaturePacks
$packs.Standard | `
Remove-SPSiteSubscriptionFeaturePackMember `
-FeatureDefinition AccSrvRestrictedList

Likewise, if you need to remove an entire Feature Pack, you can do so using the Remove-SPSiteSubscriptionFeaturePack cmdlet. This cmdlet takes in just the Feature Pack (or the ID of the Feature Pack) to remove:

$packs = Get-FeaturePacks
$packs.Standard | Remove-SPSiteSubscriptionFeaturePack

Manage Site Subscriptions

As stated previously, a Site Subscription, like a Feature Pack, is nothing more than a unique identifier that represents an individual tenant. You can create a new SiteSubscription using the New-SPSiteSubscription cmdlet:

PS C:\> $siteSub = New-SPSiteSubscription
PS C:\> $siteSub
Id Sites

d8df90b1-e681-4f1e-84dd-c...

{}

This cmdlet creates a SPSiteSubscription object that contains a property for the ID of the Site Subscription and a collection property, Sites, containing all the Site Collections associated with the Site Subscription. You can also use this object to add existing Site Collections to the Site Subscription. (Only Site Collections that have not already been added to a Site Subscription can be added.)

You can use the Get-SPSiteSubscription cmdlet to retrieve a Site Subscription using either the ID (GUID) of the Site Subscription or the URL to any Site Collection associated with the Site Subscription. (Or, if no parameters are provided, all Site Subscriptions will be returned.) The following example retrieves the Site Subscription associated with the tenant whose root Site Collection is located at http://tenant1.domain.com:

$siteSub = Get-SPSiteSubscription `
-Identity "http://tenant1.domain.com"

Note

The Get-SPSiteSubscription cmdlet will return only Site Subscriptions that have been associated with at least one Site Collection. Creating a Site Subscription using the New-SPSiteSubscription cmdlet creates the SPSiteSubscription object in memory only, and it does not commit anything to the Configuration Database until a Site Collection that uses the Site Subscription has been created.

If you need to remove a Site Subscription, you can do so using the Remove-SPSiteSubscription cmdlet. However, removing a Site Subscription will delete all associated Site Collections and any settings associated with the Site Subscription. Therefore, this cmdlet should be used with extreme caution. (As Site Collections cannot be restored individually and associated with a new Site Subscription, you will have to perform a full Farm restore to recover the Site Subscription and associated data.)

Tip

We recommend that you create a custom function to handle the creation of Site Subscriptions and the provisioning of the core tenant artifacts, such as the Tenant Administration Site and other miscellaneous settings, as described in the following sections. An example of such a function can be found in the New-Tenant.ps1 script located in the Chapter20Scripts.zip file.

Now that we've created our Site Subscription, we can associate it with a previously created Feature Pack. This can be done using the Set-SPSiteSubscriptionConfig cmdlet. This cmdlet includes a parameter for the Site Subscription that we wish to edit as well as two additional parameters: the Feature Pack to associate with the Site Subscription (-FeaturePack) and the path to the Active Directory OU that the People Picker will use to filter the users that are returned (-UserAccountDirectoryPath):

$pack = (Get-FeaturePack).Enterprise
$siteSub = New-SPSiteSubscription
$ou = "OU=Customer1,OU=Tenants,DC=domain,DC=com"
$siteSub | Set-SPSiteSubscriptionConfig `
-FeaturePack $pack `
-UserAccountDirectoryPath $ou

In the preceding example, we use our previously defined Get-FeaturePack function to retrieve the Enterprise Feature Pack and associate it with a new Site Subscription. In addition, we are setting the -UserAccountDirectoryPath parameter to filter the People Picker to allow only users from the OU that we configured earlier in this chapter (see the section “User Profile Services”).

Provision Tenants

Before we can fully provision our tenant, we need to declare some variables that we'll use throughout the following sections. The first thing we'll need is the name of the tenant, or customer, along with the username and email address of the user who will be set as the tenant administrator. For the tenant name, we want to use the value that is used for the OU and the hostname of the tenant's root Site Collection (if you need these to be different values, then declare separate variables; for our purposes, we will use a single value for both):

$tenantName = "Customer1"
$tenantAdminLogin = "domain\customer1admin"
$tenantAdminEmail = "customer1@domain.com"

Next, we need to get an instance of the Web Application that will host our tenant sites. This should correspond to the Web Application that we created earlier in this chapter.

$hostWebApp = Get-SPWebApplication "https://server"

We'll use these variables throughout the following sections. Additionally, we assume that $siteSub contains the Site Subscription as created in the previous section.

The core steps that we need to complete in order to fully provision our tenant include creating Member Sites, creating the Tenant Administration Site, configuring the User Profile and Managed Metadata Service Applications, and finally, setting any optional administrative properties that we wish to associate with the tenant.

Create Member Sites

Member Sites are just Site Collections that have been associated with a Site Subscription. The first and only required Member Site that we need to create is the Site Collection that will serve as the root Host-Named Site Collection. Because we are using Host-Named Site Collections, this Site Collection should be created before we create any other Site Collections (including the Tenant Administration Site).

To create this Site Collection, we simply use the New-SPSite cmdlet just as we would when creating any other Site Collection. The thing to note here is that we must provide the -SiteSubscription and -HostHeaderWebApplication parameters:

New-SPSite -Url $tenantUrl `
-SiteSubscription $siteSub `
-HostHeaderWebApplication $hostWebApp `
-OwnerAlias $tenantAdminLogin `
-OwnerEmail $tenantAdminEmail `
-Template "STS#0"

The template that you use for this Site Collection is irrelevant to multi-tenancy, and you can provide whatever template is appropriate for the particular tenant. If you have created the Site Collection without specifying the -SiteSubscription parameter, you can still associate the Site Collection with a Site Subscription by calling the Add method of the SPSiteSubscription object:

$siteSub.Add($site)

Additional Member Sites can be created by simply setting the URL to the appropriate managed path:

New-SPSite -Url "$tenantUrl/sites/Site1" `
-SiteSubscription $siteSub `
-HostHeaderWebApplication $hostWebApp `
-OwnerAlias $tenantAdminLogin `
-OwnerEmail $tenantAdminEmail `
-Template "STS#0"

Create the Tenant Administration Site

The Tenant Administration Site is the primary place where tenant administrators can manage their Site Collections and Service Application settings; in fact, the Tenant Administration Site is itself nothing but a standard Site Collection, which means that it is fully extensible. For example, you could extend the Tenant Administration Site in such a way as to allow tenant administrators to deploy a Sandboxed Solution to all Member Sites or otherwise provide reporting capabilities to inform the tenant administrator of what Sandboxed Solutions have been deployed to the various Member Sites.

To create the Tenant Administration Site, we again use the New-SPSite cmdlet, but this time, we must specify the -AdministrationSiteType parameter and we must set the site template to be TenantAdmin#0. In addition, as is the case with any Site Collection that we create for a tenant, we must provide the -SiteSubscription parameter. In the following example, we create the Tenant Administration Site under the admin managed path:

New-SPSite -Url "$tenantUrl/admin" `
-SiteSubscription $siteSub `
-HostHeaderWebApplication $hostWebApp `
-OwnerAlias $tenantAdminLogin `
-OwnerEmail $tenantAdminEmail `
-Template "TenantAdmin#0" `
-AdministrationSiteType TenantAdministration

It's important to note that it is not a requirement to deploy the Tenant Administration Site. However, without this site, your tenants will have no means of managing their various settings (including creating and deleting Site Collections). This may be exactly what you want, in which case, you will need to use PowerShell to adjust any tenant-specific settings or to manage Site Collections.

Configure User Profile and Managed Metadata Service Applications

You can skip this section and jump to "Store Tenant Information" if the Feature Pack associated with the Site Subscription contains only SharePoint Foundation Features. If you intend to configure your tenant to use the User Profile Services Service Application or the Managed Metadata Services Service Application, you will have a couple of additional steps to perform.

Configure the User Profile Services Service Application

We'll start by configuring the tenant to use the User Profile Services Service Application. There are two steps that we must perform to configure this Service Application fully: create the My Sites Host, and configure the Service Application to use the My Sites Host.

Just as we did when creating standard Member Sites, we will use the New-SPSite cmdlet to create the My Sites Host Site and simply provide the My Sites Host Site template for the -Template parameter:

New-SPSite -Url "$tenantUrl/mysites" `
-SiteSubscription $siteSub `
-HostHeaderWebApplication $hostWebApp `
-OwnerAlias $tenantAdminLogin `
-OwnerEmail $tenantAdminEmail `
-Template "SPSMSITEHOST#0"

Don't forget that we must always provide the Site Subscription that we will associate with the Site Collection.

With the My Sites Host Site now created, we can go ahead and configure the User Profile Services Service Application to use the host. We do this using the Add-SPSiteSubscriptionProfileConfig cmdlet. The cmdlet parameters were detailed in Table 20.3 earlier in this chapter. Before we call this cmdlet, we must first retrieve the Service Application Proxy object that we will pass into the cmdlet via the -ProfileServiceApplicationProxy parameter. We then set the remaining properties to match the Managed Path structure that we previously defined:

$userProfileProxy = Get-SPServiceApplicationProxy | `
? {$_.DisplayName -eq "UPA Hosting Proxy"}

Add-SPSiteSubscriptionProfileConfig -Id $siteSub `
-SynchronizationOU $tenantName `
-MySiteHostLocation "$tenantUrl/mysites" `
-MySiteManagedPath "/mysites/personal" `
-SiteNamingConflictResolution "None" `
-ProfileServiceApplicationProxy $userProfileProxy

Warning

When setting the -SynchronizationOU parameter, make sure you do not provide a full Distinguished Name (DN); doing so will not work. You must provide just the name of the Organizational Unit that is to be used. For example, if the DN is OU=Customer1,OU=Tenants,DC=domain,DC=com, then the parameter value should be Customer1.

Configure the Managed Metadata Services Service Application

The next Service Application we may wish to configure is the Managed Metadata Services Service Application. Specifically, we will create a Content Type Syndication Hub and associate that hub with the Service Application.

As we did with the previous Site Collections we’ve created, we will use the New-SPSite cmdlet and provide the appropriate Managed Path, Site Subscription, and owner details. In this case, choose a template that makes the most sense for your tenant—specify a template that provides the core Site Columns that you wish your tenant’s custom Content Types to utilize, or otherwise activate the appropriate Features after creation.

New-SPSite -Url "$tenantUrl/cthub" `
-SiteSubscription $siteSub `
-HostHeaderWebApplication $hostWebApp `
-OwnerAlias $tenantAdminLogin `
-OwnerEmail $tenantAdminEmail `
-Template "STS#0"

To associate this new Site Collection with the Managed Metadata Services Service Application and thereby formally register the Site Collection as a Content Type Hub, we simply pass in the URL of the Site Collection along with the Service Application Proxy to the Set-SPSiteSubscriptionMetadataConfig cmdlet. The cmdlet's parameters were detailed in Table 20.2 earlier in this chapter. Before we call this cmdlet, we must first retrieve the Service Application Proxy object that we will pass into the cmdlet via the -ServiceProxy parameter. We then set the remaining properties as needed, including the -HubUri property, which we set to the URL of our new Site Collection:

$managedMetadataProxy = Get-SPServiceApplicationProxy | `
? {$_.DisplayName -eq "MMS Hosting Proxy"}

Set-SPSiteSubscriptionMetadataConfig -Identity $siteSub `
-ServiceProxy $managedMetadataProxy `
-HubUri "$tenantUrl/cthub" `
-SyndicationErrorReportEnabled

Finally, we need to make sure that the ContentTypeHub Feature has been activated at our Content Type Hub Site Collection. This Feature should be activated automatically when the Site Collection is configured as a Content Type Hub via the Set-SPSiteSubscriptionMetadataConfig cmdlet; however, there are circumstances in which this activation may fail, requiring you to activate it explicitly:

Enable-SPFeature -Identity ContentTypeHub `
-Url "$tenantUrl/cthub"

Store Tenant Information

When working with multiple tenants, you may wish to store some information about the tenants. For example, you may wish to store the contract number associated with the tenant's subscription or perhaps store some contact information.

SharePoint provides the ability to store this information using the Site Subscription Settings Manager:

Microsoft.SharePoint.SPSiteSubscriptionSettingsManager

You can retrieve an instance of this class using the static Local property:

$mgr = [Microsoft.SharePoint.
SPSiteSubscriptionSettingsManager]::Local

Once we have a settings manager object, you can use one of two methods to retrieve a SPSiteSubscriptionPropertyCollection object that you can use to store key/value pairs. These methods are GetProperties and GetAdminProperties. The only difference between these two methods is that the GetAdminProperties method requires that the caller be a Farm Administrator, whereas the GetProperties method requires only that the user be granted Shell Admin rights to the Configuration Database using the Add-SPShellAdmin cmdlet.

In the following example, we retrieve the administrative properties collection and add a new property named ContractNumber:

$props = $mgr.GetAdminProperties($siteSub)
$props.Add("ContractNumber", "TN02032000A")
$props.Update()

Using this technique, we can store all critical tenant information with the tenant's Site Subscription, thus making it considerably easier to discover this information at a later date.