U2U Blog

for developers and other creative minds

TextBox

An Office 365 App for Site Collection Provisioning in SharePoint Online

Continuing on my previous post, I decided to create a new Office 365 app that would be able to manage site collections in SharePoint Online.

When deciding to build an app that does site collection provisioning, you could chose to do this as a SharePoint App, but SharePoint Apps are typically contextual to the web they're hosted in. With the introduction of Office 365 Apps, which are contextual to the Office 365 tenant, provisioning site collections from an app seems more to be a task for an Office 365 App.

My new application should be capable of:

  • Getting an overview of available site collections
  • Creating a new site collection
  • Delete an existing site collection

My previous post proved that you can access the SharePoint Online sites by means of the Client Side Object Model (CSOM). My goal now is to prove that you can also do SharePoint online administration tasks from an Office 365 app, using also CSOM.

If you’re interested in the full source code, you can get it from GitHub. There you’ll also find how you can register the app in Azure Active Directory.

Accessing SharePoint Online Administration

As an extension to the SharePoint Client Side Object Model, you have the “Microsoft.Online.SharePoint.Client.Tenant.dll” library which gives you access to SharePoint Online Administration tasks. I.e. creating/deleting site collections, managing deleted site collections, … .

Now, would it then be possible to use the Azure application registration and authentication to i.e. manage site collections? Yes, it is possible!
In my previous post, I mentioned how you obtain the access token from Azure Active Directory. You can then get an accessToken for SharePoint Online administration as follows:

string clientID = ConfigurationManager.AppSettings["ida:ClientId"] ?? ConfigurationManager.AppSettings["ida:ClientID"];
string appKey = ConfigurationManager.AppSettings["ida:AppKey"] ?? ConfigurationManager.AppSettings["ida:Password"];
string spSiteUrl = "https://tenant-admin.sharepoint.com";

string accessToken = await GetAccessToken(clientID, appKey, spSiteUrl)

Take care that you replace tenant by your tenant name. Now that you have the accesToken, you can create the ClientContext as follows:

ClientContext clientContext = new ClientContext("https://tenant-admin.sharepoint.com");
clientContext.ExecutingWebRequest +=
    (sender, e) =>
    {
        e.WebRequestExecutor.WebRequest.Headers["Authorization"] = "Bearer " + accessToken;
    };

Getting an overview of all the site collections

Now that you have the ClientContext, you can start using the Tenant class to get an overview of the available site collections. You can do this as follows:

// Create tenant
Tenant tenant = new Tenant(ctx);

// Get the site properties
SPOSitePropertiesEnumerable allSiteProperties = tenant.GetSiteProperties(0, true);

// Load
ctx.Load(allSiteProperties);
await ctx.ExecuteQueryAsync();

The GetSiteProperties method currently only returns a maximum of 300 site collections. If you would like to get the next 300, increment the startIndex parameter to 300.

Creating a new site collection

In order to create a new site collection, you need to know the code of the webtemplate that should be applied to the new site collection. Now if you know the template code by hart, you can just pass it as a string, but better is to first check the available templates in SharePoint Online and chose the one you want to use. Because you have a ClientContext to your SharePoint Online Administration, you can check the web property for available web templates. You can do this as follows:

// Get the webtemplates
WebTemplateCollection webTemplates = ctx.Web.GetAvailableWebTemplates(lcid, false);

// Load
ctx.Load(webTemplates, templates => templates.Where(t => t.IsHidden == false));

// Execute
await ctx.ExecuteQueryAsync();

The GetAvailableWebTemplates method takes in the language code lcid. Here also, if you know the language code that is available by hart, you can just pass it in here. Another approach would be to first check which languages are available in SharePoint Online:

// Load
ctx.Load(ctx.Web, w => w.SupportedUILanguageIds);

// Execute query
await ctx.ExecuteQueryAsync();

Now that we have the ClientContext, Language and template; you can create a new site collection. The Tenant class provides a method called CreateSite that accepts a SiteCreationProperties object as input and creates a new site collection using the properties supplied in the SiteCreationProperties instance.

The snippet below illustrates how you can create a new site collection. In this snippet, you might observe that the CreateSite method returns an SpoOperation. Now, any operation that we can do on a site collection will return an SpoOperation. So when we create a new site collection, it will return an SpoOperation containing the information about the site collection creation operation. You can check the IsComplete property on the SpoOperation to check whether the operation has completed or not (reload the SpoOperation to check again), this is because the creation of a site collection happens asynchronously.

// Create tenant
Tenant tenant = new Tenant(ctx);

// Create properties
SiteCreationProperties siteCreationProperties =
    new SiteCreationProperties()
    {
        Url = "https://tenant.sharepoint.com/sites/createdbycode"
        ,
        Title = "Site created by code"
        ,
        Owner = "admin@tenant.onmicrosoft.com"
        ,
        Template = "STS#0"
        ,
        StorageMaximumLevel = 100
        ,
        UserCodeMaximumLevel = 50
    };

// Create the sitecollection
SpoOperation operation = tenant.CreateSite(siteCreationProperties);

// Execute query
ctx.ExecuteQuery();

Deleting an existing site collection

In order to delete an existing site collection, the basic steps are again the same. The difference is the method that will be called from the Tenant class, being RemoveSite. Do note that the site collection is not permanently deleted, but is pushed to the recycle bin in which it can still be restored. Note that the RemoveSite also returns an SpoOperation.

// Create tenant
Tenant tenant = new Tenant(ctx);

// Perform delete
SpoOperation spoOperation = tenant.RemoveSite("https://tenant.sharepoint.com/sites/createdbycode");

// Load and execute
ctx.Load(spoOperation);
await ctx.ExecuteQueryAsync();

App screenshots

Overview of sites:

05

Creating a new site:

06

Consuming SharePoint CSOM from an Office 365 app

I’ve been a C# developer for more than five years now. When SharePoint 2013 was released, I started doing development for SharePoint 2013 and later also SharePoint Online. My focus was on developing web services (in combination with the SharePoint App model), native client and mobile applications using the Client Side Object Model.

When I was at the Barcelona TechEd conference in 2014, I decided to follow quite some breakout sessions about Office 365 and the “new” Office 365 APIs. It was around that time also that they released the new Office 365 Apps look and feel by means of the new “App Launcher” and the “My Apps” page.

clip_image002

Without having the knowledge about Apps for Office 365, I initially thought that these would be quite comparable to Apps for SharePoint. However, Apps for Office 365 are completely different.

Apps for Office 365

An app for Office 365 is conceptually:

  • An application that is running externally (i.e. on some website) or standalone (i.e. mobile or desktop). The application can be accessible from within the office 365 website, but also could be surfaced from within Word or Outlook.
  • Integrating somehow with Office 365 (this is not required).
  • Registered in the Azure Active Directory (AAD) that is running behind your Office 365 tenant.
  • Can use the same authentication mechanism as Office 365, resulting in single sign-on.

So before you can use your app in Office 365, you have to register it in Azure Active Directory. When registering you app in Azure AD, you also configure the permissions it gets to access the Office 365 services like mail, contacts, events and OneDrive for Business, but also SharePoint! Then I started thinking: “Does this mean that you can register an app in Azure AD and let it access SharePoint online?”.

clip_image004

Getting the access token

I was eager to discover whether it was possible to access SharePoint using the Client Side Object Model and the Azure Authentication mechanism. Jeremy Thake briefly mentioned in a blog post that it is possible to use the token you get from Azure as the Bearer token in you ClientContext requests.
So I created an ASP.NET MVC5 web application, registered it in Azure AD, set the permissions for Office 365 SharePoint Online (see above) and started experimenting.

You can get the access token as follows:

/// <summary>
/// Get the access token for the required resource
/// </summary>
/// <param name="clientID">The client ID of your app</param>
/// <param name="appKey">The app key</param>
/// <param name="resource">The resource you would like access to</param>
/// <returns></returns>
public static async Task<string> GetAccessToken(String clientID, String appKey, String resource)
{
    // Redeem the authorization code from the response for an access token and refresh token.
    ClaimsPrincipal principal = ClaimsPrincipal.Current;

    String authorizationUri = "https://login.windows.net";
    String nameIdentifier = principal.FindFirst(ClaimTypes.NameIdentifier).Value;
    String tenantId = principal.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;

    AuthenticationContext authContext = new AuthenticationContext(
        string.Format("{0}/{1}", authorizationUri, tenantId),
        new ADALTokenCache(nameIdentifier)
    );

    try
    {
        // Get the object identifier
        String objectId = principal.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;

        // Authenticate
        AuthenticationResult result = await authContext.AcquireTokenSilentAsync(
            resource,
            new ClientCredential(clientID, appKey),
            new UserIdentifier(objectId, UserIdentifierType.UniqueId)
        );

        return result.AccessToken;
    }
    catch (AdalException exception)
    {
        //handle token acquisition failure
        if (exception.ErrorCode == AdalError.FailedToAcquireTokenSilently)
        {
            authContext.TokenCache.Clear();
        }
    }

    return null;
}

You obtain the values for the ClientID and AppKey from the application page in Azure AD. You typically store the values for these properties in the configuration file of your application.

clip_image006

Now what is the Resource you have to supply? Well the resource is as follows:

So in order to access a SharePoint, you can get the access token as follows:

String clientID = ConfigurationManager.AppSettings["ida:ClientId"] ?? ConfigurationManager.AppSettings["ida:ClientID"];
String appKey = ConfigurationManager.AppSettings["ida:AppKey"] ?? ConfigurationManager.AppSettings["ida:Password"];
String spSiteUrl = "https://tenant.sharepoint.com";

String accessToken = await GetAccessToken(clientID, appKey, spSiteUrl)

Accessing a SharePoint Online Site

In order to be able to use this token for your ClientContext, you need to set the Authorization Header on each webrequest the clientcontext sends out:

ClientContext clientContext = new ClientContext(spSiteUrl);
clientContext.ExecutingWebRequest +=
    (sender, e) =>
    {
        e.WebRequestExecutor.WebRequest.Headers["Authorization"] = "Bearer " + accessToken;
    };

You can now access this site through the clientcontext you created (taking into account the permissions you’ve set in Azure AD). In case you do not have the permissions to access something through the clientcontext, you’ll typically get a UnauthorizedaccessException.

Research Tracker application

Jeremy Thake often refers to a cool Office 365 project that combines different application types to access the same data in your SharePoint site. You can find these projects on GitHub.
One of these projects uses the SharePoint REST API to access a SharePoint site, create lists, manage list items. I cloned this project and extended it to also include the functionality by means of CSOM. You can also find this project on GitHub.

TextBox