Setting up a O365 Dev Tenant – Part 4 – Upload User Photos to SharePoint


Introduction

In this series of posts, I will explain how you can set up a Development O365 Tenant quickly. Using PowerShell scripts, at the end of this process, you will have:

  • A O365 Development Tenant with 25 “DEVELOPERPACK” Licenses.
  • 25 Users assigned with license
  • A total of 274 Users added to the Tenant
    • Set up for multiple offices
    • Organisational structured
  • All users will be MFA enabled
  • All users will have photos added to their accounts
  • Enabling Office 365 Auditing
  • Setting up the SharePoint Tenant Settings and enabling Public CDN for SPFX

Unfortunately, I have found it impossible to do the following via PowerShell scripts, and these would need to be done manually. I haven’t included this information within these blog post.

  • Create a Tenant App Catalog
  • Set Organisation Details
  • Set Login Branding
  • Set Tenant Branding

In my previous post, I showed you how to import user photos into exchange. As we could only add 25 user pictures due to licensing constraints, this post will show you how to upload all the pictures into SharePoint.

Obtaining the code

I have stored my code on GitHub at the following URL for cloning. https://github.com/pmatthews05/SetupDevTenant.git

Running Set-UserPhotosInSharePoint.ps1

Before running this code, I wish to give a shout out to Christopher Walker, who had a PowerShell function for resizing images in a Gist. Thank you. https://gist.github.com/someshinyobject/617bf00556bc43af87cd

This script uses the AzureADUser.csv file and the UserImages profile pictures. It will:

  • Loop through the CSV
  • Create 3 different sized images for the user
  • Upload the 3 images to Root MySite.

To run the script, you need to install PNP PowerShell. Follow the instruction here how to install if you encounter issues. https://docs.microsoft.com/en-us/powershell/sharepoint/sharepoint-pnp/sharepoint-pnp-cmdlets?view=sharepoint-ps

Basically, if you haven’t done this previously, run PowerShell as Administrator. Then type:

Install-Module SharePointPnPPowerShellOnline -SkipPublisherCheck -AllowClobber

You will need to connect first to your root MySite, before running the script. It allows you to control the signing in, for example if you didn’t make your account MFA, then you don’t need to use the command UseWebLogin and just sign in with your credentials.

Connect-PnPOnline -url:https://[tenant]-my.sharepoint.com -useweblogin

Now you are connected, you can call the Set-UserPhotosInSharePoint.ps1 file.

.\Set-UserPhotosInSharePoint.ps1 -Path:'.\data\AzureADUsers.csv' -TenantDomain:'[tenant].onmicrosoft.com' 

Replace [tenant] with your tenant name.

This script is idempotent, so you can run it again at any point if there was an issue.

Once the script is complete, if you go to the following URL you will find pictures imported into the library at the root of the mysites.

https://[tenant]-my.sharepoint.com/User%20Photos

When the code is running, you might have noticed, in the UserImages folder, there is a new subfolder called Resize. This is where all the images are copied and resized to before being uploaded to SharePoint.

Running Set-UserProfilePhotosInSharePoint.ps1

At this point, all you really have done is uploaded pictures to SharePoint, they are not tied to the user at all. The following script will update the SharePoint user profile to point to the correct image.

Firstly, before you can run this script, we need to ensure that SharePoint online has already found your users and added them to the user profile.

  • Navigate to https://[tenant]-admin.sharepoint.com
  • Click the link to the Classic SharePoint admin centre which can be found in the left-hand navigation
  • Click User Profiles from the left-hand navigation
  • Here you should be able to see the number of User Profiles registered.

As you can see from my screen shot above my SharePoint has only registered 3 user profile so far. As we have no control over this, I will need to wait until SharePoint Online has done is syncing. This can take up to 24 hours.

After checking back later I can now see that I have user profiles for all users within Azure AD.

This code sets the UserProfile property PictureURL to [tenant]-my.sharepoint.com/user photos/profile pictures/[firstname_lastname_tenant]onmicrosoft_com_LThumb.jpg and sets the UserProfile property SPS-PicturePlaceholderState
to 0. By setting the value to 0, it indicate that SharePoint online should show the uploaded picture for the user.

First you need to connect to the admin site.

Connect-PnPOnline -url:https://[tenant]-admin.sharepoint.com -useweblogin

Now you are connected, you can call the Set-UserProfilePhotosInSharePoint.ps1 file.

.\Set-UserProfilePhotosInSharePoint.ps1 -Path:'.\data\AzureADUsers.csv' -TenantDomain:'[tenant].onmicrosoft.com' 

Replace [tenant] with your tenant name.

Once the script has completed, and search has picked up your changes, you will find your people showing up in Search / Delve etc.

In this blog post we have imported user photos into SharePoint mysites, and updated the user profiles for these users. In the next post I will be showing you how to turn on 365 Auditing, and assigning only a couple of users to view these audit logs.

Setting up a O365 Dev Tenant – Part 3 – Set User Photos in Exchange Online


Introduction

In this series of posts, I will explain how you can set up a Development O365 Tenant quickly. Using PowerShell scripts, at the end of this process, you will have:

  • A O365 Development Tenant with 25 “DEVELOPERPACK” Licenses.
  • 25 Users assigned with license
  • A total of 274 Users added to the Tenant
    • Set up for multiple offices
    • Organisational structured
  • All users will be MFA enabled
  • All users will have photos added to their accounts
  • Enabling Office 365 Auditing
  • Setting up the SharePoint Tenant Settings and enabling Public CDN for SPFX

Unfortunately, I have found it impossible to do the following via PowerShell scripts, and these would need to be done manually. I haven’t included this information within these blog post.

  • Create a Tenant App Catalog
  • Set Organisation Details
  • Set Login Branding
  • Set Tenant Branding

In my previous post, I walked you through creating users in AzureAD from a CSV file, set them up with MFA and a default password. Assigned the first 25 users a license, and uploaded their pictures into Azure AD

In this post I will be running you through the PowerShell script to import Pictures into Exchange. You might ask why we are doing this if we have previously uploaded the pictures into Azure AD, the reason is because your profile picture will sometimes show up in some places and not in others. E.g. Delve might show a picture, where SharePoint doesn’t. So, the next few posts are about uploading pictures to the tenant. These scripts upload to every possible location that have a separate place.

Obtaining the code

I have stored my code on GitHub at the following URL for cloning. https://github.com/pmatthews05/SetupDevTenant.git

Running Set-UserPhotosInExchange.ps1

This script uses the AzureADUser.csv file and the UserImages profile pictures. It will:

  • Find the user in Exchange
  • Check the photo exists in the script location for the user.
  • Uploads the picture.

To run the script, you need to use the Microsoft Exchange Online PowerShell Module. Please follow my previous blog about how to do this correctly. https://cann0nf0dder.wordpress.com/2019/04/14/unable-to-download-the-exchange-online-powershell-module-deployment-and-application-do-not-have-matching-security-zones/

Open Microsoft Exchange Online PowerShell Module. You will need to connect first, before running the script. It allows you to control the signing in.



Connect-EXOPSSession -userPrincipalName [user.name]@[tenant].onmicrosoft.com


Now you are connected, you can call the Set-UserPhotosInExchange.ps1 file.



.\Set-UserPhotosInExchange.ps1 -Path:'.\data\AzureADUsers.csv' -TenantDomain:'[mytenant].onmicrosoft.com' 


Replace [mytenant] with your tenant name.

This script is idempotent, so you can run it again at any point if there was an issue. NOTE: It will only work for the first 25 users that were assigned a license, as the others will not have an email account. (You can cancel the script after the first 25 user – Ctrl+ C)

The only part of this code that does the uploading is on line 46. Its coverts the jpg to bytes and then uploads it for the user.



Set-UserPhoto -Identity $UserCSV.UserPrincipalName -PictureData ([System.IO.File]::ReadAllBytes($pathtoPicture)) -Confirm:$false


Photos appearing in Delve

You will notice now that if you use Delve, the first 25 users in your CSV file will have pictures showing for them.

In this blog post we have imported user photos into exchange. As we could only add 25 user pictures in the next blog post I will be importing the pictures into SharePoint, so they can be used there.

Setting up a O365 Dev Tenant – Part 2 – Create Users from CSV file


Introduction

In this series of posts, I will explain how you can set up a Development O365 Tenant quickly. Using PowerShell scripts, at the end of this process, you will have:

  • A O365 Development Tenant with 25 “DEVELOPERPACK” Licenses.
  • 25 Users assigned with license
  • A total of 274 Users added to the Tenant
    • Set up for multiple offices
    • Organisational structured
  • All users will be MFA enabled
  • All users will have photos added to their accounts
  • Enabling Office 365 Auditing
  • Setting up the SharePoint Tenant Settings and enabling Public CDN for SPFX

Unfortunately, I have found it impossible to do the following via PowerShell scripts, and these would need to be done manually. I haven’t included this information within these blog post.

  • Create a Tenant App Catalog
  • Set Organisation Details
  • Set Login Branding
  • Set Tenant Branding

In my previous post, I walked you through the step of joining the Office 365 Development program, and set yourself up with a Office 365 Development Tenant with 25 Developerpack Licenses. In this post we are going to add users to the tenant from a CSV file.

Obtaining the code

I have stored my code on GitHub at the following URL for cloning. https://github.com/pmatthews05/SetupDevTenant.git

Folders

  • Data – Contains the CSV files used within the scripts
  • Settings – Contains the JSON settings for the Set-SharePointTenant.ps1 script.
  • UserImages – Contains all the photos to all the users being added to the tenant.

Scripts

  • Set-Office365Auditing.ps1 – Turns on the Audit logs, and assign people to view them.
  • Set-SharePointTenant.ps1 – Update Tenant Parameters and enabling Public CDN
  • Set-UserAccountsOnline.ps1 – Add users and assign licenses and import pictures
  • Set-UserPhotosInExchange.ps1 – Add User photos to Exchange
  • Set-UserPhotosInSharePoint.ps1 – Add user photos to SharePoint.
  • Set-UserProfilePhotosInSharePoint.ps1 – Assigns the user picture to their SharePoint profile.

Creating the users from a CSV file.

In the Data folder there is a csv called AzureADUsers.csv. It is best to open this file in Excel.

Before you run any code, you will need to fix up a couple of columns in lines 2 & 3 of the csv file.

  • On line 2, replace UserPrincipleName, GivenName, Surname, MailNickName (Columns B, C, E, F, G) with the account details you gave yourself when creating the Subscription in part 1.
  • One line 3, add your name to the manager of Dan Jump.
  • Save the CSV file.
  • (optional) Put your profile picture in the UserImages folder, with the filename [firstname lastname].jpg

All the names/address/telephone numbers are all made up, converted by me. The names might seem familiar to you, as they are the “Microsoft Users” Microsoft uses in their demos.

We are going to use the powershell script Set-UserAcountsOnline.ps1. Note: None of my scripts will connect you to a service. This will need to be done first before running.

I’m not going to go through the code as the script is quite easy to follow and to understand. If there is anything you are unsure about that is in the script, please leave a comment at the bottom of the post.

Running Set-UserAccountsOnline.ps1

This script uses the AzureADUser.csv file and the UserImages profile pictures. It will:

  • Create/Update all users in Azure AD.
  • Assign them a license (If any are left)
  • If new user assigns them a password, to be changed at next login
  • Enable their account for Multi Factor Authentication. (This can be disabled in the CSV file, by changing the MFAEnabled to false for the user)
  • Upload their picture to Azure AD

To run the script your PowerShell environment will need to have the following modules installed.

  • AzureAD
  • MSOnline

If you haven’t done this previously, run Powershell as Administrator. Then type:

Install-Module -Name AzureAD
Install-Module -Name MSOnline

Unfortunately, the only way I could enable MFA for users was to use the MSOnline module. There doesn’t seem to be a way of doing it purely using AzureAD.

As stated previously, you will need to connect first, before running the script. It allows you to control the signing in.

Connect-AzureAD
Connect-MsolService

Now you are connected, you can call the Set-UserAccountOnline.ps1 file.

.\Set-UserAccountsOnline.ps1 -Path:'.\data\AzureADUsers.csv' -tenantDomain:'[mytenant].onmicrosoft.com' -tempPassword:'[Give A Password]'

Replace [mytenant] with your tenant name. You can either put a password in -tempPassword parameter, or you can remove this parameter. By removing this parameter, you will be setting all your users to have the password P@55w0rd.

This script is idempotent, so you can run it again at any point if there was an issue. When you run out of licenses, the script continues creating the accounts, they just don’t get a license assigned to them.

MFA Setup

After the script has run, and you go to your tenant, you will be asked to re-authenticate again, and provide details for MFA.

  • Click Next
  • I recommend using the Mobile App, and Receive notifications for verification. Click Set up
  • Follow the instructions to install the Microsoft authenticator app for your mobile.
  • Once set up, you need to provide a mobile number. Fill this in and click Next.
  • On the next page, you will be given an app password. Take note of this somewhere. These are useful if you need to run a script on this account, but want to skip MFA, you can just put this password in instead. Click Finished.

View Users in Admin portal

In the Admin portal https://admin.microsoft.com under users, you will see the users have been imported. Some with licenses and some without.

  • Any user you select, will give you their details and picture.

In this blog post we have created users from a CSV file, set them up with MFA and a default password. Assigned the first 25 users a license. In the next blog post I will be showing you the PowerShell script to import Pictures into Exchange.

Setting up a O365 Dev Tenant – Part 1 – Getting the Tenant


Introduction

In this series of posts, I will explain how you can set up a Development O365 Tenant quickly. Using PowerShell scripts, at the end of this process, you will have:

  • A O365 Development Tenant with 25 “DEVELOPERPACK” Licenses.
  • 25 Users assigned with license
  • A total of 274 Users added to the Tenant
    • Set up for multiple offices
    • Organisational structured
  • All users will be MFA enabled
  • All users will have photos added to their accounts
  • Enabling Office 365 Auditing
  • Setting up the SharePoint Tenant Settings and enabling Public CDN for SPFX

Unfortunately, I have found it impossible to do the following via PowerShell scripts, and these would need to be done manually. I haven’t included this information within these blog post.

  • Create a Tenant App Catalog
  • Set Organisation Details
  • Set Login Branding
  • Set Tenant Branding

Join Office 365 Development Program.

Microsoft allows anyone with a Microsoft account to get a development Office 365 Subscription by joining the Office 365 developer program.

https://developer.microsoft.com/en-us/office/dev-program

  • Click Join Now
  • Sign in with your Microsoft Account. (Hotmail, live, outlook etc)
  • Select your Country/Region and enter your Company name. Tick the terms and conditions and then click Next
  • Fill out the next page and then click JOIN.

Get an Office 365 Subscription

After you have joined the Development program, you will be present with a screen to allow you to set up a subscription.

  • Click Set Up Subscription
  • Fill out the form and click Continue:
    • Country/Region
    • Create UserName
    • Create Domain
    • Password
    • ConfirmPassword
  • Add your phone number on the next screen, then click Set-up
  • Once created, you can then click Go to Subscription
  • After you have signed in the link takes you to https://www.office.com.

  • Click on Admin takes you to the admin centre. Expanding … Show all in the left hand navigation and then expanding Admin Centers you can see you have a full E3 license tenant.

Assign yourself a license.

This is going to be a manual step, and can skip this if you wish, as my next blog will show you how to do this via code.

  • Expand Users, and select Active Users. You will see one user in the tenant. You. Currently this account doesn’t have a license. Select the user.
  • The panel that appears on the right, click the link Edit for Product licenses.
  • Select the Location and turn On the Office 365 E3 Developer Licenses. Click Save.
  • Your user now has a production assigned to them. Click Close.

How long is my Office 365 Subscription valid for?

You may have noticed when you created your subscription it states there are 91/92 days left. Microsoft use to give their members a subscription for 1 year. They have now reduced this to 90 days, however, there is a new clause that states:

As a program member, you can get a free Office 365 developer subscription with 25 user licenses to use to build your solutions. This subscription will remain active for as long as you’re actively developing and deploying solutions.’

I’m not sure how often you need to be deploying and developing on the subscription to keep it open. All the FAQ are found in the link below.

https://docs.microsoft.com/en-us/office/developer-program/office-365-developer-program-faq

In this blog post we have set up an O365 Development Tenant with 25 “DEVELOPERPACK” Licenses and assigned one license to ourselves. In the next blog post I will be showing you the PowerShell scripts I have written to help quickly set up my tenant.

 

Access denied when attempting to move SharePoint documents


The other day at work, I had to help solve a problem for a client. They were unable to move documents from one folder to another, using the modern move functionality within SharePoint Libraries.

Note: To be able to move a document, you need to have minimum of contribute permission on both the source and destination locations. You need to be able to add a new document to the destination and delete at the source.

The client I was helping was trying to move a document from inside one folder to another folder within the same document library. The client had contribute permission. I had checked their permission at the library, folder and document level, just in case there was broken inheritance. Nothing had broken inheritance. When the client attempted to move the library, they encountered the error message:

Access denied. You do not have permissions to perform this action or access this resource.

This had me stumped for a bit. I was able to move the documents, but I was Site Collection administrator. It was only when I was looking at the views on the list, I noticed a column called Approval Status. This was unusual, as we have a default setup for all the libraries within SharePoint and approval isn’t one of them.

After further investigation, it turned out that Content Approval was turned on within versioning settings of the library.

The client didn’t have the ‘Approve Items – Approve a minor version of a list item or document’ permission in this library. It was agreed that I could turn the Content Approval status off as it shouldn’t been turned on in the first place. Soon as this was turned off, the client was able to move documents from one folder to another successfully. I didn’t test it, but I would assume that if I added the client to a group that had the Approve Items permission, they would have also been able to move the documents with Content Approval status turned on.

Reference: https://mydigitalworkplace.wordpress.com/2018/05/20/10-things-about-copy-and-move-sharepoint-online/

Programmatically change the New Menu in SharePoint Online using PowerShell


Back in October 2018 Microsoft changed the way the New menu on a document library worked. You can now edit what is shown in the New menu directly from the library.

I needed to know how to do this in code, and it looked like PNP automatically does this for you. After doing a get-pnpprovisioningtemplate, when you look at the list you will see a XML element called NewDocumentTemplates, which has a JSON formatted string. The property NewDocumentTemplates exists on the view of the list. This made me think that you could have different menu items for different views, but this doesn’t seem to be the case.

<NewDocumentTemplates>[
{"templateId":"NewFolder","title":"Folder","visible":true},
{"contentTypeId":"0x0101007ADED896313A4943AFE7F07133B1339E","isContentType":true,"templateId":"0x0101007ADED896313A4943AFE7F07133B1339E","title":"Document","visible":false},
{"contentTypeId":"0x0101009348E1CE5767914598D327EAE7EB97D500245281855DF00844BF9BD64ADD983B7D","isContentType":true,"templateId":"0x0101009348E1CE5767914598D327EAE7EB97D500245281855DF00844BF9BD64ADD983B7D","title":"CF New Request","visible":true},
{"templateId":"NewDOC","title":"Word document","visible":true},
{"templateId":"NewXSL","title":"Excel workbook","visible":true},
{"templateId":"NewPPT","title":"PowerPoint presentation","visible":true},
{"templateId":"NewONE","title":"OneNote notebook","visible":true},
{"templateId":"NewXSLForm","title":"Forms for Excel","visible":true},
{"templateId":"Link","title":"Link","visible":true}]
</NewDocumentTemplates>

Now this seems to work, when applying it back to the same list. However, I found that if I set my own content types with visible to false, and then tried on a different list that did have that content type, it didn’t hide it.

So why not?

The reason is because the contentTypeId it is expecting, is the ListContentTypeId, not the actual ContentTypeId. For each list, the ListContentTypeId will be different.

When you add a site content type to a list:

“SharePoint list makes a local copy of the site content type and adds the copy to the content type collection on the list. The new list content type is a child of the site content type. The value of the Id property for the list content type is different from the value of the Id property for its parent site content type, but otherwise the two content types are initially the same.”

https://docs.microsoft.com/en-us/previous-versions/office/developer/sharepoint-2010/ms463016(v%3Doffice.14)#effects-from-adding-a-site-content-type-to-a-list

I’m sure PNP will pick up on this and update the code at some point.

PowerShell script

I decided to write my own PowerShell script to solve this issue. First, I needed to understand the JSON format. For the built in Microsoft menu items the JSON looks as follow:

{
  "title": "Word Document",
  "templateId": "NewDOC",
  "visible": true
}

There are quite a few built in ones, and from using PNP to export out, I was able to find what they were called. (If you find out any more, that I don’t have listed here please let me know.)

  • TemplateId – Name
  • NewFolder – Folder
  • NewDOC – Word Document
  • NewXSL – Excel workbook
  • NewPPT – PowerPoint presentation
  • NewONE – OneNote notebook
  • NewVSDX – Visio drawing
  • NewXSLForm – Forms for Excel
  • Link – Link

Note: If you don’t have a license for Visio, or do not have Forms enabled for your E3 license, then the menu item will not show up anyway.

The JSON format for content types are the following:

{
  "title": "<Content Type Name>",
  "templateId": "<ListContentTypeID>",
  "visible": <true/false>,
  "contentTypeId": "<ListContentTypeID>",
  "isContentType": <true/false>
}

Below is my function script. Set-ListNewMenuItems.ps1 With this script you can hide any content types/default menu items by using the titles.

-Url:’https://tenant.sharepoint.com/sites/demo&#8217; -ListTitle:’Documents’ -ContentTypesToHide:’OneNote notebook’,’PowerPoint’,’Custom CT Name’

You can also hide all default menu items, and let your content types show. It will still show links and folder.

-Url:’https://tenant.sharepoint.com/sites/demo&#8217; -ListTitle:’Documents’ -HideDefault:$true

If you want to hide link and folder too, you would need to include their names in the ContentTypesToHide.

-Url:’https://tenant.sharepoint.com/sites/demo&#8217; -ListTitle:’Documents’ -ContentTypesToHide:’Folder’,’Link’ -HideDefault:$true

After running my script to remove Document, OneNote notebook, Word Document and Forms For Excel

-Url:’https://tenant.sharepoint.com/sites/demo&#8217; -ListTitle:’Documents’ -ContentTypesToHide:’Document’,’OneNote notebook’,’Word Document’,’Forms For Excel’ -HideDefault:$false

As you can see from above, it states that it is Showing Visio drawing. However, on my tenant I do not have a license for it. Below shows you how my New menu has been updated.

The code isn’t perfect, and could do with improvements such as ordering. Please feel free to use it.

Externally Sharing – SPFX IsExternallyShared Field Customizer


This is my last post of the Externally Sharing series.

This post will show you how I have created a SPFX field customizer, where if it is displaying in the view, it will perform a call to the GetSharingInformation, then iterate through the links -> Invitations and principals results looking for external users. If it finds an external user, it displays a sharing link for the item, or it doesn’t.

 

ExternalShare2

You can find my Git Hub project at the following URL: https://github.com/pmatthews05/spfx-IsExternallyShared

The field customizer is a REACT SPFX project. As the control onInit() it grabs the list id from the pageContext.


@override
  public onInit(): Promise {
    // Add your custom initialization to this method.  The framework will wait
    // for the returned promise to resolve before firing any BaseFieldCustomizer events.
    Log.info(LOG_SOURCE, 'Activated IsExternallySharedFieldCustomizer with properties:');
    Log.info(LOG_SOURCE, `The following string should be equal: "IsExternallySharedFieldCustomizer" and "${strings.Title}"`);
    this.listId = this.context.pageContext.list.id;
    return Promise.resolve();
  }

As the control onRenderCell event fires, I grab the listitem’s ID. Then pass the ListID and the ID of the item to my .tsx control.

@override
  public onRenderCell(event: IFieldCustomizerCellEventParameters): void {
    const id = event.listItem.getValueByName('ID');
    Log.info(LOG_SOURCE, `Loaded onRenderCell: ID: ${id}`);
    const isExternallyShared: React.ReactElement =
      React.createElement(IsExternallyShared,
        {
          id: id,
          listId: this.listId,
          context: this.context
        } as IIsExternallySharedProps);
    ReactDOM.render(isExternallyShared, event.domElement);
  }

The ExternallyShared.tsx file sets its externalShared state to 0. There are 3 values for externalShared.

0 – Loading

1 – Shared

2- Not Shared

The ExternallyShared.tsx file calls the GetSharingInformation/permissionsInformation in a POST REST API call. From the results I get back (assuming successful), I loop through the links, and principals looking for external users. As soon as I find 1 external user I return my results back. The code here isn’t perfect. I should really create an Interface for the returning json results.

export default class IsExternallyShared extends React.Component {
  constructor(props: IIsExternallySharedProps, state: IIsExternallySharedState) {
    super(props, state);
    this.state = {
      externallyShared: 0
    };
  }
  @override
  public componentDidMount(): void {
    Log.info(LOG_SOURCE, 'React Element: IsExternallyShared mounted');
    this._getIfExternallyShared()
      .then(isExternallyShared => {
        this.setState({
          externallyShared: isExternallyShared
        });
      })
      .catch(error => {
        Log.error(LOG_SOURCE, error);
      });
  }
  @override
  public componentWillUnmount(): void {
    Log.info(LOG_SOURCE, 'React Element: IsExternallyShared unmounted');
  }

 @override
 public render():React.ReactElement {
 return (

 

);
}
privateasync_getIfExternallyShared():Promise {
 var response = await this.props.context.spHttpClient.post(`${this.props.context.pageContext.web.absoluteUrl}/_api/web/Lists('${this.props.listId}')/GetItemById('${this.props.id}')/GetSharingInformation/permissionsInformation`, SPHttpClient.configurations.v1, null);
if (response.status!=200) {
 return 0;
}
  var jsonReponse = await response.json();
    let returnValue = 2;
    for (let link of jsonReponse.links) {
      if (link.linkDetails.HasExternalGuestInvitees) {
        returnValue = 1;
        break;
      }
    }

    if(returnValue == 2)
    {
      for(let principal of jsonReponse.principals)
      {
        if(principal.principal.isExternal){
          returnValue =1;
          break;
        }
      }
    }

    return returnValue;
  }

As you can see in the render() method, I have created my own ExternallySharedIcon control. Passing in the externallyShared value.

  

The ExternallySharedIcon.tsx control just displays an 64based image depending on the value.

  1. – Shows the spinning loading icon
  2. – Shows the sharing icon
export default class ExternallySharedIcon extends React.Component{
    private SHARED_ICON: string = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAG00AABtNAWneuvsAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTZEaa/1AAACkUlEQVRoQ9XaO2gVQRTG8ev7VSiKASGi2PgANYJaxE5IYqdiZavYWAk2IgoWgp1YaaxEG0HUSiy1sooGQnyAoDYqgiGg+ADf/08YGMLJvTP7mNn94FeES3ZPcmdnz8xupyEZwCVM4At+4j3u4QiWoJFZhZv428M7HECjsgGvYRVs+YMzaESW4yWsQns5huy5Cqu4EF+xDtmik/+CVVyoy8iWU7CKijGNecgSTY1WUbF2IEuewiooVrZp9RmsgmIdQtLMxWFo/FoFxRpEsuzFY1iFFPEdSdqLbbgPq4gybqPWrMU1/IZVQBk65k5EZQH6sOL/T7NHn1/AN1gnr8JFBEUFn8U41N66A3zELezDHCiLcAJT8E9WtTuYj65RUcfxGdZBfA+h5iqmq3T0OzpPyLT6A+fR8+6r4tVnWAepiobXObhZRMPzKB5hZo/0AVewEUE5Df8AVbuL9Zgty7AFahP64YZoUDZDX5V14rKeYwi15gask5fxCSehYVJrFkMLBauIIjRXX8caJMluWIUU8QRJ+xTlIKxiYr2CmrbkqfIPyLJS2gWroCJ0596DpKn6ItYejma1ZBexolnDKqYMN40uRO2p80b2AsOoPa1uJZSUzdxSKBpevZq5TYiK2lyNX/9glgdQO63p0/q8mzfQedQnWZ/7tB4Jaqf9rIa1oNGiRQuaEbivWP/FxixorMQuKauckmcKXlKWiS5ALerLbuJa1CjqxpskW1HHtoqGUtJoY2sMVjFFJNvY8qMLvrVbi36q2p1OvrnrMgmroFjZttdb/4Cj9Y+Y9JDPv7MXkfUhnzIKq7AQ2R+zKq1/0K3oVYOYLrZRrxq4rIRe9lBxVtHOW+xHY7Md3V630aaDkU7nH6/PvLG2FyRSAAAAAElFTkSuQmCC";
    private LOADING_ICON: string = "data:image/gif;base64,R0lGODlhIAAgAJEDAMbvwFTOQ37acf///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJCgADACwAAAAAIAAgAAACUpyPqcvtD6Oc1AAhALq5HhEEAgKKnlGOX6h6nHa850y7GCzJS0rxO9sDLnS5W+2ITCopxEiTJJT4FNNIdWOcPJdcWyeW9VSvk3G00tx21+y2owAAIfkECQoAAwAsAAAAACAAIAAAAk6cj6nL7Q+jnNQAIUB9IgSxOd0XNldWpup6alPLjOAkx94s1Qv8Yu4KDAqHJh+Fp9DlbjYSjbkz9lDEagk5wFKUA+4WavBOf1mp9YxOOwoAIfkECQoAAwAsAAAAACAAIAAAAkecj6nL7Q+jnNQAIUB9IgSxOd0XNldWpup6aisygu8Rz0dr5/rOU/j0U9QmQ6FHJikmghFm7/lgOolHWDWkVPowLgsXCg6LCwAh+QQJCgADACwAAAAAIAAgAAACQZyPqcvtD6Oc1AAhQH0iBLE53Rc2V1am6npqKzKC7xHPR2vn+s5TeF4DemS2X++INA6UlKDBWYFCfRiXpYrMarcFACH5BAkKAAMALAAAAAAgACAAAAI7nI+py+0Po5zUACFAfSIEsTndFzZXVqbqemorMoLvEc9Ha+f6zlN4XgN6ZLZf74g0zoI25kyJjEqniAIAIfkECQoAAwAsAAAAACAAIAAAAjecj6nL7Q+jnNQAIUB9IgSxOd0XNldWpup6aisygu8Rz0dr5/rOU3heA3pktl/viEwql8ymM1cAACH5BAkKAAMALAAAAAAgACAAAAIynI+py+0Po5zUACFAfSIEsTndFzZXVqbqemorMoLvEc9Ha+f6zvf+DwwKh8Si8YhUFQAAIfkECQoAAwAsAAAAACAAIAAAAiqcj6nL7Q+jnNQAIUB9IgSxOd0XNldWpurKtu4Lx/JM1/aN5/rO9/4vKwAAIfkEBQoAAwAsAAAAACAAIAAAAiqcj6nL7Q+jnNQBIUB9IgSxOd0XNldWpurKtu4Lx/JM1/aN5/rO9/6vKgAAIfkEBQoAAwAsGAAKAAQABAAAAgeEZBGneFAAACH5BAUKAAMALBgAEgAEAAQAAAIHhGQRp3hQAAAh+QQFCgADACwSABgABAAEAAACB4RkEad4UAAAIfkEBQoAAwAsCgAYAAQABAAAAgeEZBGneFAAACH5BAUKAAMALAQAEgAEAAQAAAIHhGQRp3hQAAAh+QQFCgADACwEAAoABAAEAAACB4RkEad4UAAAOw==";
    @override
    public render(): React.ReactElement {
        const image = this._getSharedStatus(this.props.isExternallyShared);
        if (image != "") {
            return (
                <img src="{image}" width="17px" />
            );
        }
        else {
            return ();
        }
    }
    private _getSharedStatus(value: number): string {
        if (value == 0)
            return this.LOADING_ICON;
        if (value == 1)
            return this.SHARED_ICON;
        if (value == 2)
            return "";
    }
}

The github project shows you how to create the column for the list. It is a standard text column, that doesn’t show up in any New/Edit forms. It only shows up in the view field. I’m using a pnp template to add it to the site collection.

Taking it further

The spfx-isExternallayShared field customizer is a very simple version of the one I created for my client. I was concerned with the number of times I was calling the GetSharingInformation, especially on very large libraries with many users. The call is only made for the number of items that are currently displayed on the screen to the user, however, when you start multiplying that by a possible 2000+ users that is a much larger load on SharePoint. Therefore, I moved the calling of the REST API to an Azure Function. Using the uniqueID of the item I was able to also use Redis Cache.

The first person who visited the library for each row would perform the actual GetSharingInformation call within the Azure Function, as there is nothing in the Redis Cache, then store either a 0 (not externally shared) or 1 (externally shared) with the UniqueID of the item as the key in Redis Cache. The second call to the library, the Azure Function would first check the cache, and just return that value.

The downside was the additional cost of running Azure Functions and Redis Cache, and that the externally shared value was no longer up to date and could be an hour out of date.

UPDATE: It turns out moving it to an Azure Function, where the calls are being made by one App account causes throttling issues. The client had over 79k calls made in an hour (many document libraries and users), and the App token that I was using got blocked by Microsoft. After further conversations with technical people at Microsoft, the calls need to be made in the context of the user. Since I’ve implemented the simple solution above, we haven’t noticed any throttling issues. Therefore if you want to move to an Azure Function, any calls back into SharePoint should really be done in the context of the user.