Hide your documents from Delve


Delve is a powerful tool to see and discover information that is likely to be interesting to you. You only see content that you have access to. The powerful search behind Delve uses the Microsoft Graph.

You can access Delve from the Waffle inside your Office 365, or by going directly to the URL https://delve.office.com and signing in with your 365 account.

Now you or someone else will only see a document if they have access to it. However, there might be a reason that you don’t want it to show up in Delve at all. To prevent a document showing up in delve you can create a HideFromDelve column of the type Yes/No in the library the document is in. The column creates a new crawled property within search schema called ows_HideFromDelve. This is automatically mapped to the HideFromDelve managed property. Set the column value to Yes for the document you do not wish to see in Delve.

Set the default value to No otherwise every document you create within the library will not show up in Delve (Unless this is the desire outcome).

Advertisements

SharePoint Online site keeps refreshing on Microsoft Edge


A weird thing started happening on my Microsoft SharePoint site yesterday. Every time I hit a page/site the browser continuously refreshed over and over. I had no problems if I did this in Chrome, or even Internet Explorer, only in Microsoft Edge. Originally I thought it was a problem with the site, because if I went to a different tenant I didn’t encounter the same issue.

So, I then booted up another pc that had Microsoft Edge on it, and viewed my site expecting it to refresh over and over, but it didn’t. Therefore, it seemed to be a problem with my Edge explorer.

I first tried to clear all settings. Clicking on the 3 ellipses and then select Settings > Clear browsing data. I ticked everything to be cleared, and then clicked Clear. Unfortunately, this didn’t work for me, and made my Edge explorer crash on opening.

Extreme measures were required. I needed to complete reset Microsoft Edge. Now if you are following these steps, I must warn you, you might lose your favourites, history and settings. I recommend that you create a full backup or create a system restore point. Although I didn’t bother with a system restore or backup. My favourites were still there afterwards, I’m not sure if this is a syncing thing between my devices though, as I’ve just noticed that my favourites are the same on two machines.

Fixing Microsoft Edge

  • Close all open Microsoft Edge browsers.
  • Open file explorer and navigate to the following location (You will need to show hidden files):
    • C:\users\<YourUserName>\AppData\Local\Packages
  • The folder called Microsoft.MicrosoftEdge_8wekyb3d8bbwe first right click it and select Properties, and remove the check from the Read-Only option, click Apply and then OK
  • Now try and delete the folder. If you get Access Denied prompts, just select Continue. Any files you cannot delete just skip for now.
  • Go inside the Microsoft.MicrosoftEdge_8wekyb3d8bbwe folder and try deleting any remaining folders, you might be not able to delete the AC folder.
  • Restart your computer.
  • Now we are going to obtain the Microsoft Edge package and reregister it. Open Windows PowerShell by right clicking it
    and Run as administrator
  • Type the following to get to your user cd c:\Users\<YourUserName> press enter.
  • Now type
Get-AppXPackage -AllUsers -Name Microsoft.MicrosoftEdge | Foreach {Add-AppxPackage -DisableDevelopmentMode -Register "$($_.InstallLocation)\AppXManifest.xml" -Verbose}
  • If successfully you will not see any red message in the powershell window, just a yellow one saying operation complete for C:\Windows\SystemApps\Micorosft.MirosoftEdge.8wekyb3d8bbwe\AppXManifest.xml”
  • Reboot your machine on more time. Afterwards Microsoft Edge will be restored and I found that I didn’t have a refreshing issue with my SharePoint online site anymore.

Getting Fiddler to work with SharePoint Online


Something that I’ve always struggled with is getting Fiddler to provide me with REST API results for SharePoint online. These steps should help you.

Install and configure Fiddler

  1. First download and install Fiddler if you haven’t already. You can download fiddler at the following URL : https://www.telerik.com/download/fiddler Unfortunately you have to use an email address to be able to download it now.
  2. After first install of Fiddler, you will get an AppContainer Configuration dialog appear. Just click Cancel for now.
  3. Once Fiddler has opened, from the menu click Tools > Telerik Fiddler Options
  4. On the HTTPS tab ensure that Decrypt HTTPS traffic is selected
  5. Click OK. You will get a dialog asking if you should Trust the Fiddler Root certificate. It is fine to say Yes here.
  6. Say Yes to install the certificate.
  7. They really want to make sure, you are sure. Click Yes again.
  8. Fiddler’s root certificate has now been added to the Machine Root List.
  9. Close and Restart Fiddler.

Composing a REST URL for SharePoint Online.

First we need to log in to SharePoint online.

Now this is the point when I can never get it to work. I open IE/Edge, and instantly whenever I try to hit a https web page I get the following screen.

The trick here is to open Chrome and use that instead.

  1. Using Chrome, log in to your SharePoint online.
  2. In Fiddler, you should see in the left pane, it’s been capturing all your requests. Find a 200 result for your SharePoint site.
  3. In the right hand pane, take down the following information and store it in NotePad.
    1. FedAuth Cookie
    2. rtFA Cookie
  4. Above the right hand pane in Fiddler, one of the tabs is called Composer click on this tab.
  5. In the GET section put your REST API request E.g https://<YourOnlineName>.SharePoint.com/sites/Workflow/_api/web
  6. In the section below enter the following:
    Accept: application/json;odata=verbose
    Content-Type: application/json;odata=verbose
    Cookie:
    
  7. After cookie, you will want to put the rtFa=<rtFaCode>;FedAuth=<FedAuthCode>
  8. Click the Execute button at the top right of the screen. Once you have pressed it a call will be made and show up in the left hand pane. It’s easy to spot because the icon is for JSON.
  9. Double click on this entry and the right hand bottom pane, you will have your JSON response.

Performing Post Requests

There are a few more steps to complete to perform a post request, I’m going to walk you through the steps below. I already have a list called FiddlerList in my SharePoint online site. Currently it holds one item. (My site called Workflow as I was testing something with workflow before working on this post, this has nothing to do with the demo, hopefully it won’t confuse you)

  1. In Fiddler, from your previous composed GET request, first change the GET request to list all items currently in this list. My GET request is now https:// <YourOnlineName>.sharepoint.com/sites/Workflow/_api/web/Lists/getByTitle(‘FiddlerList’)/Items
  2. The results are show below. Take note of your type in the __metadata node. Mine is SP.Data.FiddlerListListItem
    you will need this later for adding an item. The type is normally SP.Data.<ListName>ListItem
  3. Now we need to create a POST request to the URI of /_api/contextinfo and then capture the FormDigestValue

  4. Now we have all the information required to create an item. Go back to the composed tab. Change the URI back to the way we had before to get the list items. https://<YourOnlineName&gt;.sharepoint.com/sites/Workflow/_api/web/Lists/getByTitle(‘FiddlerList’)/Items
  5. Add the X-RequestDigest: put in your FormDigestValue. Your page should look similar to below.
  6. Then in the bottom pane, the Request Body, put the following (Remember to change the type to match your list type).
    {'__metadata': {'type':'SP.Data.FiddlerListListItem'},
    'Title':'Created by Fiddler REST'
    };
    
  7. Click Execute
  8. If all has worked well you should get a 201 Create Response back, and see the item created in your list.

Update API Request

To perform an update to the list item, first we need to know what the Item ID is. At the end of the 201 create JSON response I can see that the Item I created has the ID of 2.

  1. In my composer I have changed my URI to https://<YourOnlineName>.sharepoint.com/sites/workflow/_api/web/Lists/getByTitle(‘FiddlerList’)/Items(2)
  2. In the top pane, I have set the content-length back to 0 and added IF-MATCH: * and X-HTTP-Method: PATCH this indicates that the post request is actually an update.
  3. Lastly in the bottom pane, Request Body, I have changed the Title.

    Note: Screen shot shows MERGE which is backwards compatible, should use newer command PATCH.
  4. After executing, I get a 204 response, and I can see the title has changed in my list.

Delete API Request

Lastly I’m going to delete my item.

  1. Change X-HTTP-Method to say DELETE instead of PATCH
  2. Remove the Request Body text.
  3. Click Execute, you will get a 200 response back.
  4. Your item has now been removed from the List.

The above should be enough to get you going. You can now use fiddler to test out your REST API calls.

Branding your Office 365 sign-in pages


Are you fed up when you go to sign into your SharePoint Online sites, that you see the default picture that Microsoft provides? Wouldn’t it be nice that the sign in page matched your branding, gave your users a consistence look and feel? Well this article is going to show you how to do that.

Note: A branded sign-in page only appears when you visit a service with a tenant-specific URL such as https://outlook.com/contoso.com. When you visit a service with non-tenant specific URLs (e.g https://myapps.microsoft.com) a non-branded sign-in page appears until you have entered your User ID.

The following screen shot shows an example of the Office 365 sign-in page on a desktop after a customisation:

The following screen shot shows an example of the Office 365 sign-in page on a mobile device after customisation:

What can you customise?

On the screenshot below I have highlighted the areas that you can customise.

  1. Large Image / Background colour – You can change the image, or show a background colour which will be used in place of the image on low bandwidth or narrow screens.
  2. Logo – Your logo can be shown at the top right of the screen instead of the Office 365 logo.
  3. Sign-in Page Text – Although not showing on the above picture, you can supply sign-in page text. This text could be used to display a legal statement, simple instructions, or even contact information for your help desk.

SharePoint Online Tenant

To customise your sign-in page, you need to do this through Azure AD. If you have just a SharePoint Online tenant of the domain *.onmicrosoft.com and never used Azure, you will find that you cannot get to Azure. Luckily this isn’t too much of a problem if you have a credit card. (Don’t worry it doesn’t cost any money).

If you go to https://manage.windowsazure.com or https://portal.azure.com and attempt to sign in with your account, that you use for SharePoint online, you will see the screen below.

Chris O’Brien blog explains it further here (http://www.sharepointnutsandbolts.com/2014/04/using-azure-instance-behind-your-office-365-tenant.html), but basically you just need to click on “Sign up for Windows Azure” then follow the instructions and enter your credit card details. (Again it doesn’t cost you anything). It gives you a pay-as-you-go Azure instance.

Configuring your directory with company branding

  • Sign into your Azure classic portal (https://manage.windowsazure.com) as an administrator of the directory you want to customise, and select your directory.
  • Along the menu/toolbar list, Click Configure.
  • Under Directory properties click Customize Branding.
  • Modify the elements listed below. All fields are optional. See below for screenshots and details of all customisable elements.
    • Banner Logo
    • Sign-in Page Text
    • Sign-in Page Illustration
    • Sign-In Page Background colour.
  • Click Save.

Note: If you have applied changes to your sign-in page, it can take up to an hour for the changes to appear. Mine happen within a few minutes.

Any time you wish to change your customisation, just by going back and clicking the Customize Branding button again. It is also here where you can add different branding settings for a specific language.

Different Branding for different languages

After you have configured your default branding settings, by going back and clicking the Customize Branding button, the first screen you are presented with is changing existing settings (which if you have just followed this blog, only see Default here) or you can add branding settings for a specific language. Select the language and then click the arrow button, and upload pictures/text as you did before. Once set, this branding will only show for the given browser language.

Customisable elements details

Below you will find the screen shot of the customising branding wizard with descriptions that you would find if you click the help tool tip icon.

  • Banner Logo (60 x 280 pixels) – The banner logo is displayed on the Azure AD sign-in page, when users sign in to cloud application that use this directory. It’s also used in the Access Panel service.
    • Max pixel size: 60px by 300px
    • Recommended to keep under 30 pixels high to avoid introducing scrollbars on mobile devices.
    • Recommended file size: 5-10kb
    • Use a PNG image with a transparent background if possible.
    • Avoid using a logo with small text on it, as the image may be resized to fit smaller screens.
  • Square Logo (240 X 240 pixels) – The square logo (previously referred to as “Title Logo”) is used to represent user accounts in your organization, on Azure AD web UI and in Windows 10.
    • Max pixel size: 240px by 240px
    • Recommended file size: 5-10kb
    • Use a PNG image with a transparent background if possible.
    • Avoid using a logo with small text on it, as the image may be resized to fit smaller screens.
  • Square Logo, Dark Theme (240 x 240 pixels) – If configured, this image will be used instead of the “Square Logo” image in combination with dark backgrounds, such as Windows 10 Azure AD Joined screens in the out-of-box experience.
    • If your logo already looks good on white and on dark blue/black backgrounds, there’s no need to configure a separate Dark Theme logo.
  • User ID Placeholder – This will replace “someone@example.com” that’s shown as a hint in the user ID input field on the Azure AD login page.
    • Important: you should only configure this if you only support internal users. If you expect external users to sign in to your app(s), we recommend you leave this blank (Azure AD will show “someone@example.com”).
  • Sign-In Page Text Heading – Add a heading above your customized sign-in page text. If not configured, this space is left blank on Azure AD web login pages, and replaced by “Need help” on Azure AD Join experience on Windows 10.
    • Plain text only.
    • Don’t exceed 30 characters.
  • Sign-In Page Text – This text appears at the bottom of the Azure AD sign in page, on the web, in apps and in the Azure AD Join experience on Windows 10. Use this space to convey instructions, terms of use and help tips to your users.
    • Plain text only.
    • Can’t be longer than 500 characters (250-300 characters recommended).
    • Remember, anyone can see your login page so you shouldn’t use this space to convey sensitive info!

  • Sign-In Page Illustration – This large image is displayed on the side of the Azure AD sign in page. By design, this image is scaled and cropped to fill in the available space in the browser window.
    • PNG, JPEG or GIF
    • 1420×1200 resolution recommended.
    • Recommended file size: 300 kb (max file size 500 kb).
    • Use an abstract illustration or picture. Since the image gets resized and cropped, avoid using rasterized text and keep the “interesting” part of the illustration in the top-left corner.
  • Sign-In Page Background Colour – On high latency connections, the sign-in page illustration may not load, in which case the login page will fill in the space with a solid colour.
    • Enter an RGB colour code in hex format (e.g. #FFFFFF).
  • Hide KMSI (Keep Me Signed In) – Choose whether your users can see the “Keep me signed in” check box on the Azure AD sign-in page. This option has no impact on session lifetime, and only allows users to remain signed in when they close and reopen their browser.
    • Important: some features of SharePoint Online and Office 2010 have a dependency on users being able to check this box. If you hide this option, users may get additional and unexpected sign in prompts.
  • Post Logout Link Label – If this is configured, Azure AD will show a link to a web site of your choice, after users sign out of Azure AD web applications.
    • Make sure to configure both the label and URL properties!
    • Link can be plain text only.
    • URL can be HTTP or HTTPS.
  • Post Logout Link URL – If this is configured, Azure AD will show a link to a web site of your choice, after users sign out of Azure AD web applications.
    • Make sure to configure both the label and URL properties!
    • Link can be plain text only.
    • URL can be HTTP or HTTPS.

References: https://azure.microsoft.com/en-gb/documentation/articles/active-directory-add-company-branding/

SharePoint online new Site Content Page. How does it know to render classic view? (Cookies!)


If you are lucky enough today, you might have noticed the new Site Contents page. It appears if your SharePoint online version is 16.0.532.1205 or above. You can find out which version you are running by putting the following in the URL:

https://<tenant>.sharepoint.com/_vti_pvt/buildversion.cnf

This new page is similar to the new Document library pages Microsoft have implemented. The new pages also have lost the ability to run scripts on the page.

In the bottom left of the page, there is a link that states “Return to classic SharePoint” when this is clicked, you return back to the Site Contents we all know and love. (Love might be a bit strong).

However, after I clicked the link, I couldn’t see any way of turning it back to the new Site Content look and feel. How does SharePoint know to display all Site Content pages in classic mode?

It turns out there is a session cookie called splnu set to the value of 0. When this is set in the cookies it displays the old look. Just by closing the page down and re-opening it removes this cookie and then displays the new look and feel again. You can prove that it is this cookie by using chrome tools.

Open the site in Chrome. Switch to the classic mode by clicking the link in the left hand bottom corner of the screen. If you refresh the page, you stay in classic mode. Open the developer tools (F12), and swtich to Resources and expand the Cookies section.

Click on spInu and click delete, then refresh the page. The page reverts back to the new look and feel.

Updating an expired Client Secret of SharePoint Add-in


Been working with SharePoint Add-in tokens for a while now, but this week has been the first time I’m still working with an add-in longer than a year in one environment. My application start throwing the error message:

Error:Token request failed., InnerMessage:System.Net.WebException: The remote server returned an error: (401) Unauthorized.”

I knew that they did expire after a year, but never really thought about (until now) how to go about renewing them. Luckily Microsoft documentation nowadays is a lot better than it used to be. I found this walk through https://msdn.microsoft.com/en-us/library/office/dn726681.aspx. In case Microsoft takes the link down, or changes the URL, I will explain the steps below. I have also changed the code slightly for the Report on Client ID expiry dates, as the Microsoft one didn’t return the results I expected.

Report on Client ID expiry dates.

  • Open Windows Powershell and run the following cmdlet:
Connect-MsolService
  • A login prompt will appear, here enter the tenant-administrator credentials for the Office 365 tenancy where the add-in was registered with AppRegNew.aspx
  • You can generate a report that list each add-in in the tenant with the date that the secret expires with the following PowerShelll code.

    $applist = Get-MsolServicePrincipal -all  |Where-Object -FilterScript { ($_.DisplayName -notlike &quot;*Microsoft*&quot;) -and ($_.DisplayName -notlike &quot;autohost*&quot;) -and  ($_.ServicePrincipalNames -notlike &quot;*localhost*&quot;) }
    $output = &quot; &quot;
    foreach ($appentry in $applist)
    {
        $principalId = $appentry.AppPrincipalId
        $principalName = $appentry.DisplayName
    
        $results =  Get-MsolServicePrincipalCredential -AppPrincipalId $principalId -ReturnKeyValues $false | Where-Object { ($_.Type -ne &quot;Other&quot;) -and ($_.Type -ne &quot;Asymmetric&quot;) }
        if($results.count -gt 0)
        {
          $output += &quot;PrincipalId`t:`t$principalId`n&quot;
          $output += &quot;PrincipalName`t:`t$principalName`n&quot;
          $output += &quot;Keys`n&quot;
         foreach($result in $results)
         {
            $output += &quot;Type`t:`t&quot; + $result.Type + &quot;`n&quot;
            $output += &quot;Value`t:`t&quot; + $result.Value + &quot;`n&quot;
            $output += &quot;KeyId`t:`t&quot; + $result.KeyId + &quot;`n&quot;
            $output += &quot;StartDate`t:`t &quot; + $result.StartDate + &quot;`n&quot;
            $output += &quot;EndDate`t:`t&quot; + $result.EndDate + &quot;`n&quot;
            $output += &quot;Usage`t:`t&quot; + $result.Usage+ &quot;`n&quot;
            $output += &quot;`n&quot;
         }
         $output += &quot;-----------------------------------------------`n&quot;
        }
    }
    $output | Out-File &quot;c:\temp\appsec.txt&quot; 

     

    • The above code first filters out Microsoft’s own applications, add-ins still under development (and a now-deprecated type of add-in that was called autohosted).
    • Filters out non-SharePoint add-ins and add-in like workflow.
  • Open the file at c:\temp\appsec.text to see the report.

An example of the report below:

Note: The PrincipalID is your Client ID

Generate a new secret for another year.

To generate a new secret, you just need to run the following PowerShell script:

$clientId = &lt;#Replace with your ClientID of the add-in#&gt;
$bytes = New-Object Byte[] 32
$rand = [System.Security.Cryptography.RandomNumberGenerator]::Create()
$rand.GetBytes($bytes)
$rand.Dispose()
$newClientSecret = [System.Convert]::ToBase64String($bytes)
New-MsolServicePrincipalCredential -AppPrincipalId $clientId -Type Symmetric -Usage Sign -Value $newClientSecret
New-MsolServicePrincipalCredential -AppPrincipalId $clientId -Type Symmetric -Usage Verify -Value $newClientSecret
New-MsolServicePrincipalCredential -AppPrincipalId $clientId -Type Password -Usage Verify -Value $newClientSecret
$newClientSecret

The output of the above PowerShell file will give you a new Client Secret, take note of this:

Generate a new secret for 3 years.

It is possible to create a secret that will last 3 years, the PowerShell script is very similar to the above script, but now it has a start and end date.

$clientId = &lt;#Replace with your ClientID of the add-in#&gt;
$bytes = New-Object Byte[] 32
$rand = [System.Security.Cryptography.RandomNumberGenerator]::Create()
$rand.GetBytes($bytes)
$rand.Dispose()
$newClientSecret = [System.Convert]::ToBase64String($bytes)
$dtStart = [System.DateTime]::Now
$dtEnd = $dtStart.AddYears(3)
New-MsolServicePrincipalCredential -AppPrincipalId $clientId -Type Symmetric -Usage Sign -Value $newClientSecret -StartDate $dtStart -EndDate $dtEnd
New-MsolServicePrincipalCredential -AppPrincipalId $clientId -Type Symmetric -Usage Verify -Value $newClientSecret -StartDate $dtStart -EndDate $dtEnd
New-MsolServicePrincipalCredential -AppPrincipalId $clientId -Type Password -Usage Verify -Value $newClientSecret -StartDate $dtStart -EndDate $dtEnd
$newClientSecret

Updating the remote web application using the new secret

  • If you are redeploying from Visual Studio you will need to update the client Secret in your Web.Config
  • If you are just updating directly in Azure, you can just go to the configurations and update the new secret there.

If you are using the default TokenHelper.cs file in your project, and it’s not the prerelease version then you can add a second app setting called SecondaryClientSecret. Here you would put in your old secret, and in the ClientSecret put in the new one. This is so if your token is going to expire soon, the application will still work as it will try one first then the other.

Time to propagate Client Secret to SharePoint

According the Microsoft link you should wait at least 24 hours to propagate the ClientSecret. However, I found as soon as I changed the secret I could use it straight away. After changing your Client Secret if you run the ‘Report on Client ID expiry dates’ powershell again, those dates didn’t update for me until the following day.

I ran the report the following day, and as you can see below, Demo App 1 which was shown in the screen shot above now has 3 new keys with new dates.

Deleting expired client secrets

As expired client secrets do not seem to get removed, it is recommended to delete them. The code below will grab any keys that are expired for a ClientID and delete them.

$clientId = &lt;#Replace with your ClientID of the add-in#&gt;
$keys = Get-MsolServicePrincipalCredential -AppPrincipalId $clientId -ReturnKeyValues $false
$dtNow = [System.DateTime]::Now
foreach($key in $keys)
{
 if($key.EndDate -lt  $dtNow)
 {
   write-host $key.KeyId &quot; Expired&quot;
   Remove-MsolServicePrincipalCredential -KeyIds @($key.KeyId) -AppPrincipalId $clientId
 }
}

Adding to Property Bag and Index using PowerShell in SharePoint Online


The other day, I very lazily, used SharePoint Designer to add a few items to the property bag. Soon as I done it, someone then said they wanted to search upon the values inside the property bag. Unfortunately this isn’t something that can be done via SharePoint Designer. After spending 5 minutes or so searching online for a pre-made PowerShell code, I could only find the solution for On-Prem.

The only piece of code I found to do this to SharePoint Online was through the Office Developer PNP CSOM code. I really didn’t want to create a Visual Studio project just to use the PNP Core. (See http://dev.office.com/patterns-and-practices for more information Office Dev PNP, there is so much information and cool videos there, there really is no need to re-hash anything in my own blog.) So I decided to just create PowerShell script which is pretty close to being like for like copy of the PNP Core method:

Web.AddIndexedPropertyBagKey(this Web web, string key)

Before I go into the code, I will explain how SharePoint knows which property bag items need to be indexed for search. Just by adding the item doesn’t make it visible to search, it has to be added to another property bag item called “vti_indexedpropertykeys“. The value of this property bag item is a pipe delimited Base64String. (Example: RABhAHUAZwBoAHQAZQByAA==|UwBvAG4A|cwBvAG4A| ). As you can see, it’s not just as simple as adding the string name to the vti_indexedpropertykeys value.

To encode a single value in PowerShell the following line of code works:

[System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($value))

To decode a single value from Base64String to text the following PowerShell code works:

[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($EncodedValue))

The below code will ask you for the Web Url, your username, password (Secure string), propertybag key name and value. The code will first add or update the value in the property bag, and if it hasn’t been added to the index it will add it.

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")| Out-Null
$indexPropertyKeyConst = "vti_indexedpropertykeys"

function GetIndexedPropertyBagKeys($ctx)
{
  $results = @()
  $web = $ctx.Web;
  $ctx.Load($web.AllProperties)

  try
  {
  $ctx.ExecuteQuery();
  }
  catch{
   Write-host "Error accessing property bag " $_.Exception.Message -ForegroundColor Red
   exit 1
  }

  $indexPropertyBase64 = $web.AllProperties[$indexPropertyKeyConst];
  $separator = "|"
  $option = [System.StringSplitOptions]::RemoveEmptyEntries

  if(![string]::IsNullOrEmpty($indexPropertyBase64))
  {
    $resultsBase64 = $indexPropertyBase64.Split($separator, $option)

    foreach($r in $resultsBase64)
    {
     $results += [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($r))
    }
  }
    #comma not a mistake, required to ensure correct type is returned on empty or single array value.
    return ,$results
}

function GetEncodedValueForSearchIndexProperty($keysArray)
{
  $encode64Keys = [String]::Empty
  foreach($key in $keysArray)
  {
   $encode64Keys += [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($key))
   $encode64Keys += "|"
  }

  return $encode64Keys;
}

function AddIndexedPropertyBagKey($ctx, $propertyKey)
{
  [bool]$result = $false;
  [bool]$addValue = $false;
  $keys = GetIndexedPropertyBagKeys($ctx)

  if($keys -cnotcontains $propertyKey)
  {
     $addValue = $true;
  }

 if($addValue)
 {
   try
   {
   $keys += $propertyKey
   $keysBase64String = GetEncodedValueForSearchIndexProperty($keys)
   $ctx.Web.AllProperties[$indexPropertyKeyConst] = $keysBase64String
   $ctx.Web.Update()
   $ctx.ExecuteQuery()
   $result = $true
   }
   catch
   {
    Write-host "Error adding $propertyKey to index. " $_.Exception.Message -ForegroundColor Red
    exit 1
   }
 }

  return $result
}

function CreateUpdatePropertyBag($ctx, $propertyKey, $propValue)
{
  $web = $ctx.Web;
  $ctx.Load($web.AllProperties)
  try
  {
    $ctx.ExecuteQuery();
    $web.AllProperties[$propertyKey] = $propValue;
    $web.Update();
    $ctx.ExecuteQuery();
  }
  catch{
   Write-host "Error adding $propertyKey to property bag " $_.Exception.Message -ForegroundColor Red
   exit 1
  }
}

$webUrl = Read-Host -Prompt "Enter the WebUrl"
$username = Read-Host -Prompt "Enter your Email login"
$password = Read-Host -Prompt "Password for $username" -AsSecureString
$propKeyToIndex = Read-Host -Prompt "Enter the property key name to index"
$propValue = Read-Host -Prompt "Enter the $propKeyToIndex value"

$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($webUrl)
$ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $password)

CreateUpdatePropertyBag $ctx $propKeyToIndex $propValue;
AddIndexedPropertyBagKey $ctx $propKeyToIndex;

Write-Host "Complete" -ForegroundColor Green

You can download the source code directly from my OneDrive.

Thank you to my colleague Paul Perry with his help around PowerShell arrays.