About Ian Hayse

SharePoint Admin. SharePoint Guy. I have been working with SharePoint nonstop for the past nine years. In those nine years, I've done everything from 'normal' Admin work, development, migrations, training, planning, search, workflow, InfoPath, to crazy fixing of the hands-off SharePoint databases. I'm located in Austin, TX and can always help out with small projects.

Connect to SharePoint Online Using PowerShell

Update and a much better way to approach this:
Use a SharePoint App Only Client Id and Secret to access the site, list, or library.

Microsoft documentation:
You can create an app principle that is limited to a single site, list, library, or a combination of them:

 $sampleConnect = Connect-PnPOnline -Url "https://YOURsite.sharepoint.com/sites/parent/child" -AppId "12345-94c3-4149-bda5-abcedffadsf" -AppSecret "643r4er5sfdadsfadsfdsf=" -ReturnConnection

Write-Host  $sampleConnect.Url
In this example, I’m connecting to a Site Collection on my tenant.

1) You have created a token in your o365 site
1.1) https://portal.office.com/account/
1.2) On the left site of the page click Security & privacy, then click Create and manage app passwords
1.3) In the app password page click the create button and give it a name.
1.4) Save the password to a secure location.
1.5) There is a better way of doing this that I will cover in a future post.
2) You have downloaded to CSOM DLL(s) from Nuget


$userName = "me@sharepointed.com"
$pw = "abc123taco"  # I"M USING AN APP PASSWORD 
$siteCollectionUrl = "https://sharepointed.sharepoint.com/sites/taco"

#Secure the password
$securePassword = ConvertTo-SecureString $pw -AsPlainText -Force

Add-Type -Path "C:\Code\DLL\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Code\DLL\Microsoft.SharePoint.Client.Runtime.dll"

#Create Context
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteCollectionUrl)

$ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($userName, $securePassword)

$web = $ctx.Web
$properties = $web.AllProperties

Write-Host " Site Collectione URL: $($web.Url)"
Write-Host "Properties are "

foreach ($prop in $properties) {

Use Flow to Update SharePoint Hyperlink Field

If you want to update a hyperlink field in SharePoint Online using Flow, you will need to use the Send an HTTP request to SharePoint action (for now). I first tried using the Update item Flow action but it would not correctly set the URL and Description values. One other issue I had was using the Post method as opposed to Patch. Every blog/forum post was suggesting the use of the Post method but the Patch method is what I ended up using.

For testing, create a list titled Contractors, in the list add a Hyperlink field titled website. In your Flow add a Send HTTP request to SharePoint action.

Site Address: select the site you created the Contractors list in.
Method: PATCH
Uri: _api/web/lists/getbytitle(‘Contractors’)/Items(‘ID From above Action’)
Accept application/json
Content-Type application/json; odata=verbose

'__metadata': { 'type': 'SP.Data.ContractorsListItem' },
          '__metadata': {'type':'SP.FieldUrlValue'},
               'Description':'Title from above Action',
                'Url': 'http://www.bbb.com'

Peek code:

    "inputs": {
        "host": {
            "connection": {
                "name": "@parameters('$connections')['shared_sharepointonline']['connectionId']"
        "method": "post",
        "body": {
            "method": "PATCH",
            "uri": "_api/web/lists/getbytitle('Contractors')/Items('@{triggerBody()?['ID']}')",
            "headers": {
                "Accept": "application/json",
                "Content-Type": "application/json; odata=verbose",
                "X-HTTP-TYPE": "MERGE",
                "IF-MATCH": "*"
            "body": "{\n'__metadata': { 'type': 'SP.Data.ContractorsListItem' },\n   'website':\n   {\n          '__metadata': {'type':'SP.FieldUrlValue'},\n               'Description':'@{triggerBody()?['Title']}',\n                'Url': 'http://www.bbb.com'\n  }\n}"
        "path": "/datasets/@{encodeURIComponent(encodeURIComponent('https://EnterYourURL.sharepoint.com/sites/spdev'))}/httprequest",
        "authentication": "@parameters('$authentication')"
    "metadata": {
        "flowSystemMetadata": {
            "swaggerOperationId": "HttpRequest"

Get 5 Most Recently Update Items From ALL Lists in a FARM

A wise man recently asked me how I’d go about retrieving the five most recent modified items from every list in a farm. Fun question and there are a couple of ways of going about this, but here is what I came up with.

Things to note: 1) on prem farm with more than one web app. 2) if you are dealing with a large farm, I’d suggest chunking out the Get-SPSite -limit all and the $site.allwebs into to smaller data sets. 3) script needs comments and error handling or reporting


if ($null -eq (Get-PSSnapin -Name Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue)) {
    Add-PsSnapin Microsoft.SharePoint.PowerShell

$sites = Get-SPWebApplication | Get-SPSite -limit all 

foreach ($site in $sites) {
    foreach ($web in $site.AllWebs) {
        $lists = $web.Lists
        foreach ($list in $lists) {
            $query = New-Object Microsoft.SharePoint.SPQuery 
            $query.Query =  
                  <FieldRef Name='Modified' Ascending='False' />
            $query.RowLimit = 5
            $listItems = $list.GetItems($query)
            if ($listItems) {
                foreach ($lIem in $listItems) {
                    Write-Host "Web: $web -- List: $list -- Item: $($lIem.Name) "
        if ($web) {

Using SharePoint Keyword Query to Search Across Site Collections

Quick and easy way to search for an item across site collections. I would suggest using one of the Keyword query tools to fine-tune your queries. Also note that SharePoint will limit your search results to 10,000 items, but you can page your results and cycle through them. In the example below, I’m searching across all the site collections off of the /sites/ managed path. With the returned dataset, I’m looping through the rows getting the SPFile of each row.

$site = New-Object Microsoft.SharePoint.SPSite "https://example.site.com"

$keywordQuery = New-Object Microsoft.office.Server.Search.Query.KeywordQuery $site

$queryText = "SomeField:Taco AND Path:https://example.site.com/sites/*"
$keywordQuery.QueryText = $queryText
$keywordQuery.TrimDuplicates = $false
$searchExec = New-Object Microsoft.Office.Server.Search.Query.SearchExecutor
$searchResults = $searchExec.ExecuteQuery($keywordQuery)

$dTable = $searchResults.Table[000].Table.Rows

foreach($row in $searchResults.Table[000].Table.Rows)
      $web = Get-SPWeb $row.SPWebUrl
      $file = $web.GetFile($row.Path)
      Write-Host $file.ServerRelativeUrl

Use Selenium to Upload a File to SharePoint

I created a Visual Studio console app.
Added the Selenium WebDriver and the Selenium Chome WebDriver Nuget packages

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;

namespace SeleniumUpload
    class Program
        static void Main(string[] args)
            //set span to 20 seconds
            System.TimeSpan span = new System.TimeSpan(0, 0, 0, 20, 0);
            //load chrome and the sharepoint library you want to upload to
            IWebDriver driver = new ChromeDriver();           
            driver.Url = "https://yoursite/yourlibrary/Forms/AllItems.aspx";

            //click the upload button

            //switch the driver focus to the file upload modal

            //allow some time for the modal to load 
            WebDriverWait wait = new WebDriverWait(driver, span);

            //get the upload field from the sharepoint modal
            IWebElement uploadElement = driver.FindElement(By.Id("ctl00_PlaceHolderMain_ctl02_ctl04_InputFile"));

            //use sendkeys to insert the path to the file you want to upload

            //click the ok button on the modal

PowerShell and CSOM Bulk Delete List Items

From my desktop, I wanted to delete all the items in a list. The list had 10,000+ items and loop through the items one at a time is slow and not efficient. This is where the batch process comes in and will help to quickly delete all the items in a large list.

In this example, I commented-out the SharePoint Online Credentials because I’m working with an on-prem environment.

The batch size is set to 1,000

#Load SharePoint CSOM Assemblies (you will need to grab these from a SharePoint server)
Add-Type -Path "C:\SharePoint DLL\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\SharePoint DLL\Microsoft.SharePoint.Client.Runtime.dll"

$SiteURL = "https://mylocalsite/sites/taco/"
$ListName = "GiantList"
$BatchSize = "1000"

Try {
    #$Cred= Get-Credential
    #$Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.Username, $Cred.Password)

    #Setup the context
    $Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
    #$Ctx.Credentials = $Credentials

    #Get the List
    $List = $Ctx.Web.Lists.GetByTitle($ListName)

    #Define Query to get List Items in the batch
    $Query = New-Object Microsoft.SharePoint.Client.CamlQuery
    $Query.ViewXml = @"



    #Get List Items in Batch
    Do {
        $ListItems = $List.GetItems($Query)

        if ($ListItems.Count -gt 0) {
            for ($i = $ListItems.Count - 1; $i -ge 0; $i--) {
        else {
            Write-Host "." -foregroundcolor black -backgroundcolor yellow
            $continue = $false;

        $Query.ListItemCollectionPosition = $ListItems.ListItemCollectionPosition
    While ($Query.ListItemCollectionPosition -ne $null)

Catch {
    write-host -f Red "Error Adding Items to List!" $_.Exception.Message

Limiting the number of rows returned in each query will help to avoid this error:

Exception calling "ExecuteQuery" with "0" argument(s): "The attempted operation is prohibited because it exceeds the list view threshold enforced by the administrator."

Some of the scripts were borrowed from:

I plan to revisit this and batch processing where you combine all the delete statements into a large XML string and pass it to SharePoint for processing.

Copy or Move Folder Structure in SharePoint Using CSOM

This post is intended for SharePoint Online and 2016+ using CSOM.

In previous version of SharePoint, moving a folder structure to a different library or a different library in another site collection required a good amount of code and effort. With the introduction of Microsoft.SharePoint.Client.MoveCopyUtil we now have the ability to use methods like MoveFolder to perform a cut and paste of a folder structure. There are other methods that are worth exploring like FolderCopy that is a copy and paste of a structure.

More methods can be found here: MoveCopyUtil members

Name Description
Public method Static member CopyFile
Public method Static member CopyFileByPath
Public method Static member CopyFolder
Public method Static member CopyFolderByPath
Public method Static member MoveFile
Public method Static member MoveFileByPath
Public method Static member MoveFolder
Public method Static member MoveFolderByPath

Example of moving a folder between site collections.

string dstUrl = "https://sharepoint.com/sites/B/";

using (ClientContext srcContext = new ClientContext(dstUrl))
  string sourceFolder = "https://sharepoint.com/sites/A/libraryname/foldername";
  string destFolder = dstUrl + "libraryname/foldername";

  MoveCopyUtil.MoveFolder(srcContext, sourceFolder, destFolder);

Again, this is using CSOM with the latest version of the NuGet package (16.0.4351.1000).

Move OneNote Notebooks to New SharePoint Library or Server

My company recently completed a SharePoint 2010 to 2016 migration. With the migration came the use of HTTPS security, so my OneNote notebooks stored in SharePoint would no longer sync. All of my notebooks displayed an error of Not syncing.

Here is how I fixed the issue:
First, make sure all of your notebooks are in SharePoint.
In OneNote, click on the File tab.
Locate the first notebook you want to update.
Next to the notebook name click Settings, then Properties.
In the Properties window click on Change Location…
Copy the URL of your SharePoint document library.
Paste the URL into the OneNote Chose a sync location… window.
Select the folder you want to sync OneNote with.
Click OK, a message box should appear saying the item is syncing.
Give it a minute and you should be set.

SharePoint SPFx PNP gulp trust-dev-cert Error

Trying to setup my dev environment to play around with SPFx and ran into this error:

[08:33:19] Error - [trust-cert] Error: root "Trusted Root Certification Authorities"
Signature matches Public Key
CertUtil: -addstore command FAILED: 0x80070005 (WIN32: 5 ERROR_ACCESS_DENIED)
CertUtil: Access is denied.

[08:33:19] Error – [trust-cert] Certificate trust failed with an unknown error.
[08:33:19] Error – ‘trust-cert’ sub task errored after 5.61 s
“Error trusting development certificate.”
[08:33:19] ‘trust-dev-cert’ errored after 5.62 s
[08:33:19] ==================[ Finished ]==================
Error – [trust-cert] Error: root “Trusted Root Certification Authorities”
Signature matches Public Key
CertUtil: -addstore command FAILED: 0x80070005 (WIN32: 5 ERROR_ACCESS_DENIED)
CertUtil: Access is denied.

Error – [trust-cert] Certificate trust failed with an unknown error.
Error – ‘trust-cert’ sub task errored after 5.61 s
“Error trusting development certificate.”

The only way I could manage to get around it was to manually create a cert using the OOB Windows tools.

Creating your .cer file

  1. Start by locating makecert.exe. You can typically find it in and C:\Program Files (x86)\Windows Kits\8.1\bin\x64 on Windows Server 2012 R2.
  2. Browse to that location with Command Prompt with Administrative privileges.
  3. You can actually create a certificate to run against your machine name and you can also have it work against localhost. To create a certificate for your machine name, use the following command:

makecert -n “CN=%ComputerName%, CN=localhost” -ss MY -sr LocalMachine -b 08/09/2016 -e 09/09/2028 -a sha256 -sky exchange -r -pe myCert.cer

Return to where you tried to run trust-dev-cert and move to the next step.  We now have a dev cert so there is no use in creating one using gulp.

Thanks to Santi Murtagh for the instructions on setting up the cert.

Creating a Self-Signed SSL Certificate for Developing and Testing Against IIS

The Web application at X could not be found.

Error: The Web application at https://sharepoint.sharepointed.com could not be found. Verify that you have typed the URL correctly. If the URL should be serving existing content, the system administrator may need to add a new request URL mapping to the intended application.

I created a .net console app to update some stuff in SharePoint.  When executing the .exe file with a new service account, I received the above error.

First I tried granting Shell access to the content db that I was working with but that didn’t solve the problem.

$cDb = Get-SPContentDatabase -site "https://taco.sharepointed.com/"
Add-SPShellAdmin -UserName "domain\userAccount -database $cDb

Running the same command without the database switch fixed my problem.

Add-SPShellAdmin -UserName "domain\userAccount"