Renaming a file in SharePoint Online using PowerShell

Renaming a file in SharePoint Online using Powershell….

Wasn’t as simple as I initially thought, you need to make use of MoveTo …

# Call the SharePoint DLL
Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll" 

#ASSUMES CONNECTION TO SHAREPOINT ONLINE

# Get reference to the site / library
$web = Get-Web
$list = $web.Lists.GetByTitle('Documents')
$ctx.Load($list)
$ctx.ExecuteQuery()
$list.ItemCount

# Pull back 5 documents
$camlQuery = New-Object Microsoft.SharePoint.Client.CamlQuery
$camlQuery.ViewXml = "
      5
  "

$items = $list.GetItems($camlQuery)
$ctx.Load($items)
$ctx.ExecuteQuery()

# Foreach file...rename
foreach($file in $items)
{
	$file1 = $file.File
	$ctx.Load($file1)
	$ctx.ExecuteQuery()
		
	$docName = $file1.name
	$newDocName = [String]::Concat($docName, "-Test-")

	$filePath = "/Documents/" + $newDocName
	
	# Make use of MoveTo to rename the file...
	$file1.MoveTo($filePath, [Microsoft.SharePoint.Client.MoveOperations]::Overwrite)
	$ctx.ExecuteQuery()
}



Advertisements

Uploading large files to SharePoint Online

I’ve uploaded many files to SharePoint 2013 Online over the last couple of years, all these files have been pretty small.  These files have been uploaded via PowerShell scripts.

The other day I needed to upload a slightly larger file (more then 2MB) and got the following issue:

The request message is too large. The server does not allow messages that are larger than 2097152 bytes.

Previously I was uploading files in the following way:

function Add-FileToSpLib($file)
{
	$filePath = Get-ChildItem $file	
	$fileBytes = [System.IO.File]::ReadAllBytes($filePath.FullName)
	$fileinfo = New-Object Microsoft.SharePoint.Client.FileCreationInformation
	$fileinfo.Content = $fileBytes
	$fileinfo.Url = $library.RootFolder.ServerRelativeUrl + "/" + $filePath.Name
	$fileinfo.Overwrite = $true
	$newFile = $library.RootFolder.Files.Add($fileinfo)
	

	$ctx.Load($newFile)
	$ctx.ExecuteQuery()
}

The above function caused issues when the file was larger than 2MB.

Using the method below I was able to workaround this issue:

	$CurrentList = $ctx.Web.Lists.GetByTitle("My Library");
	$ctx.Load($CurrentList.RootFolder);
	$ctx.ExecuteQuery();


	$filePath = 'C:\Files\fileToUpload.txt'
	$stream  = New-Object IO.FileStream $filePath ,'Open'
	
	
	[Microsoft.SharePoint.Client.File]::SaveBinaryDirect($ctx, $CurrentList.RootFolder.ServerRelativeUrl.ToString() + “/” + $filePath.Split(‘\\’)[2], $stream, $true);
	$stream111.Close()

More info on the SaveBinaryDirect method:

https://msdn.microsoft.com/EN-US/library/office/microsoft.sharepoint.client.file.savebinarydirect.aspx

 

Adding a web part to a page using CSOM / PowerShell

The following script adds a web part to a page using PowerShell and the Client Side Object Model (SharePoint 2013).

Prior to adding a web part to the page you need to:

  • Get the XML of the web part –  the easiest way is to export the web part and view the XML in Notepad. The XML will need to be stored as a variable in your script.

The basic steps:

  • Open a connection to the site collection
  • Get the page
  • Check out
  • Create the web part definition – using the XML from your exported web part
  • Add the web part to the page
  • Check in the page
#### CLIENT CONTEXT INSTANTIATED - $CTX ####
# My previous code (not included) deals with connecting to my Site Collection hosted on Office 365 

# URL of page where we want to add the webpart
$serverRelativeUrl = '/sites/tDEV/pages/Contact-Us.aspx'
#  Get the page
$oFile = $ctx.get_web().getFileByServerRelativeUrl($serverRelativeUrl)
# Load and print out the Title of the page
$ctx.Load($oFile)
$ctx.ExecuteQuery()
$oFile.Title

# CheckOut the page
$oFile.CheckOut()
$ctx.ExecuteQuery()
# Get the webpart manager
$limitedWebPartManager = $oFile.getLimitedWebPartManager("Shared")
# Import the web part using the web part XML, $wpxml contains the XML from the exported web part
$wpd = $limitedWebPartManager.ImportWebPart($wpxml)
# Using the web part definition we can add the webpart to the page - in the header zone.
$limitedWebPartManager.AddWebPart($wpd.WebPart, "Header", "1")
# Check in the page
$oFile.CheckIn("test", "MajorCheckIn")
$ctx.ExecuteQuery()

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.

Office 365 authentication prompt when opening documents

In our environment we are using Office 365, we configured ADFS / ADFS Proxies / and Dirsync to manage SSO.

We have constructed smart links to ensure users are automatically signed into SharePoint when using their work laptop and Internet Explorer – if they are using a difference device / browser then they are directed to the corporate login page.

We experienced an issue where customers would be requested to sign into Word / Excel / PowerPoint when opening files directly from SharePoint.

This was resolved my modifying the smart links to contain 

LoginOptions%3D1

 

Example smart link:

https://sts.contoso.com/adfs/ls/?wa=wsignin1.0&wtrealm=urn:federation:MicrosoftOnline&wctx=MEST%3D0%26LoginOptions%3D1%26wa%3Dwsignin1.0%26rpsnv%3D2%26ver%3D6.1.6206.0%26wp%3DMCMBI%26wreply%3Dhttps:%252F%252Fportal.microsoftonline.com%252FDefault.aspx%26lc%3D1033%26id%3D271345

more info on smart links

http://community.office365.com/en-us/w/sso/using-smart-links-or-idp-initiated-authentication-with-office-365.aspx

Remove accounts from people search

Recently came across an issue where there were 3 “David Hendry’s” appearing within people search on SharePoint 2013 / Office 365.  So if a user searched for me, they would see 3 ‘identical’ people to choose from.

The accounts were:

* My main domain account
* My domain admin account
* My cloud admin account

The requirement was to hide cloud admin and domain admin accounts from the search results.

The resolution was to edit the People Search Core Results web part…

I modified the query (shown below) to include the domain name for internal accounts, remove any accounts containing adm_ and onmicrosoft.com.  

query

 

Error when using Query Builder

I had a minor issue when putting together a result source within my Search Service Application…

My issue was that the ‘Search Results Preview’ wasn’t working, in my case this was an easy fix.

Solution, Enable the ‘Search Server Web parts and Template’ on the Site Collection level within Central Admin.

Search feature for query builder
Search feature for query builder