harbar.net component based software & platform hygiene

Rational Guide to Multi Tenancy with SharePoint 2010, Part Six: Provisioning Tenants

Print | posted on Tuesday, September 14, 2010 10:33 AM

 

Introduction

This, the sixth part of the Rational Guide article on multi tenancy, will walk through the creation of feature packs and the provisioning of Tenants for the sample scenario detailed in part three.

If you haven’t checked out the previous parts, I strongly encourage you to review them. I won’t repeat information and I assume you have read the previous parts, which are:

  1. Feature and Capability Overview
  2. Planning your Deployment
  3. Example Scenario and what Multi Tenancy brings to the party
  4. Configuring the base Infrastructure
  5. Creating Partitioned Service Applications
  6. Provisioning Tenants (this article)
  7. Testing the Functionality and other considerations

IMPORTANT: You must have run the scripts in the order presented in this series. If you haven’t run the scripts in part four, you should go do that before proceeding!

 

So far in the article series we have configured the core infrastructure and created service applications in partitioned mode. Whilst there are a few nuances to those steps which are critical to success, there is nothing clever going on so far, and we have zero functionality. Now we look at the really interesting part of the deployment, actually provisioning tenants and setting up the service applications to work with them.


Creating Feature Packs

For our scenario we wish each of the three tenants to use a different edition (or SKU) of SharePoint 2010. Therefore we need to create our Feature Packs which will encapsulate the SKU features before we provision the tenants.

This work is pretty simple. All we need to do is create a feature pack for each SKU and add to each the necessary features in an additive fashion. Luckily this ‘work’ has been done for us in the Hoster’s Guide. This is a very long script, so I will simply include here an excerpt. Get the full script here.

Add-PSSnapin microsoft.sharepoint.powershell  -ea silentlycontinue

#validate pre-reqs
$app = Get-SPServiceApplication |? {$_.TypeName -eq "Microsoft SharePoint Foundation Subscription Settings Service Application"}
if($app -eq $null){
 "Settings Service Application must already exist"
 exit;
}

#Create an alias for Add-SPSiteSubscriptionFeaturePackMember 
Set-Alias AddFeature Add-SPSiteSubscriptionFeaturePackMember



#Foundation Feature Pack
$ffp = New-SPSiteSubscriptionFeaturePack
AddFeature -identity $ffp -FeatureDefinition AdminLinks -ea SilentlyContinue
AddFeature -identity $ffp -FeatureDefinition AnnouncementsList -ea SilentlyContinue

# ... the rest of the features ...

AddFeature -identity $ffp -FeatureDefinition workflowProcessList -ea SilentlyContinue 
AddFeature -identity $ffp -FeatureDefinition XmlFormLibrary -ea SilentlyContinue 



#Standard Feature Pack
$sfp = New-SPSiteSubscriptionFeaturePack
AddFeature -identity $sfp -FeatureDefinition AccSrvRestrictedList -ea SilentlyContinue 
AddFeature -identity $sfp -FeatureDefinition AdminLinks -ea SilentlyContinue         

# ... the rest of the features ...

AddFeature -identity $sfp -FeatureDefinition Workflows -ea SilentlyContinue    
AddFeature -identity $sfp -FeatureDefinition XmlFormLibrary -ea SilentlyContinue



#Enterprise Feature Pack
$efp = New-SPSiteSubscriptionFeaturePack
AddFeature -identity $efp -FeatureDefinition AccSrvMSysAso -ea SilentlyContinue  
AddFeature -identity $efp -FeatureDefinition AccSrvRestrictedList -ea SilentlyContinue

# ... the rest of the features ...

AddFeature -identity $efp -FeatureDefinition Workflows -ea SilentlyContinue        
AddFeature -identity $efp -FeatureDefinition XmlFormLibrary -ea SilentlyContinue




#output IDs to text file
$out = "foundationFeatures = "+$ffp.ID
$out = $out + "standardFeatures = "+$sfp.ID
$out = $out + "enterpriseFeatures = "+$efp.ID
$out | out-File "featurePacks.txt"

All I’ve done here is add a little bit at the end of the script to output the Feature Pack IDs to a text file, as we will need these later when provisioning tenants. Whilst we can add custom properties to feature packs, it is simplest and easiest to work directly with the GUIDs used.

Shoving the GUIDs out to a text file is OK for this simple example scenario which is intended for explanation purposes, but is not much good for the real world. A better approach would be to add them to the Farm’s property bag.


Provisioning Tenants

Now we get to the interesting part. The general idea here is to have a chunk of script that can be run repeatedly which sets up a tenant and all the related configuration.

First up we set up some variables for our hosting web app and the name of the two service apps we’ll be working with. Also we’ll add in the GUIDs for our feature packs:

Add-PSSnapin Microsoft.SharePoint.Powershell -EA 0 

# farm details - update to reflect environment
$hostingMainURL = "http://sph1"
$upaProxyName = "Hosting Farm Tenant User Profile Service Proxy"
$mmsProxyName = "Hosting Farm Tenant Managed Metadata Service Proxy"

# feature packs - update after creating them
$foundationFeatures = "82b4a51b-de9a-42f4-b035-9427fa8ff222"
$standardFeatures = "5906fe7c-cca3-4cc5-a074-4594f1d36933"
$enterpriseFeatures = "107ae862-2fed-4b14-b542-ae9b327f2b71"

 

Now before we can continue, you must add the user whom is running this script to the Permissions of the User Profile Service Application. This is NOT the Administrators, but the Permissions. How you choose to do that is up to you. If you don’t do this it won’t work. I also strongly recommend you quit your PowerShell host after setting this. This is why I haven’t scripted the permissions within it. I am not going to detail the reasons behind that in this article (it may show up in a separate post, it’s quite a meaty topic!) The script will nag you about this and then immediately afterwards run an IISReset, which is required:

$a = Read-Host "Have you added the user running this script to *permissions* on the UPA?"

Write-Host "Resetting IIS..."
iisreset

Now we create a PowerShell Function for doing all the real work. As you can see we pass in a number of parameters – these are all the things we need to configure the various options. I’ve built this so as to take in the Customer Name – a nice way to shortcut all the URLs and OUs and such. But of course this is just a sample scenario, in the real world this would need further refinement.

We start by grabbing a reference to our hosting web app and create a new site subscription:

function ProvisionTenant($customerName, $customerTenantAdmin, $customerTenantAdminEmail, $customerFeatures, $hostingMainURL, $upaProxyName, $mmsProxyName, $foundationFeatures)
{
    Write-Host "Provisioning Tenant..."
    Write-Host "Name: $customerName"
    Write-Host "Admin: $customerTenantAdmin"
    Write-Host "Email: $customerTenantAdminEmail"
    Write-Host "Features: $customerFeatures"
    Write-Host "Foundation: $foundationFeatures"

    # grab the web app
    $webApp = Get-SPWebApplication $hostingMainURL

    # create new Site Subscription
    Write-Host "Creating Site Subcription..."
    $sub = New-SPSiteSubscription

Once we have a Site Subscription, we can configure the feature pack to be used, and the People Picker to only use a particular OU, which we pass in in a DN format:

# assign feature pack and configure the OU to use in the People Picker for the Subscription
    Write-Host "Assiging Feature Pack and configuring People Picker..."
    Set-SPSiteSubscriptionConfig –id $sub -FeaturePack $customerFeatures -UserAccountDirectoryPath "OU=$customerName,OU=Customers,DC=sharepoint,DC=com"

Next up, we create a member site using the Subscription and passing in the web app to the –HostHeaderWebApplication. We must do this first as the other steps require a site at the ‘root’.

    # create the "main" member site (we need a site at the root to use Host Headers and Managed Paths in the following cmdlets)
    Write-Host "Creating Member Site..."
    New-SPSite -url "http://$customerName.sharepoint.com" -SiteSubscription $sub -HostHeaderWebApplication $webApp -owneralias $customerTenantAdmin -owneremail $customerTenantAdminEmail -template sts#0

Then we create the Tenant Admin site note the -AdministrationSiteType parameter:

# create Tenant Admin site 
    Write-Host "Creating Tenant Admin site..."
    New-SPSite -url "http://$customerName.sharepoint.com/admin" -SiteSubscription $sub -HostHeaderWebApplication $webApp -owneralias $customerTenantAdmin -owneremail $customerTenantAdminEmail -template tenantadmin#0 -AdministrationSiteType TenantAdministration

Now we can move on to the configuration of the Service Applications. Remember that our old ‘global’ settings no longer make sense. We have cmdlets for setting these on a per-tenant basis. FIrst we will check to see if the tenant is using the Foundation Feature Pack, and if not configure the SAs. (BDC and SSO have no per tenant config that is set upfront). We also need to create a MySite host site collection before we can configure the UPA:

# everything else needs standard
    if (!($customerFeatures -eq $foundationFeatures))
    {
        Write-Host "Tenant has SharePoint Server features"
        # create a mysite host
        Write-Host "Creating My Site Host..."
        New-SPSite -url "http://$customerName.sharepoint.com/mysites" -SiteSubscription $sub -HostHeaderWebApplication $webApp -owneralias $customerTenantAdmin -owneremail $customerTenantAdminEmail -template SPSMSITEHOST#0

At this stage we can configure the UPA. Note the –SynchronizationOU parameter is a plain string value. We could enter a DN here, but it won’t work properly, so don’t!

        # configure the MySites host, MySites path, Naming Resolution and Profile Sync OU for the Subscription
        Write-Host "Configuring Tenant Profile Config..."
        $upaProxy = Get-SPServiceApplicationProxy | where-object {$_.DisplayName -eq $upaProxyName}
        Add-SPSiteSubscriptionProfileConfig -id $sub -SynchronizationOU $customerName -MySiteHostLocation "http://$customerName.sharepoint.com/mysites" -MySiteManagedPath "/mysites/personal" -SiteNamingConflictResolution "None" -ProfileServiceApplicationProxy $upaProxy

Next up is to create a site collection for the content type hub and configure the MMS service app.

        # create a site for the Content Type Gallery
        Write-Host "Creating Content Type Gallery..."
        New-SPSite -url "http://$customerName.sharepoint.com/cthub" -SiteSubscription $sub -HostHeaderWebApplication $webApp -owneralias $customerTenantAdmin -owneremail $customerTenantAdminEmail -template sts#0

        # configure the Content Type Gallery for the Subscription
        Write-Host "Configuring Tenant Content Type Gallery..."
        $mmsProxy = Get-SPServiceApplicationProxy | where-object {$_.DisplayName -eq $mmsProxyName}
        # ContentTypeHub feature activation may fail - if so activate manually
        Set-SPSiteSubscriptionMetadataConfig -identity $sub -serviceProxy $mmsProxy -huburi "http://$customerName.sharepoint.com/cthub" -SyndicationErrorReportEnabled
        Write-Host "Activating Content Type Hub..."
        Enable-SPFeature -Identity ContentTypeHub -url "http://$customerName.sharepoint.com/cthub"
        
        
    }
    Write-Host "Tenant Provisioned!"
    return $sub;
}

That’s it! Now we can call our function for each tenant to provision them:

# customer details - do this for each tenant
$customerName = "Microsoft"
$customerTenantAdmin = "SHAREPOINT\msadmin"
$customerTenantAdminEmail = "msadmin@sharepoint.com"
$customerFeatures = $enterpriseFeatures

ProvisionTenant $customerName $customerTenantAdmin $customerTenantAdminEmail $customerFeatures $hostingMainURL $upaProxyName $mmsProxyName $foundationFeatures


$customerName = "Oracle"
$customerTenantAdmin = "SHAREPOINT\oadmin"
$customerTenantAdminEmail = "oadmin@sharepoint.com"
$customerFeatures = $standardFeatures

ProvisionTenant $customerName $customerTenantAdmin $customerTenantAdminEmail $customerFeatures $hostingMainURL $upaProxyName $mmsProxyName $foundationFeatures


$customerName = "Apple"
$customerTenantAdmin = "SHAREPOINT\appleadmin"
$customerTenantAdminEmail = "appleadmin@sharepoint.com"
$customerFeatures = $foundationFeatures

ProvisionTenant $customerName $customerTenantAdmin $customerTenantAdminEmail $customerFeatures $hostingMainURL $upaProxyName $mmsProxyName $foundationFeatures

It’s all very simple and reliable. The entire script has been tested over 3,000 times with no failures on a variety of different servers. Of course the scenario is contrived for the purposes of explanation, however with minor tweaks it can be modified to suit.

In the final instalment, I’ll talk through some testing considerations, along with other things that are critical to bear in mind when looking at implementing multi-tenant solutions with SharePoint 2010.

If you want the complete script, it is available on the TechNet Script Center.

Feedback

Gravatar

# re: Rational Guide to Multi Tenancy with SharePoint 2010, Part Six: Provisioning Tenants

Spence,

with the speed you're publishing the last 3 parts it's hard to keep up :) I take a deep bow for your knowledge and clear instructions. Thank you very much.

I have followed your instructions to the letter, but have run into a problem though...

When adding the user whom is running this script to the Permissions of the User Profile Service Application (using the Central Administration site), I receive the following error as soon as I try to start the people picker: ""An error has occured in the claim providers configured from this site collection".

I have not yet done any claim authentication configuration. Do I need to dive into the claim authentication config before I can continue or have I missed a step somewhere?

Kind regards,
Erik Cheizoo
eXcellence & Difference
The Netherlands

9/14/2010 6:50 PM | Erik Cheizoo
Gravatar

# re: Rational Guide to Multi Tenancy with SharePoint 2010, Part Six: Provisioning Tenants

Hate to answer my own posts, but it might help others.

The mistake I made was accessing the server using the FQDN (http://servername.domainname.net:adminport) while the site was created using the netbios name (http://server:adminport). This caused the claims provider to fail.

I guess I need to create an AAM for the FQDN of the server, as the netbios name will not always work...

Application Eventviewer
Level: Warning
Event ID: 8059
Source: SharePoint Foundation
Message:
Alternate access mappings have not been configured. Users or services are accessing the site https://<servername>:<adminport> with the URL https://<FQDN servername>:<admin port>. This may cause incorrect links to be stored or returned to users. If this is expected, add the URL https://od-app03.online-documents.net:10443 as an AAM response URL.

9/14/2010 7:06 PM | Erik Cheizoo
Gravatar

# re: Rational Guide to Multi Tenancy with SharePoint 2010, Part Six: Provisioning Tenants

i have can't seem to any of the tenant sites (http://Customer.domain.local). when i "View All Site collections" i can see that they were created. all i get is "Internet Explorer can not display this webpage?? any idea what i've missed? also i have these wierd characters (i:0#.w) in front of the Primary Administrators log on ID, no idea where they came from.. what did i miss

12/14/2011 11:58 PM | Giovanni
Gravatar

# re: Rational Guide to Multi Tenancy with SharePoint 2010, Part Six: Provisioning Tenants

Great set of articles, I really appreciate your work on this. I built a farm using the AutoInstaller scripts from codeplex (tweaking some things and removing some things based on the first set of your posts), and then used your scripts to build the Service Apps, Feature Packs, and to provision tenants. I do have a couple of questions, hopefully you can provide some insight.

I have read that if you are going to use host named site collections, you cannot enter a host header when creating a web application. That is fine, unless you want to create additional web applications using the same port. How do I get around this? Will it work if I enter a host header when creating each web application, but remove the host header in IIS, and assign each web site a unique IP address?

What if I have two tenants that need to share the same OU in AD? I have a client that needs a site called portal.client.com, and another called www.client.com, but the same AD users will be using both sites.

Until recently, you cannot have http site collections on a web application that was created as https. There is a new switch that allows this now. I turned it on, but I cant seem to browse to an http site collection in an https web application. I was just wondering if you have had any experience with this.

Lastly, how do I handle certificates for multiple domains? If in a single web application I have one site collection with a portal.clientA.com url, and another site collection with a portal.clientB.com url, how do I assign two certificates to the web application in IIS? I assume I will need to use something like UAG for off-box SSL termination. I have not seen very good documentation for configuring this, unless it is just done automatically when creating UAG trunks.

Thanks again for any insight you can provide!

12/28/2012 7:55 PM | Matt Palmgren
Gravatar

# re: Rational Guide to Multi Tenancy with SharePoint 2010, Part Six: Provisioning Tenants

Matt:

if you need additional web apps then you can either create them with host headers, or if you want them to host HNSC then you'll need to remove that host header and change the IP address they listen on in IIS after creation. This all works fine, it's just that SharePoint is ignorant of IP address bindings so you have to trick it with host headers. Not sure *why* you would need more web apps but there it is.

Tenants can NOT share the same OU, this is a design constraint of the Multi tenant design model. There are workarounds but they are not trivial and they are not supported. From the requirements you outline, tenancy is not the appropriate deployment for them.

If you want to mix and match HTTP and HTTPS site collections in the same web application, the web application must be created using HTTP.

You will need multiple Certficates, and this complicates things considerably. Alternatively use different web applications or use sub domains

1/8/2013 4:59 AM | harbars
Gravatar

# re: Rational Guide to Multi Tenancy with SharePoint 2010, Part Six: Provisioning Tenants

Hi Harbars,

We also want to setup HTTPS in our multi-tenant environment. in your answer to Matt you tell that you have to create a web app with HTTP.

"If you want to mix and match HTTP and HTTPS site collections in the same web application, the web application must be created using HTTP."

so when I do that I receive:
"WARNING: The port specified for the new host header site does not match any known bindings in the specified Web Application. The new site will not be accessible if the Web Application is not extended to an IIS Web Site serving this port."

So I extended the web app with SSL. But when you run the script again you will get: "New-SPSite : <nativehr>0x80070057</nativehr><nativestack></nativestack>"

Can you give us/me some more information how to do this? Creating a mixed environment.
In IIS I that nothing yet with any SSL certificate

2/22/2013 1:35 PM | Sander van Gelderen
Gravatar

# re: Rational Guide to Multi Tenancy with SharePoint 2010, Part Six: Provisioning Tenants

Sander: you need to create the app using HTTP then manually add an SSL binding in IIS. then you can create the site collections

2/24/2013 9:18 PM | harbars

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 6 and 3 and type the answer here: