I’ve been working on a client requirement to automate SharePoint library management via scripts to implement a document lifecycle with many document libraries that have custom content types and requires regular housekeeping for ownership and permissions.

Solution overview

To provide a seamless user experience, we decided to do the following:

  1. Create a document library template (.stp) with all the prerequisite folders and content types applied.
  2. Create a list to store the data about entries for said libraries. Add the owner and contributors for the library as columns in that list.
  3. Whenever the title, owners or contributors are changed, the destination document library will be updated.

Technical Implementation

The solution has two main elements to automate this process

  1. Microsoft Flow – Trigger when an item is created or modified
  2. Two Azure Functions – Create the library and update permissions

The broad steps and code are as follows

  1. When the flow is triggered, we would check the status field to find if it is a new entry or a change.

Note: Since Microsoft flow doesn’t have conditional triggers to differentiate between create and modified list item events, use a text column in the SharePoint list which is set to start, in progress and completed values to identify create and update events.

  1. The flow will call an Azure function via an HTTP Post action in a Function. Below is the configuration of this.
  2. For the “Create Library” Azure function, create a HTTP C# Function.
  3. In the Azure Function, open Properties -> App Service Editor. Then add a folder called bin and then copy two files to it.
    • Microsoft.SharePoint.Client
    • Microsoft.SharePoint.Client.Runtime

Please make sure to get the latest copy of the Nuget package for SharepointPnPOnlineCSOM. To do that, you can set up a VS solution and copy the files from there, or download the Nuget package directly and extract the files from it.

  1. After copying the files, reference them in the Azure function using the below code

#r “Microsoft.SharePoint.Client.dll”
#r “Microsoft.SharePoint.Client.Runtime.dll”
#r “System.Configuration”
#r “System.Web”

  1. Then create the SharePoint client context and create a connection to the source list.
  2. After that, use the ListCreationInformation class to create the Document library from the library template using the code below.


ListTemplateCollection templates = context.Site.GetCustomListTemplates(context.Web);
context.Load(templates);
context.ExecuteQuery();
log.Info("Templates Loaded");
// Initialize list or library creation info
var template = templates.First(listTemp => listTemp.Name == "<name of template>");
var listCreationInfo = new ListCreationInformation();
listCreationInfo.Title = LibName;
listCreationInfo.Description = LibName;
listCreationInfo.ListTemplate = template;
listCreationInfo.TemplateFeatureId = template.FeatureId;
listCreationInfo.TemplateType = template.ListTemplateTypeKind;
// Add Document Library to site
LibNew = context.Web.Lists.Add(listCreationInfo);
context.ExecuteQuery();

  1. After the library is created, break the role inheritance for the library as per the requirement
  2. Update the library permissions using the role assignment object
  3. To differentiate between People, SharePoint Groups and AD Groups, find the unique ID and add the group as per the script below.


log.Info("Inheriting permissions");
//Inherit permissions if broken
rListitem.ResetRoleInheritance();
transList.ResetRoleInheritance();
log.Info("Now let's start breaking the permissions again");
// Break permissions
rListitem.BreakRoleInheritance(false, false);
transList.BreakRoleInheritance(false, false);
RoleAssignmentCollection collRoleAssign = destList.RoleAssignments;
context.ExecuteQuery();
collRoleAssign.Add(author, new RoleDefinitionBindingCollection(context){web.RoleDefinitions.GetByType(RoleType.Contributor)});
collRoleAssign.Add(editor, new RoleDefinitionBindingCollection(context){web.RoleDefinitions.GetByType(RoleType.Contributor)});
applog.LogInformation("Update finised for created by and modified by");
try{
FieldUserValue[] fldContributors = (FieldUserValue[])rListitem["Contributors"];
if(fldContributors != null)
{
applog.LogInformation($"{fldContributors.Length} Contributors found. Applying updates");
for (int i = 0; i < fldContributors.Length; i++)
{
try
{
var userInfoList = context.Site.RootWeb.SiteUserInfoList;
var userInfo = userInfoList.GetItemById(fldContributors[i].LookupId);
context.Load(userInfo,info => info.ContentType);
context.ExecuteQuery();
string userType = "Person";
if(userInfo != null)
userType = userInfo.ContentType.Name;
if(userType == "Person" || userType == "DomainGroup")
{
var fldContributoruser = web.EnsureUser(fldContributors[i].LookupValue);
if(fldContributors[i].LookupValue.ToString() != authorUser.LookupValue.ToString() || fldContributors[i].LookupValue.ToString() != modifiedUser.LookupValue.ToString())
{
collRoleAssign.Add(fldContributoruser, new RoleDefinitionBindingCollection(context){web.RoleDefinitions.GetByType(RoleType.Contributor)});
}
try
{
Group visitorsGroup = grpCollection.GetByName("<Group Name>");
UserCollection collUser = visitorsGroup.Users;
collUser.AddUser(fldContributoruser);
context.Load(fldContributoruser);
context.Load(visitorsGroup);
context.ExecuteQuery();
}catch(Exception ex8){applog.LogError($"Error at Visitors group Add: {ex8.Message}");}
}
else if(userType == "SharePointGroup")
{
var primaryGroup = grpCollection.GetByName(fldContributors[i].LookupValue);
collRoleAssign.Add(primaryGroup, new RoleDefinitionBindingCollection(context){web.RoleDefinitions.GetByType(RoleType.Contributor)});
rListitem.RoleAssignments.Add(primaryGroup, new RoleDefinitionBindingCollection(context){web.RoleDefinitions.GetByType(RoleType.Contributor)});
}
}
catch(Exception ex3)
{applog.LogError($"Error at Contributor Creation: {ex3.Message}");}
}
applog.LogInformation("Contributors processing finished");

Note: In case you have people objects that are not in AD anymore because they have left the organisation, please refer to this blog for validating them before updating  Resolving “User not found” issue while assigning permissions using SharePoint CSOM

       Note: Try to avoid item.Update() from the Azure Function as that will trigger a second flow run, causing an iterative loop, instead use item.SystemUpdate()

  1. After the update is done, return to the Flow with the success value from the Azure Function which will complete the loop.

As shown above, we saw how we can automate document library creation from a template and permissions management using Flow and Azure Functions

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s