As part of some work I have been doing recently and some long conversations (thanks again Spence) I have been working with the Multi-Tenancy options. For the solution I was looking at designing I was wondering whether the multi-tenant approach would work. Historically in SharePoint 2007, to get the idea of common services for a site you would normally have to create separate SSP's for each site that needed. This meant there was slightly more overhead for the server and in general configuration. The separate SSP's approach did work however; it meant you could configure the SSP's to sync with active directory etc that would be independent of the other sites. In effect each website became isolated from each other with separate databases from each other. Now if we jump into the present with SharePoint 2010 we have the whole multi-tenant approach which in reality is not the same at all. The concept is the same but there are still some of the same problems and architecture issues that we all faced within SharePoint 2007.
For my solution I have multiple web sites that need to be separate from each other security wise as well as the services that they offer. Each site needed to have user profiles available and sync to potentially different user stores such as Active Directory. So after some investigation and discussion I decided that to achieve this that multi-tenant may be the approach. The logic behind this was to minimize the performance overhead on the servers with multiple service applications and backend databases, as well as trying to design a cleaner solution. So I decided to test this approach.
To begin with I would suggest that you look at the following documents and posts before you jump into the whole Multi-Tenant approach:
http://www.harbar.net/presentations/spevo/DD105%20Multi%20Tenancy.pdf
http://www.harbar.net/articles/sp2010mt1.aspx
http://www.harbar.net/articles/sp2010mt2.aspx
http://blogs.technet.com/b/speschka/archive/2009/11/30/enabling-multi-tenant-support-in-sharepoint-2010.aspx
http://blogs.technet.com/b/speschka/archive/2009/12/30/multi-tenancy-in-sharepoint-2010-part-2.aspx
http://blogs.technet.com/b/speschka/archive/2010/01/16/multi-tenancy-in-sharepoint-2010-part-3.aspx
So to begin, the system I am testing on has a base install of SharePoint 2010, the configuration database is created and Central Administration is all setup. In order to enable Multi-Tenant support you will need to perform the following steps:
- Provision Service Applications in Partition Mode
- Create the Tenant Sites (assuming the web applications have been created beforehand)
- Create Subscriptions
- Associate Subscriptions to the Tenant Sites
- Create Tenant Admin Sites
Let's begin, firstly you will need to use Powershell for this provisioning, and you will need to make sure that each Service Application that can be set to Multi-Tenant has the correct parameter added (Shown in Bold in the Powershell itself.
Firstly we will create the Partition (Multi-Tenant) Service Applications using the following Powershell:
NOTE: You will see I am using a bunch of variables within the Powershell commands, these are just for the base names etc I need in the script
Â
BCS Service and Proxy
New-SPBusinessDataCatalogServiceApplication -Name $bcsSAName -ApplicationPool $saAppPoolName -DatabaseServer $databaseServerName -DatabaseName $databasePrefixName"_BCS" -PartitionMode > $null
Get-SPServiceInstance | where-object {$_.TypeName -eq "Business Data Connectivity Service"} | Start-SPServiceInstance > $null
Â
Metadata Service and Proxy
New-SPMetadataServiceApplication -Name $metadataSAName -ApplicationPool $saAppPoolName -DatabaseServer $databaseServerName -DatabaseName $databasePrefixName"_METADATA" -PartitionMode > $null
New-SPMetadataServiceApplicationProxy -Name "$metadataSAName Proxy" -DefaultProxyGroup -ServiceApplication $metadataSAName -PartitionMode > $null
Get-SPServiceInstance | where-object {$_.TypeName -eq "Managed Metadata Web Service"} | Start-SPServiceInstance > $null
Â
User Profile Service and Proxy
$userProfileService = New-SPProfileServiceApplication -Name $userProfileSAName -ApplicationPool $saAppPoolName -ProfileDBServer $databaseServerName -ProfileDBName $databasePrefixName"_PROFILE" -SocialDBServer $databaseServerName -SocialDBName $databasePrefixName"_SOCIAL" -ProfileSyncDBServer $databaseServerName -ProfileSyncDBName $databasePrefixName"_SYNC" -PartitionMode
New-SPProfileServiceApplicationProxy -Name "$userProfileSAName Proxy" -ServiceApplication $userProfileService -DefaultProxyGroup -PartitionMode > $null
Get-SPServiceInstance | where-object {$_.TypeName -eq "User Profile Synchronization Service"} | Start-SPServiceInstance > $null
Â
Secure Store Service and Proxy
$serviceapp = New-SPSecureStoreServiceApplication -Name $secureStoreSAName -partitionmode -sharing -databaseserver $databaseServerName -databasename $databasePrefixName"_ SSOPART" -applicationpool $saAppPoolName -auditingEnabled:$true -auditlogmaxsize 30
$proxy = $serviceapp | New-SPSecureStoreServiceApplicationProxy -defaultproxygroup:$true -name "$secureStoreSAName Proxy"
Update-SPSecureStoreMasterKey -ServiceApplicationProxy $proxy -Passphrase $farmPassPhrase
Start-Sleep -s 5
Update-SPSecureStoreApplicationServerKey -ServiceApplicationProxy "$secureStoreSAName Proxy" -Passphrase $farmPassPhrase
Â
Search Service and Proxy
Start-SPEnterpriseSearchServiceInstance $searchServerName
Start-SPEnterpriseSearchQueryAndSiteSettingsServiceInstance $searchServerName
$searchApp = New-SPEnterpriseSearchServiceApplication -Name $searchSAName -ApplicationPool $saAppPoolName -DatabaseServer $databaseServerName -DatabaseName $databasePrefixName"_SEARCH" -Partitioned
$searchInstance = Get-SPEnterpriseSearchServiceInstance $searchServerName
$searchApp | Get-SPEnterpriseSearchAdministrationComponent | Set-SPEnterpriseSearchAdministrationComponent -SearchServiceInstance $searchInstance
$InitialCrawlTopology = $searchApp | Get-SPEnterpriseSearchCrawlTopology -Active
$CrawlTopology = $searchApp | New-SPEnterpriseSearchCrawlTopology
$CrawlDatabase = ([array]($searchApp | Get-SPEnterpriseSearchCrawlDatabase))[0]
$CrawlComponent = New-SPEnterpriseSearchCrawlComponent -CrawlTopology $CrawlTopology -CrawlDatabase $CrawlDatabase -SearchServiceInstance $searchInstance
$CrawlTopology | Set-SPEnterpriseSearchCrawlTopology -Active
do {write-host -NoNewline .;Start-Sleep 1;} while ($InitialCrawlTopology.State -ne "Inactive")
$InitialCrawlTopology | Remove-SPEnterpriseSearchCrawlTopology
$InitialQueryTopology = $searchApp | Get-SPEnterpriseSearchQueryTopology -Active
$QueryTopology = $searchApp | New-SPEnterpriseSearchQueryTopology -Partitions 1
$IndexPartition= (Get-SPEnterpriseSearchIndexPartition -QueryTopology $QueryTopology)
$QueryComponent = New-SPEnterpriseSearchQuerycomponent -QueryTopology $QueryTopology -IndexPartition $IndexPartition -SearchServiceInstance $searchInstance
$PropertyDatabase = ([array]($searchApp | Get-SPEnterpriseSearchPropertyDatabase))[0]
$IndexPartition | Set-SPEnterpriseSearchIndexPartition -PropertyDatabase $PropertyDatabase
$QueryTopology | Set-SPEnterpriseSearchQueryTopology -Active
$searchAppProxy = New-SPEnterpriseSearchServiceApplicationProxy -Name "$searchSAName Proxy" -SearchApplication $searchSAName -Partitioned > $null
Â
We will now create the other general Service Applications using the following Powershell.
Â
Access Services and Proxy
New-SPAccessServiceApplication -Name $accesssSAName -ApplicationPool $saAppPoolName > $null
Get-SPServiceInstance | where-object {$_.TypeName -eq "Access Database Service"} | Start-SPServiceInstance > $null
Â
Visio Service and Proxy
New-SPVisioServiceApplication -Name $visioSAName -ApplicationPool $saAppPoolName > $null
New-SPVisioServiceApplicationProxy -Name "$visioSAName Proxy" -ServiceApplication $visioSAName > $null
Get-SPServiceInstance | where-object {$_.TypeName -eq "Visio Graphics Service"} | Start-SPServiceInstance > $null
Â
Web Analytics Service and Proxy
$stagerSubscription = "<StagingDatabases><StagingDatabase ServerName='$databaseServerName' DatabaseName='SHAREPOINT_STAGE'/></StagingDatabases>"
$reportingSubscription = "<ReportingDatabases><ReportingDatabase ServerName='$databaseServerName' DatabaseName='SHAREPOINT_WAREHOUSE'/></ReportingDatabases>"
New-SPWebAnalyticsServiceApplication -Name $WebAnalyticsSAName -ApplicationPool $saAppPoolName -ReportingDataRetention 20 -SamplingRate 100 -ListOfReportingDatabases $reportingSubscription -ListOfStagingDatabases $stagerSubscription > $null
New-SPWebAnalyticsServiceApplicationProxy -Name "$WebAnalyticsSAName Proxy" -ServiceApplication $WebAnalyticsSAName > $null
Get-SPServiceInstance | where-object {$_.TypeName -eq "Web Analytics Service"} | Start-SPServiceInstance > $null
Get-SPServiceInstance | where-object {$_.TypeName -eq "Web Analytics Data Processing Service"} | Start-SPServiceInstance > $null
Â
Excel Services and Proxy
New-SPExcelServiceApplication -name $excelSAName –ApplicationPool $saAppPoolName > $null
Set-SPExcelFileLocation -Identity "http://" -ExcelServiceApplication $excelSAName -ExternalDataAllowed 2 -WorkbookSizeMax 10 -WarnOnDataRefresh:$true
Get-SPServiceInstance | where-object {$_.TypeName -eq "Excel Calculation Services"} | Start-SPServiceInstance > $null
Â
Performance Point Service and Proxy
New-SPPerformancePointServiceApplication -Name $performancePointSAName -ApplicationPool $saAppPoolName > $null
New-SPPerformancePointServiceApplicationProxy -Default -Name "$performancePointSAName Proxy" -ServiceApplication $performancePointSAName > $null
Get-SPServiceInstance | where-object {$_.TypeName -eq "PerformancePoint Service"} | Start-SPServiceInstance > $null
Â
State Service and Proxy
New-SPStateServiceDatabase -Name $databasePrefixName"_STATE" -DatabaseServer $databaseServerName | New-SPStateServiceApplication -Name $stateSAName | New-SPStateServiceApplicationProxy -Name "$stateSAName Proxy" -DefaultProxyGroup > $null
Â
Word Conversion Service and Proxy
New-SPWordConversionServiceApplication -Name $WordAutomationSAName -ApplicationPool $saAppPoolName -DatabaseServer $databaseServerName -DatabaseName $databasePrefixName"_WORD" -Default > $null
Get-SPServiceInstance | where-object {$_.TypeName -eq "Word Automation Services"} | Start-SPServiceInstance > $null
Â
Usage Application
New-SPUsageApplication -Name $usageSAName -DatabaseName $databasePrefixName" _USAGE" -DatabaseServer $databaseServerName
Â
We then need to create the Subscription Service Application using the following command:
Â
$App = New-SPSubscriptionSettingsServiceApplication -ApplicationPool $saAppPoolName -Name $subscriptionSAName -DatabaseName $databasePrefixName" _SUBSCRIPTION"
$proxy = New-SPSubscriptionSettingsServiceApplicationProxy -ServiceApplication $App
Get-SPServiceInstance | where {$_.TypeName -eq "Windows SharePoint Services Subscription Settings Service"} | Start-SPServiceInstance
Â
So now we have everything created we now have a full list of service applications ready to use.
Â
So now we need to perform the following steps:
Â
- Provision Service Applications in Partition Mode
- Create the Tenant Sites (assuming the web applications have been created beforehand)
- Create Subscriptions
- Associate Subscriptions to the Tenant Sites
- Create Tenant Admin Sites
To create the Tenant Sites we are going to use the following commands. Firstly we need to set some variables as before:
Â
$tenantSiteTemplate = "BLANKINTERNETCONTAINER#0"
$tenantTemplate = "tenantadmin#0"
$tenantMySiteTemplate = "SPSMSITEHOST#0"
$tenantSecondaryOwnerAlias = "DOMAIN\Administrator"
$tenantOneOwnerAlias = "DOMAIN\TenantOneAdmin"
$tenantTwoOwnerAlias = "DOMAIN\TenantTwoAdmin"
$tenantThreeOwnerAlias = "DOMAIN\TenantThreeAdmin"
Â
Now we use the following commands to create the sites:
Â
new-spsite -url $tenantOneUrl -template $tenantSiteTemplate -owneralias $tenantOneOwnerAlias
new-spsite -url $tenantTwoUrl -template $tenantSiteTemplate -owneralias $tenantTwoOwnerAlias
new-spsite -url $tenantThreeUrl -template $tenantSiteTemplate -owneralias $tenantThreeOwnerAlias
Â
Once these sites are created we need to now get a reference to them and created the subscriptions and associate them to the newly created sites.
Â
$site1 = Get-SPSite | where {$_.url -eq $tenantOneUrl }
$site2 = Get-SPSite | where {$_.url -eq $tenantTwoUrl }
$site3 = Get-SPSite | where {$_.url -eq $tenantThreeUrl }
Â
$sub1 = New-SPSiteSubscription
$sub2 = New-SPSiteSubscription
$sub3 = New-SPSiteSubscription
Â
Set-SPSite -Identity $site1 -SiteSubscription $sub1
Set-SPSite -Identity $site2 -SiteSubscription $sub2
Set-SPSite -Identity $site3 -SiteSubscription $sub3
Â
So now we have all the service applications created, tenant sites created and subscriptions created and assigned we are now ready to perform the rest of the configuration. In the next post we will look at the next steps:
Â
- Provision Service Applications in Partition Mode
- Create the Tenant Sites (assuming the web applications have been created beforehand)
- Create Subscriptions
- Associate Subscriptions to the Tenant Sites
- Create Tenant Admin Sites
Â
We will also look at the association of the service applications within the subscription, as well as the configuration for using different OU's for each tenant. Hope this helps.
Â
NOTE: If I have missed anything so far let me know. J