SharePoint Item Level Permissions using PowerShell and CSOM

I generally try to avoid using item level permissions, but I had a specific scenario where these were needed. Had I been using an on-premise solution I would have built a timer job to manage the permissions, however as Office 365 was being used I decided to build a PowerShell script using the Client Side Object Model – the script is executed every night.

  • The script iterates through a couple of lists each night a performs a couple of actions:
    Checks for new items
  •  Based on the list item data, find the column ‘EmployeeId’ and query active directory to find the User Principle Name (UPN) (EmployeID is stored against each user in AD).
  • If the above action finds a user from AD the script removes all permissions on the list item and sets unique permissions so only the employee and a management group have access to that list item.

The following code snippets assume a connection to SharePoint is open (ClientContext), and the current web is loaded into context.

To find the SharePoint group the following was used:

# Load in list of groups on the current web.
$groups = $web.SiteGroups
$ctx.Load($groups)
$ctx.ExecuteQuery()

# Find the group called HR. Note - using GetGroupByName was not working, therefore I had to iterate through the groups.

foreach($group in $groups)
{

if($group.Title -eq "hr")
{
$hrGrp = $group.Id
}
}

# Get the group and load into context to be used.
$spGrp = $groups.GetById($hrGrp)
$ctx.Load($spGrp);
$ctx.ExecuteQuery();

To set item level permission on each list item:

# Get the list by Title and load.
$list = $web.Lists.GetByTitle("MyList")
$ctx.Load($list)
$ctx.ExecuteQuery()
$listTitle = $list.Title

# Simple query - purely used to ensure all data is returned.
$camlQuery = New-Object microsoft.SharePoint.Client.CamlQuery
$camlQuery.ViewXml = "10000"
	
# Load in the items.
$collListItem = $list.GetItems($camlQuery)
$ctx.Load($collListItem)
$ctx.ExecuteQuery()

# Iterate through each item.
foreach ($item in $collListItem)
{	
	# reset variable to ensure no false positives.
	$upn = $null
	$user = $null
	$roleAssignment = $null	
	$continue = $false

	# Set a couple of variable, get the user from AD based on employee number.
	$recordId = $item.Id
	$upn = Get-Upn -eid $item["EmployeeID"]
	$continue = $false

	if($upn -ne $null)
	{
		$continue = $true
	}
	else
	{
		Add-LogMessage "ERROR: Missing employee number on record '$recordId' "
	}	

	if($continue)
	{
		# Break inheritance on the list item and remove existing permissons.
		$item.BreakRoleInheritance($false, $true)

		# Get the permissions role for 'Read' and 'Edit'.
		$reader = $web.RoleDefinitions.GetByName("Read");
		$Editor = $web.RoleDefinitions.GetByName("Edit");
		
		# Create a role assignment and apply the 'read' role.
		$roleAssignment = New-Object microsoft.SharePoint.Client.RoleDefinitionBindingCollection($ctx)
		$roleAssignment.Add($reader)
		
		# Create a role assignment for editors - applying the 'edit' role.
		$roleAssignmentEditor = New-Object microsoft.SharePoint.Client.RoleDefinitionBindingCollection($ctx)
		$roleAssignmentEditor.Add($Editor)

		# Ensure the user exists on the site level, using EnsureUser.
		$user = $ctx.Site.RootWeb.EnsureUser($upn)
		$ctx.Load($user)

		# Apply the two permission roles to the list item.
		# $user is a SharePoint user.
		$ctx.Load($item.RoleAssignments.Add($user, $roleAssignment))    
		# spGrp is the HR group returned in the above snippet.
		$ctx.Load($item.RoleAssignments.Add($spGrp, $roleAssignmentEditor))

		# Update field on the list item to show it has ben processed.
		$item["A001"] = "PROCESSED"
		$item.Update()
	}

	# Execute changes.
	$ctx.ExecuteQuery();
}

Retention Policies in Office 365 

I had a requirement to configure a mechanism within Office 365 to allow pages on an intranet to expire after a set amount of time.  When a page has expired the content owner is alerted and a task is assigned to review the page content. 

A number of content types have been designed and each content type can have an information management policy assigned to indicate the retention period. 

To configure retention in Office 365: 

  1. Create a content type the usual way
  2. Connect to the site using SharePoint Designer and create a workflow.  Select the SharePoint 2010 workflow engine as this will allow you to connect the workflow to a content type.
  3. Modify the content type through SharePoint (Site Settings –> Content Types) and select ‘Workflow Settings’
  4. This should allow you to select the workflow created in step 2.

   addworkflow

Now the content type is ready, and the workflow is assigned to the content type – we now need to configure the retention policy:

  1. Select ‘Information Management Policies Settings’ from the content type page
  2. Configure a retention policy
  3. Configure the expiration policy
  4. Configure the action to start a workflow and select the workflow created earlier.

retention overview

retentiondetail

Note: After testing in Office 365, we cannot manually start the retention timer job to iterate through the expired items – the timer job runs every Sunday at around 08:30 (in my tenant at least) – makes testing quite difficult, so any testing should be completed in an on-prem environment so the timer job can be manually executed.