Overview

As part of the new Client Side Object Model (CSOM) assemblies released in the SharePoint Online Client Components SDK, we now have many new capabilities and improvements. One specifically is the capability to programmatically provision One Drive for Business sites in Office 365

There is basically two ways to provision a users One Drive for Business site, the first way is user initiated that is when the user navigates to their newsfeed, site or One Drive links that are within the suite bar, the other is by pre-provisioning leveraging CSOM. In some cases it might not be feasible to have the user initiate the provision process. Say for example, you are migrating from an on-premises SharePoint farm or other repositories, and you don’t want to wait for the user to click a link before you start your migration. The AMS team is in the process of publishing this sample at https://officeams.codeplex.com/, but if you need it now and don’t want to wait here is a sample to get you going.

Use Cases

  • You have an custom on-boarding process and you want to create One Drive Sites when you on-board new employees
  • You are performing an on-premises migration to Office 365
  • You are migrating from the G word or other repositories

Dependencies Components 

SharePoint Online Client Redistributable

http://www.microsoft.com/en-us/download/details.aspx?id=42038)

References

Description

Version

Microsoft.Online.SharePoint.Client.Tenant

16.0.2617.1200

Microsoft.SharePoint.Client

16.0.2617.1200

Microsoft.SharePoint.Client.Runtime

16.0.2617.1200

Microsoft.SharePoint.Client.UserProfiles

16.0.2617.1200

Requirements

  • You must be a tenant administrator to provision One Drive for Business sites programmatically
  • Your code must have a context to your tenant admin URL. For example https://microsoftacs-admin.sharepoint.com

Code Samples

Rest API Implementation

public void CreatePersonalSiteUsingRest(string tenantAdminUrl , string userName, string password , string emailIDs)
{
//Validate Arguments
Uri _uri = new Uri(tenantAdminUrl);
string _emailData = emailIDs;
if (_creds == null)
{
_creds = new SharePointOnlineCredentials(userName, Utilities.StringToSecure(password));
string authCookie = _creds.GetAuthenticationCookie(_uri);
_cookies = new CookieContainer();
_cookies.Add(new Cookie("FedAuth", authCookie.TrimStart("SPOIDCRL=".ToCharArray()), "", _uri.Authority));

Uri _apiContextInfo = new Uri(_uri, API_CONTEXTINFO);

HttpWebRequest _endpointRequest = (HttpWebRequest)HttpWebRequest.Create(_apiContextInfo.OriginalString);
_endpointRequest.Method = "POST";
_endpointRequest.Accept = "text/xml;charset=utf-8";
_endpointRequest.CookieContainer = _cookies;
_endpointRequest.ContentLength = 0;
_endpointRequest.Credentials = _creds;

//We need to get the form digest since we are submitting or writing data via rest.
//We need to make a call to _api/contextinnfo to retreive the digestvalue and attach the form digest value to all
//requests. http://msdn.microsoft.com/en-us/library/office/jj164022(v=office.15).aspx

HttpWebResponse _endpointResponse = (HttpWebResponse)_endpointRequest.GetResponse();
XmlNamespaceManager _xmlnspm = new XmlNamespaceManager(new NameTable());
_xmlnspm.AddNamespace("d", "http://schemas.microsoft.com/ado/2007/08/dataservices");
StreamReader _contextinfoReader = new StreamReader(_endpointResponse.GetResponseStream(), System.Text.Encoding.UTF8);
var _formDigestXML = new XmlDocument();
_formDigestXML.LoadXml(_contextinfoReader.ReadToEnd());
var _formDigestNode = _formDigestXML.SelectSingleNode("//d:FormDigestValue", _xmlnspm);
_formDigest = _formDigestNode.InnerXml;
}

Byte[] _payLoad = System.Text.Encoding.ASCII.GetBytes(_emailData);
HttpWebRequest _spoMySiteRequest = (HttpWebRequest)HttpWebRequest.Create(new Uri(_uri, API_CREATEPERSONALSITE));
_spoMySiteRequest.Method = "POST";
_spoMySiteRequest.ContentType = "application/json;odata=verbose";
_spoMySiteRequest.Accept = "application/json;odata=verbose";
_spoMySiteRequest.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
_spoMySiteRequest.Headers.Add("X-RequestDigest", _formDigest);
_spoMySiteRequest.CookieContainer = _cookies;
_spoMySiteRequest.ContentLength = _emailData.Length;
_spoMySiteRequest.Credentials = _creds;

Stream _itemRequestStream = _spoMySiteRequest.GetRequestStream();
_itemRequestStream.Write(_payLoad, 0, _payLoad.Length);
_itemRequestStream.Close();

HttpWebResponse _mysiteCreateResponse = (HttpWebResponse)_spoMySiteRequest.GetResponse();

}

 

Invocation

PersonalSiteCreator _spoPersonalSiteCreator = new PersonalSiteCreator();
String _adminTenantUrl = "https://youradmin.sharepoint.com";
String _userName = "myAdmin@yourdomain.onmicrosoft.com";
String _passWord = "myAdminPassword";

//You dont want to provision more than 200 users personal sites during a single request. If you have a large amount of users consider
//waiting for the last users site to be provisioned. The reason behind this is not to bombard the service with requests.

// One Users
string _restPayload = "{'emailIDs':['user1@MicrosoftACS.onmicrosoft.com']}";

// Multiple Users
string _restPayload = "{'emailIDs':['user1@MicrosoftACS.onmicrosoft.com', 'user2@MicrosoftACS.onmicrosoft.com']}";
_spoPersonalSiteCreator.CreatePersonalSiteUsingRest(_adminTenantUrl, _userName, _passWord, _restPayload);

 

CSOM Implementation

/// <summary>
/// Sample Member that provisions personal sites leveraging CSOM
/// </summary>
/// <param name="tenantAdminUrl">The Tenanat Admin URL for your SharePoint Online Subscription</param>
/// <param name="userName">The User name, who has tenant admin permssions</param>
/// <param name="password">The password for the user who has tenant admin permissions</param>
/// <param name="emailIDs">The email ids for users who personal site you want to create</param>
public void CreatePersonalSiteUsingCSOM(string tenantAdminUrl, string userName, string password, string[] emailIDs)
{
using (ClientContext _context = new ClientContext(tenantAdminUrl))
{
try
{
SharePointOnlineCredentials _creds = new SharePointOnlineCredentials(userName, Utilities.StringToSecure(password));
_context.Credentials = _creds;
_context.ExecuteQuery();

ProfileLoader _profileLoader = ProfileLoader.GetProfileLoader(_context);
_profileLoader.CreatePersonalSiteEnqueueBulk(emailIDs);
_profileLoader.Context.ExecuteQuery();
}
catch(Exception _ex)
{
throw;
}
}
}

Invocation

PersonalSiteCreator _spoPersonalSiteCreator = new PersonalSiteCreator();
String _adminTenantUrl = "https://youradmin.sharepoint.com";
String _userName = "myAdmin@yourdomain.onmicrosoft.com";
String _passWord = "myAdminPassword";

//You dont want to provision more than 200 users personal sites during a single request. If you have a large amount of users consider
//waiting for the last users site to be provisioned. The reason behind this is not to bombard the service with requests.

//Create Personal Sites using CSOM
string[] _usersToCreate = { "User3@MicrosoftACS.onmicrosoft.com" , "User4@MicrosoftACS.onmicrosoft.com"};
_spoPersonalSiteCreator.CreatePersonalSiteUsingCSOM(_adminTenantUrl, _userName, _passWord, _usersToCreate);

 

During execution of the above code you might notice in the User Profiles a similar view, that the users personal site is displaying a SPSSITEERROR. This is expected during provisioning and will later be updated once the back-end processes have completed.

 


After this service has provisioned the site you will see a similar screen below which displays the users personal site.

 

 

FAQ

Q: Do we have an API to provision on-premises One Drive Sites?

I’m sorry at this time no.

Q: Should I use the Rest API or CSOM?

That depends, use what you are comfortable with however, our opinion is that CSOM is more intuitive in this scenario. 

Q: Can I batch more than 200 users at a time?

No, there is limit of 200 users per request.

Q: I have more than 200 users and I will need to submit multiple requests can I do that?

Sure you can, but you must batch the requests. Before you submit the next batch of users you should validate that the last user in your batch has had their One Drive site created. In the background all requests are queued up to the maximum 200.

Q: What happens, if we don’t listen to the above guidance?

You will receive an exception. We told you so.

Q: How long can does it take to provision a One Drive Site?

That depends we have seen it take up to 5 minutes per site during the day. During testing we provisioned 4,000 site collections and it took sometime to provision all the users. We are always improving the service.

Q - Can I use this code and material within any project I'm involved?

Feel free to use this on your engagements or solutions, but remember we told you no more than 200 users per requests

Q – Where can we provide feedback or we have other comments?

https://www.facebook.com/officeams or https://officeams.codeplex.com/

Q – These code snippets are nice, but can you point me to the Visual Studio Solution?

EDITED: The solution has been released and is available at http://officeams.codeplex.com/

Q – Do you have a PowerShell example?

Glad you asked. Here you go.

Open up SharePoint PowerShell console and run the following commands:

$webUrl = "https://yoursharepointadmin.SharePoint.com"
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($webUrl)

$web = $ctx.Web
$username = "admin@myadmin.sharepoint.com"
$password = read-host -AsSecureString

$ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username,$password )

$ctx.Load($web)
$ctx.ExecuteQuery()

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.UserProfiles")

$loader =[Microsoft.SharePoint.Client.UserProfiles.ProfileLoader]::GetProfileLoader($ctx)

#To Get Profile
$profile = $loader.GetUserProfile()
$ctx.Load($profile)
$ctx.ExecuteQuery()
$profile

#To enqueue Profile
$loader.CreatePersonalSiteEnqueueBulk(@("user1@domain.com"))
$loader.Context.ExecuteQuery()

Q – What if I submit a user who already has a One Drive Site?

We will process the request within the service and we will handle gracefully, however you should validate the user doesn’t have a site already to reduce the burden. See the AMS samples on how to determine if a user has One Drive site.

Q –   I want to add additional users or apply custom branding to the One Drive site how can I do that using this API?

The CreatePersonalSiteEnqueueBulk method only provisions the Site Collection, you can use the AMS sample Deploy branding to personal SkyDrive Pro sites automatically for possible options.

Special Thanks to

Kimmo Forss, Bob Fox, Vesa Juvonen, Rob Howard, Kirk Daues, Steve Walker, Ali Mazaheri, Bert Jansen, Jeremy Mazner, Phil Newman, Luca Bandinelli, Neil Hodgkinson, Bill Baer, Microsoft America Cloud Services & Office 365 CoE