Blog of Sundar Narasiman

SharePoint Multi tenancy – Faqs

1. What is multi-tenancy in SharePoint ?

The SharePoint platform has the capability to isolate and separate data from different web sites while sharing Service Application resources across same sites. This capability is called as Multi-tenancy. It primarily relies on Site Subscriptions and Subscription Ids

2. How data is partitioned in a hosted environment in SharePoint ?

The data is partitioned in a hosted environment in SharePoint by using the concept of Site Subscriptions. Site Subscription group tenant data across all site-collections owned by tenant, and provide the ability to separate and group each tenant’s data in a shared environment.

3. What is the role of Administrator in the context of hosted SharePoint environments ?

The administrators can centrally deploy and manage features & services, while providing tenants full control over the usage and experience.

4. What is the role of Subscription Ids with respect to SharePoint multi-tenancy ?

The site-collections of each and every tenant are grouped based on a common subscription ID. The Subscription ID helps to map features and services to tenants and also to partition service data according to tenant.

5. Can multiple subscriptions be hosted be hosted in a single web application ?

Yes, multiple Subscriptions can be hosted inside a single web application. Again, multiple subscriptions can also reside in the shared content database.

6. How administrators manage subscriptions and features for each tenant?

Administrators can define which services are available and activated for each tenant. The Subscription ID for a tenant can be used to map service partitions to site-collections

7. Can a service data be shared across multiple tenants ?

Yes, the service data can be shared across multiple tenants, so that all the tenants can share data for a specific service.

8. Can a service data be partitioned for each and every tenant ?

Yes, the service data can be partitioned for each and every tenant, ensuring that sensitive data is not exposed to other tenants. In this case, service data for a single tenant need to be implemented within a separate partition for that service.

9. What are the various roles involved when it comes to Tenant Administration ?

  • Hosting Company
  • Hosted Company Administrator
  • Hosted Company

10. What are the roles of a Hosting Company when it comes to Tenant Administration ?

The following are the typical roles of a Hosting Company for Tenant Administration:

  • Manages the farm-level settings and hardware
  • Controls the database configurations
  • Installs all new approved features and solutions
  • Brands the Tenant Administrator pages

11. What are the roles of a Hosted Company Administrator when it comes to Tenant Administration ?

The following are the typical roles of a Hosted Company when it comes to Tenant Administration :

  • Purchases space, features, and bandwidth from hosting company
  • Controls the architecture of customer sites but not the content
  • Reviews usage statistics

12. What are the roles of a Hosted Company when it comes to Tenant Administration ?

The following are the typical roles of a Hosted Company when it comes to Tenant Administration :

  • Owns a site collection
  • Installs or removes set of features and solutions
  • Configures features and services
  • Reviews usage statistics

 Subscribe to my blog

Migrate SharePoint sites using Content Migration APIs

The export/import method provides the flexibility to migrate a site/sub-site from one web application to another web application (in a different content database) within a farm. It also provides the flexibility to export a sub-site and import it as a root site-collection in another web application. In this post I’d be discussing about how to programmatically migrate SharePoint sites using Content Migration APIs.

 

Code to export a site

                    string sourceSiteURL = "Your site url";//site to be exported
                    SPExportObject exportObject = "Path where you site to be exported";
                    string folderPath = "folder path";

                    using (SPSite sourceSite = new SPSite(sourceSiteURL))
                    {
                        using (SPWeb sourceWeb = sourceSite.OpenWeb())
                        {                            
                            //Create the Export Setting object and update the setting properties.  
                            SPExportSettings settings = new SPExportSettings();
                            settings.SiteUrl = sourceWeb.Url;
                            settings.ExportMethod = SPExportMethodType.ExportAll;
                            settings.BaseFileName = EXPORT_FILENAME; // "export.cmp";
                            settings.FileLocation = "provide file location";
                            settings.LogFilePath = @folderPath + LOG_FILENAME; // "\LogFile.log";                                
                            settings.IncludeSecurity = SPIncludeSecurity.All;

                            setExcludeDependencies(migrationSettings, settings);
                            settings.OverwriteExistingDataFile = true;
                            settings.CommandLineVerbose = true;
                            settings.FileMaxSize = 1024;
                            settings.FileCompression = true;

                            // Add the Export object to the ExportSetting Object  
                            settings.ExportObjects.Add(exportObject);


                            // add the Export Settings to the SPExport object and Run the Export .  
                            SPExport export = new SPExport(settings);
                            export.Run();

                            LogAll.logTextWriting(true, ServerConstant.exportSuccessfully);
                            isExportCompleted = true;
                        }
                    }
               

Code to import a site

                    // Get the Site collection Url from the config file.  

                    using (SPSite rootSiteColl = new SPSite(destinationSiteURL))
                    {
                        SPWebApplication webApp = rootSiteColl.WebApplication;
                        rootSiteColl.AllowUnsafeUpdates = true;
                        using (SPWeb rootWeb = rootSiteColl.OpenWeb())
                        {
                            // Package import
                            System.Uri siteURL = new Uri(destinationSiteURL);
                            string baseDataFileName = EXPORT_FILENAME; // "export.cmp";
                            string dataFileLocation = @folderPath;
                            string logFileLocation = @folderPath + LOG_FILENAME; // "\LogFile.log";

                            SPImportSettings importSettings = new SPImportSettings(siteURL, dataFileLocation, baseDataFileName);
                            importSettings.IncludeSecurity = SPIncludeSecurity.All;
                            importSettings.RetainObjectIdentity = true;                            
                            importSettings.CommandLineVerbose = true;
                            importSettings.LogFilePath = logFileLocation;
                            importSettings.WebUrl = destinationSiteURL;
                            importSettings.FileCompression = true;


                            SPImport import = new SPImport(importSettings);
                            import.Run();
                            rootWeb.AllowUnsafeUpdates = false;
                            webApp.FormDigestSettings.Enabled = true;
                            webApp.FormDigestSettings.Expires = true;
                            rootWeb.Close();
                        }
                        rootSiteColl.AllowUnsafeUpdates = false;
                    }

The power of the import functionality is that we can pick and choose whether to retain the security, versions, object ids etc. The main drawback of this approach
is that it does not preserve workflow instances, workflow associations, history and tasks. Every workflow association must be recreated and there is no way
to restore the running instances from original site. But nonetheless, the export/import has real power or re-arranging the site-hierarchy in the target.
 

 Subscribe to my blog

SharePoint 2013 Programmatically read list items using Java Script

Yesterday, I saw a question in MSDN forums on how to programmatically read SharePoint List items using JavaScript. Here is the full workable code below :-

var siteUrl = '/sites/MySiteCollection';

function retrieveListItems() {

    var clientContext = new SP.ClientContext(siteUrl);
    var oList = clientContext.get_web().get_lists().getByTitle('YourCustomList');
        
    var camlQuery = new SP.CamlQuery();
    camlQuery.set_viewXml('<View><Query><Where><Geq><FieldRef Name='ID'/>' + 
        '<Value Type='Number'>1</Value></Geq></Where></Query><RowLimit>10</RowLimit></View>');
    this.collListItem = oList.getItems(camlQuery);
        
    clientContext.load(collListItem);
        
    clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));        
        
}

function onQuerySucceeded(sender, args) {

    var listItemInfo = '';

    var listItemEnumerator = collListItem.getEnumerator();
        
    while (listItemEnumerator.moveNext()) {
        var oListItem = listItemEnumerator.get_current();
        listItemInfo += 'nID: ' + oListItem.get_id() + 
            'nTitle: ' + oListItem.get_item('Title') + 
            'nBody: ' + oListItem.get_item('Body');
    }

    alert(listItemInfo.toString());
}

function onQueryFailed(sender, args) {

    alert('Request failed. ' + args.get_message() + 'n' + args.get_stackTrace());
}

 Subscribe to my blog

Inspecting SharePoint 2013 ContextToken and Refresh Token

In previous post, I briefly discussed about the OAuth Tokens and SharePoint 2013 Authentication & Authorization.  We know that there are couple of tokens, namely Context-Token and Refresh-Token are involved in the life-cycle of SharePoint 2013 App Authentication and Authorization. These tokens are not exactly the SAML 1.1 tokens and they  are bit different. The whole intend of this article is to inspect and understand what these tokens are.

In this article I would be creating a sample Auto-Hosted App, inside the page_load of the App-web project (of auto-hosted app) I would be adding some snippet of code to inspect and fetch the OAuth Context Token and Refresh Token.

Let’s create a sample Auto-Hosted App in Visual Studio 2012 with name ‘TestAppforOAuth’,

pic2

Open the default.aspx.cs of the AppWeb Project

Import the following namespaces at the top of the project

using Microsoft.SharePoint.Client;
using System.Net;
using System.IO;

In the Page_Load method add the following snippet of code

TokenHelper.TrustAllCertificates();
string contextTokenString =
      TokenHelper.GetContextTokenFromRequest(Request);

if (contextTokenString != null)
      {
            SharePointContextToken contextToken =
            TokenHelper.ReadAndValidateContextToken(
                  contextTokenString, Request.Url.Authority);

            Response.Write("<h2>Valid context token</h2>");
            Response.Write(
                  "<p>" + contextToken.ToString() + "</p>");
            Response.Flush();

            Uri sharepointUrl = new      
                  Uri(Request.QueryString["SPHostUrl"]);
            string accessToken =
                  TokenHelper.GetAccessToken(contextToken, 
                  sharepointUrl.Authority).AccessToken;

            Response.Write("<h2>Valid access token 
                  retrieved</h2>");
            Response.Write("<p>" + accessToken + "</p>");
            Response.Flush();

            HttpWebRequest request =
                   (HttpWebRequest)HttpWebRequest.Create
                   (sharepointUrl.ToString() + 
                  "/_api/Web/title");
            request.Headers.Add("Authorization", "Bearer " +
                  accessToken);
            HttpWebResponse response = 
                   (HttpWebResponse)request.GetResponse();
            StreamReader reader = new 
                  StreamReader(response.GetResponseStream());

            Response.Write("<h2>Web title retrieved using 
                  REST</h2>");
            Response.Write("<p>" + reader.ReadToEnd() + "</p>");
            Response.Flush();
      }

Configure the Tenant level Read Permission for the App.

app permission1

app permission2

Hit F5, Deploy the app and trust it. If we inspect the Tokens these are not SAML, they are what they call it as JWT Tokens.

tokens

 Subscribe to my blog

Add heading and link to SharePoint Global Navigation through Powershell

I was wondering whether there is a way to add the heading and link to the Top Navigation of SharePoint site using PowerShell Script. It is feasible, here is the full workable script below :-

$web = Get-SPWeb "http://yourserver:9090/sites/yoursite/"
$pubWeb = [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($web)



function AddHeading($HeadingName,$Link)
{


$CreateSPNavigationNode = [Microsoft.SharePoint.Publishing.Navigation.SPNavigationSiteMapNode]::CreateSPNavigationNode

$qlNav = $pubWeb.Navigation.CurrentNavigationNodes

$headingNode = $CreateSPNavigationNode.Invoke($HeadingName, $Link, [Microsoft.SharePoint.Publishing.NodeTypes]::Heading, $qlNav)

$web.Update()

}



function AddLink($HeadingName,$DisplayName,$URL,$External)

{
Start-Sleep -Seconds 5

$qlNav1 = $web.Navigation.QuickLaunch

$qlNav1 | select Title, ID

$qlink = $qlNav1 | where {$_.Title -eq $HeadingName}

$linkNode = New-Object Microsoft.SharePoint.Navigation.SPNavigationNode($DisplayName,$URL,$External)

$qlink.Children.AddAsLast($linkNode)

}


AddHeading "Heading" "http://microsoft.com"
AddLink "Heading" "Link" "http://hotmail.com" "True"

 Subscribe to my blog

Add heading and link to SharePoint left navigation through powershell

I was wondering whether there is a way to add the heading and link to the left navigation of a SharePoint site using Powershell script. It is feasible, here is the full workable script below:-

$web = Get-SPWeb "http://yourserver:9090/sites/SCO/"
$pubWeb = [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($web)


function AddLeftNavHeading($HeadingName,$Link)
{


$CreateSPNavigationNode = [Microsoft.SharePoint.Publishing.Navigation.SPNavigationSiteMapNode]::CreateSPNavigationNode

$qlNav = $pubWeb.Navigation.CurrentNavigationNodes

$headingNode = $CreateSPNavigationNode.Invoke($HeadingName, $Link, [Microsoft.SharePoint.Publishing.NodeTypes]::Heading, $qlNav)

$web.Update()

}


function AddLeftNavLink($HeadingName,$DisplayName,$URL,$External)

{
Start-Sleep -Seconds 5

$qlNav1 = $web.Navigation.QuickLaunch

$qlNav1 | select Title, ID

$qlink = $qlNav1 | where {$_.Title -eq $HeadingName}

$linkNode = New-Object Microsoft.SharePoint.Navigation.SPNavigationNode($DisplayName,$URL,$External)

$qlink.Children.AddAsLast($linkNode)

}

AddLeftNavHeading "Heading" "http://microsoft.com"
AddLeftNavLink "Heading" "Link" "http://hotmail.com" "True"

 Subscribe to my blog

Understanding OAuth in the world of SharePoint 2013 App

The OAuth is the new buzz in the world of SharePoint 2013 App development.  Just to remember, OAuth is not the protocol for authenticating users to access SharePoint. It would still be done by Claims Authentication. The OAuth comes into picture when we want to authenticate and authorize SharePoint 2013 Apps.

I’ll start with some briefing on OAuth and the key concepts that we need to understand about OAuth. OAuth is the internet protocol for creating and managing app identity. It is also a cross-platform mechanism for authentication and authorizing apps. The OAuth is also the emerging internet standard which is used by Facebook, Twitter and Google.

OAuth gives the power and flexibility of having app identity in addition to the user identity. Here are the some pointers about App Identity

  • App should be granted permissions independently of user permission
  • App can request specific permission from the user during installation
  • App can be granted more permission than the user (Elevation)
  • App is constrained to what it can do during and after installation

Here are some important concepts around OAuth

1. Content Owner – User who grants permission to content in a site

2. Client App – This is the remote App (running on a Cloud or Hosted environment) that needs permission to Site Content . In our case it is SharePoint 2013 App

3. Content Server – The web server that serves the content to be accessed by App. In our case it is SharePoint 2013 Server (Cloud or On-Premise)

4. Authentication Server – Trusted server that authenticates apps and creates oAuth tokens. In our case it is Azure ACS server or oAuth compatible authentication server

oAuth1

Let’s see what is happening in each step in the above picture.

Step 1 –> The user accesses the SharePoint 2013 portal and SharePoint 2013 authenticates the user using Claims Authentication

Step 2 –>  SharePoint 2013 requests for the Context Token for the user, from Windows Azure ACS (Access Control Services)

Step 3 –> ACS returns Context Token

Step 4 –> SharePoint 2013 passes the Context Token to the user

Step 5 –> User accesses App using Context Token

Step 6 –> Client App pulls Refresh Token from the Context Token and requests ACS for oAuthToken

Step 7 –> ACS server returns OAuth token to the client app

Step 8 –> Client App makes CSOM/REST calls to SharePoint site by passing OAuth Token

Step 9 –> SharePoint 2013 returns site content to App based on the App Permission Manifests

Step 10 –> Client App returns the App Content to the user

 Subscribe to my blog

SQL Azure Error: Tables without a clustered index are not supported in this version of SQL Server

I was trying to execute the following SQL Query in SQL Azure and got this error

Msg 40054, Level 16, State 1, Line 2

Tables without a clustered index are not supported in this version of SQL Server. Please create a clustered index and try again”

/****** Object:  Table [dbo].[Department]    Script Date: 2/18/2013 9:42:19 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Department](
    [DepartmentID] [int] IDENTITY(1,1) NOT NULL,
    [DepartmentName] [varchar](50) NOT NULL,
    [DepartmentCode] [char](5) NOT NULL,

 CONSTRAINT [PK_Department] PRIMARY KEY 
(
    [DepartmentId] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF)

)


GO
/****** Object:  Table [dbo].[Employee]    Script Date: 2/18/2013 9:42:19 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Employee](
    [EmpID] [int] IDENTITY(1,1) NOT NULL,
    [EmpName] [varchar](50) NOT NULL,
    [EmpAge] [int] NOT NULL,
    [EmpExperience] [decimal](5, 2) NULL,
 CONSTRAINT [PK_Employee] PRIMARY KEY
(
    [EmpID] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF)
)

I’ve learn that the SQL Azure does not support tables that are without clustered Index. The fix for this is to tweak the above SQL Query to included clustered index.

/****** Object:  Table [dbo].[Department]    Script Date: 2/18/2013 9:42:19 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Department](
    [DepartmentID] [int] IDENTITY(1,1) NOT NULL,
    [DepartmentName] [varchar](50) NOT NULL,
    [DepartmentCode] [char](5) NOT NULL,

 CONSTRAINT [PK_Department] PRIMARY KEY CLUSTERED 
(
    [DepartmentId] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF)

)


GO
/****** Object:  Table [dbo].[Employee]    Script Date: 2/18/2013 9:42:19 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Employee](
    [EmpID] [int] IDENTITY(1,1) NOT NULL,
    [EmpName] [varchar](50) NOT NULL,
    [EmpAge] [int] NOT NULL,
    [EmpExperience] [decimal](5, 2) NULL,
 CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED 
(
    [EmpID] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF)
)

The above changes highlighted in yellow has  solved my issue of “

Msg 40054, Level 16, State 1, Line 2

Tables without a clustered index are not supported in this version of SQL Server. Please create a clustered index and try again”"

 Subscribe to my blog

Moving a SQL Server 2008 Database from on-premise to SQL Azure

These days I’m doing lot of proof-of-concepts around the SharePoint 2013 App Model and how an ASP.NET MVC 3 application can be consumed/integrated in the SharePoint 2013 with the help of the App Model. As a first-step, I was trying to migrate a SQL 2008 database of an ASP.NET MVC3 application to Windows Azure. When it comes to migrating the database to Azure, there are multiple options available :-

a)Generate Insert SQL Scripts (compatible for SQL Azure) using ‘Tasks —> Generate Scripts’ in SQL Management Studio

b)Directly deploy the on-premise database to SQL Azure ‘Tasks’ —> Deploy Database to SQL Azure

c)Generate .dacpac export of SQL data + Schema and import it in SQL Azure

Since my database schema is not complex and data is also very less, I’m leveraging the 1’st approach.

Right-click on SQL database — > Tasks –> Generate Scripts

image

Select the list of tables to be migrated

image

Advanced Settings –>

Select ‘Sql Azure Database’ for Database Engine Type and ‘Schema and data’ for Types of data to script.

image

Select the location to save generated ‘.sql’file

image

You’ll see successful creation of .sql files

image

Log in to Windows Azure Management Portal and create new instance of SQL

image

Specify SQL Access Account

image

Select ‘Manage’ option for SQL Server and open a new Query window

image

Click ‘Run’. Now you are all set.

Microsoft MVP for year 2013

The New Year 2013 has started really well. I got the Microsoft Most Valuable Professional Award for SharePoint Server by Microsoft, for 5’th year. I’d like to thank Biplab & Tanmay from Microsoft for all the support and encouragement as a part of the MVP program.

clip_image001

Dear Sundararajan Narasiman,

Congratulations!

We are pleased to present you with the 2013 Microsoft® MVP Award! This award is given to exceptional technical community leaders who actively share their high quality, real world expertise with others. We appreciate your outstanding contributions in SharePoint Server technical communities during the past year.Also in this email:

· About your MVP Award Gift

· How to claim your award benefits

· Your MVP Identification Number

· MVP Award Program Code of Conduct

The Microsoft MVP Award provides us the unique opportunity to celebrate and honor your significant contributions and say “Thank you for your technical leadership.”

Mike Hickman

Director Community EngagementMicrosoft