Removing External Users fully from a SharePoint Tenancy using PowerShell


This blog post has all come about as the client I was working for was having problems sharing documents in SharePoint with some external users. It turned out that the user was already in Azure AD as a Contact which is part of Exchange. This meant when an internal person attempted to share/Invite into SharePoint/MSTeams it all appeared to work correctly for the external user, but sometimes it didn’t. When looking at external users through the Admin portal, this external user was showing, but their email address was blank. After speaking with Microsoft, it turns out, because the email address was already found within the tenancy, it creates a unique violation when adding the external user to the Active Directory.

I have been working with Microsoft support regarding this, and the resolution was that this is as design!!??! Only by feeding back on the Office 365 uservoice this issue “might” looked at and fixed. See resolution notes below:

Symptom:
When you invite external users who exist as contacts in your environment, their email does not get populated in their guest user ID which results in them not being able to login to your environment and access the shared data.
Cause:
The issue is coming from a conflict caused by the email address which is already populated for the mail contact.
Resolution:
This is behavior by design as all objects in Azure AD have to be unique.
You cannot have 2 objects with the same email address.

When you invite one of your contacts to your content in O365, it actually creates a completely new guest user object in your environment and since the email address which is supposed to be populated in the email attribute is already in use by the contact, the email address does not get populated.

The only way to resolve this issue at the moment is to eliminate any conflicts that are in place, by removing the conflicting email contact and re-invite the user to your content.
More information:
The best thing I can offer to you is the following:

Please go to our UserVoice portal where other people are facing the same behavior and up-vote it, comment and have the whole IT department do the same as well.

Allow a “Guest User” to be converted to a different account type
https://office365.uservoice.com/forums/273493-office-365-admin/suggestions/19966537-allow-a-guest-user-to-be-converted-to-a-differen

This led me to working on a process and script that would remove the users from everywhere.

Locations to remove the External User from:

  • Contacts
  • Azure AD Guest Users
  • Azure AD Deleted Users
  • All SharePoint Sites
  • All SharePoint Hidden User lists
  • SharePoint User Profile

Contacts

To remove the External User from the contacts you will need to use the MSOL PowerShell module.

$UserEmail = "<ExternalUserEmailAddress>"
Connect-MsolService
Get-MsolContact | ? EmailAddress -eq $UserEmail | Remove-MsolContact -Force

Or you can manually do this by going to admin.microsoft.com and under Users -> Contacts select the user and click Delete contacts.

Azure AD

To remove the External User from Azure AD you will still require using the MSOL PowerShell module. In fact, this script and the above script could be merged.

$Environment = "<TenantName>"
$UserEmail = "<ExternalUserEmailAddress>"
Connect-MsolService
$externalConversionEmail = ($UserEmail -replace '@', '_') + "#EXT#@" + $Environment + ".onmicrosoft.com"
$FoundUser = Get-MsolUser | ? UserPrincipalName -eq $externalConversionEmail
if($FoundUser){
Remove-MsolUser -UserPrincipalName $($FoundUser.UserPrincipalName) -Force
#To see All Deleted User Get-MsolUser -ReturnDeletedUsers
Remove-MsolUser -UserPrincipalName $($FoundUser.UserPrincipalName) -RemoveFromRecycelBin -Force
#To Remove All Deleted Users Get-MsolUsers -ReturnDeletedUsers | Remove-MsolUser -RemoveFromRecycleBin -Force
}

To do this manually, in admin.microsoft.com under Users -> Guest Users, select the user and click delete.

Then go into Users -> Deleted users and remove them from there.

Remove from SharePoint

To remove from SharePoint, if you have a large tenancy and you don’t know all the places where the external user could have been shared with, then you will have to use the following script. This script will remove the external user from the SharePoint Site, ensure that they are removed from the User Information list, and then lastly it will clear the person from the SharePoint User Profile.

I discovered that if I didn’t remove them from the User Profile, when attempted to reshare a document with that user, the people picker would grab the internal userprincipalname (<ExternalUserEmail>#EXT#@<Tenant>.onmicrosoft.com) as the email address and then prevent me clicking the Sharing button. This is because the people picker uses Graph API /Me/People and grabs the value from there. Once removed from everywhere, including the User Profile this no longer happens.

The following script uses SPO PowerShell Module and you will need to connect first using Connect-SPOService. The account that you use, needs to be a SharePoint Global Administrator.

The script checks if it can find the ExternalUser, and if it can remove the user using Remove-SPOExternalUser.

Then it loops through every site collection and looks for the user using Get-SPOUser with the internal userprincipalname. If found it removes the user using Remove-SPOUser. Once it has looped through all SharePoint sites, it then checks the SharePoint User Profile and removes the user from UserProfile Remove-SPOUserProfile. This command will remove a user from the UserProfile if they in the “Active Profiles” or the “Profiles Missing from Import”

<#
.SYNOPSIS
Loops through the SharePoint sites of the tenant, looking for the external user and removing them.
You need to have already connected to the Tenant as a SharePoint Global Adminstrator using Connect-SPOService -url:https://<tenant>-admin.sharepoint.com
.EXAMPLE
.\Remove-ExternalUserFromTenant.ps1 -Environment:<tenant> -UserEmail:<externalEmailAddres>
#For Tenant called Dev34223 and external email address fred.bloggs@outlookdomain.com
.\Remove-ExternalUserFromTenant.ps1 -Environment:Dev34223 -UserEmail:fred.bloggs@outlookdomain.com
#>
[CmdletBinding(SupportsShouldProcess)]
param(
[Parameter(Mandatory)]
[string]
$Environment,
[Parameter(Mandatory)]
[string]
$UserEmail
)
Clear-Host
$sites = Get-SPOSite -Limit ALL
$externalConversionEmail = ($UserEmail -replace '@', '_') + "#EXT#@" + $Environment + ".onmicrosoft.com"
$ErrorActionPreference = 'Stop'
$InformationPreference = 'Continue'
Write-Information -MessageData "Get $UserEmail External User within SharePoint"
$ExtUser = Get-SPOExternalUser -Filter $UserEmail
if ($null -ne $ExtUser) {
Write-Information -MessageData "Remove $UserEmail within SharePoint"
Remove-SPOExternalUser -UniqueIDs @($ExtUser.UniqueId) -Confirm:$false
}
$found = $false
$Sites | ForEach-Object {
$site = $PSItem
$i = $i + 1
try {
Get-SPOUser -site:$($site.Url) -LoginName:$externalConversionEmail
write-Information "Found user $UserEmail in site $($site.Title) Url:$($site.Url)"
Remove-SPOUser -site:$($site.Url) -LoginName:$externalConversionEmail
$found = $true;
}
catch {
#User not found.
}
Write-Progress -Activity "Removing User - $UserEmail" -Status "Progress:$($site.Url)" -PercentComplete ($i / $Sites.count * 100)
}
if ($found) {
Write-Information "User $UserEmail removed from SharePoint Sites"
}
else {
Write-Information "User $UserEmail wasn't found within SharePoint Sites"
}
Write-Information -MessageData "Remove $externalConversionEmail from SharePoint User profile"
try {
Remove-SPOUserProfile -LoginName $externalConversionEmail
}
catch {
Write-Information "Unable to find $externalConversionEmail in the user profiles."
}

If the plan is to add the external person back into your tenant, once the script has run, you will need to wait at least a few hours (maybe leave it for a day to be sure) to ensure all back end processes of Microsoft have completed.

When you share a document/folder with the external user they will get the invited link and enter a code experience, this way they do not turn up inside you Azure AD. However, if you share a site with them, or add them to a MS Teams, they will appear in your Azure AD correctly.

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.

Externally Sharing – GetSharingInformation REST API


Externally Sharing Series:

My last two blogs I have been explaining how to set up your tenant for External Sharing, and as a user how to share a document to an external user. At the end of my last blog post I explained how you can view which documents have been externally shared, by navigating to Site Usage and viewing the Externally Shared tab.

However, as stated previously, this section is flawed in a couple of ways.

  1. It relies on Search. Meaning that documents may not show up for a while.
  2. It only shows documents that have been externally shared, and the external user has viewed the file. This is because there is a flag (ViewableByExternalUser) on the file that gets updated, only after an external user has viewed the file, not when you have shared it.
  3. Following on from the last point, your site could have lots of documents externally shared, but you would never know if no one externally has viewed the file.
  4. Sharing of a folder will show up the folder in the list, (if external user goes directly to the folder). However, any items that are within the folder, which would also be externally shared, do not show up in this list. (Even if the external user has viewed the files)

If you had a content query webpart and want the same results to be brought back as it shows you in the ‘Shared externally’ tab you need to set your Search Query as follows:

Path:"<a href="https://examplesite.sharepoint.com/">https://examplesite.sharepoint.com/</a>" (IsDocument:"True" OR contentclass:"STS_ListItem") ViewableByExternalUsers=TRUE

GetSharingInformation REST API

The above ‘Externally Shared’ tab and search call, although it can bring back documents, it wasn’t what my client really wanted. They wanted to know straight away if the document was externally shared or not.

I emailed Stephen Rice (Product Manager for SharePoint and OneDrive for business) at Microsoft, and he said there wasn’t a current way of tagging the documents if they were externally shared at the time of sharing the document. The ViewableByExternalUsers is only updated when the user enters the document.

What I couldn’t understand was when I click the Info Panel for a document, if it was externally shared, it would state that the item was shared with guests. This was before an external user access the document.

Using Chrome network capture, I investigated what happens when the Info Panel loads up, and one call caught my attention.

https://tenant.sharepoint.com/_api/web/Lists(@a1)/GetItemById(@2)/GetSharingInformation?@a1='listguid'&@a2='itemID'&$Expand=permissionsInformation,pickerSettings

This call is made via a POST call.

Looking at the Preview response and digging further down into the results that returned, the permissionsInformation section contains all the Sharing links, and groups and users that the item is shared with.

permissionsInformation > links > results

Contains an array of link results. This includes internal sharing links, anonymous links and external links.

linkDetails

Important part of the linkDetails:

  • Created & CreatedBy – When the link was created and by who.
  • Expiration – Only used for anonymous links
  • HasExternalGuestInvitees –
    Indicates if any users that have been invited are external users
  • IsEditLink – Set to true for contribute, false for read
  • Invitations – See below
  • LinkKind – Enumeration (Microsoft.SharePoint.Client.SharingLinkKind)
    • 0 – Uninitialized
    • 1 – Direct
    • 2 – OrganizationView – People within ‘tenant’ Read
    • 3 – OrganizationEdit – People within ‘tenant’ Contribute
    • 4 – AnonymousView
    • 5 – AnonymousEdit
    • 6 – Flexible – Used in External Sharing with email address.
Invitations > results

The invitation sections gathers the information about the person who doing the inviting ‘InvitedBy‘ and who has been invited “invitee”. You also have the exact time and date when the invite was sent out.

linkMembers

I have discovered that the linkMembers only has values after the external user has visited the link.

permissionsInformation > links > results

The principals results are the groups and members that have access to the documents. If the external user has been previous added to your tenant via a site share, or document share before December 2017, then they will have an actual account in your tenant. Anything shared with them will show up here, instead of through the Shared Links. The Paul Matthews shown in the screenshot below is actually my personal email address and therefore an external user.


The role are:

  • 0 – None
  • 1 – Can View
  • 2 – Can Edit
  • 3 – Owner

principal

The principal contains the user or SP Group information. For users (principalType 1) this includes if they are external or not.

How to use this information?

The last section showed you all about the GetSharingInformation REST API call. It has to be done with a POST call, but apart from that, it does obtain all the Sharing Information in regard to that item. The only one thing I would say it is lacking is that if a SP Group that is assigned to that document had an external user in it. (For example, the members group, because you have externally shared the site) the isExternal flag on the principal of that group isn’t set to True.

Now knowing about GetSharingInformation REST API call I was able to create a SPFX field customizer. My next blog post will show you how I built the SPFX field customizer.

Externally Sharing – User experience with SharePoint Online


Externally Sharing Series:

Continuing from my previous blog “Externally Sharing – Settings within SharePoint Online” which shows you the different configuration settings within your tenant to enable External Sharing, this blog post is showing from a user perspective how to share, and from the external user’s perspective on the email they receive and how they sign in.

As an internal user sharing

From a document library/list, a user can easily share a document/folder with an external user.

  • Click on the document that you wish to share.
  • From the ribbon click on Share
  • From the dialog, select the drop down.
  • Select Specific people, optionally untick Allow editing if you only wish to give read access.
  • Put in the email address.
  • Click Send

An email will be sent to the user.

To find out who the document has been shared with, click the tick next to the document, then open the information panel. Under Has Access, click Mange Access.

If the user you have shared with, is found within your tenants directory, they will be added as a person to the manage access section. (Cliff K) in screenshot above. If the external user is not found in your directory, then a link will be created. (The top link in the screenshot above). By hovering your mouse over the image below the link, will show you who that person is.

An external user experience

An external user will receive an email from no-reply@sharepointonline.com. Please Note: The email might be in the users Junk Email.

The external user will need to click the link. They will be taken to a page, where they need to verify that it is them.

The external user will need to click the Send Code button. An email will be sent to their email address and be given a code. Please note: This email could also end up in the Junk folder.



The external user will then gain access to the file.

What documents are externally shared?

Unless you are the only user on the site, you will not know what documents have been externally shared. There is a way to see what documents have been externally shared for a given site.

  • Go to Site Contents.
  • Along the menu bar click the Site Usage button.
  • Then click the Shared externally tab.

This will display the externally shared items.

Issues with the Externally Shared tab

Although the above shows you which documents are externally shared, it is flawed in a couple of ways.

  1. It relies on Search. Meaning that documents may not show up for a while.
  2. It only shows documents that have been externally shared, and the external user has viewed the file. This is because there is a flag on the file that get’s updated, only after an external user has viewed the file, not when you have shared it.
  3. Following on from the last point, your site could have lots of documents externally shared, but you would never know if no one externally has viewed the file.
  4. Sharing of a folder will show up the folder in the list, (if external user goes directly to the folder). However, any items that are within the folder, which would also be externally shared, do not show up in this list. (Even if the external user has viewed the files)

This blog post has described how you would share a document with an external user, and how that external user would then gain access to the document. Lastly, I have shown where you can view which documents have been externally shared, although it isn’t great.

My next post will be more development focused, and explain how I’m able to work out if an item has been externally shared or not.


Externally Sharing – Settings within SharePoint Online


Externally Sharing Series:

It is possible to share content from your SharePoint environment to external users. These users are not part of your tenant. They might have their own tenant within their company, or they might just be a person with a personal email address, such as a live.com or gmail.com.

There are few setup steps within your tenant to allow/disallow external Sharing.

SharePoint Admin center

By navigating to your SharePoint Admin Center, https://<tenant>-admin.sharepoint.com on the left hand side of the screen, there is a sharing navigation link. This takes you to the ExternalSharing.aspx page.

Sharing outside your organization

This part of the page you are able to disable externally sharing completely, or open it up granular.

  • Don’t allow sharing outside your organization
    • This disables externally sharing
  • Allow sharing only with the external users that already exist in your organization’s directory
    • If you have previous shared in the past (especially before Microsoft changed how the External Sharing worked – Dec 2017), or shared a entire site with an external person, their account get’s put into your Organisation as a guest account. Having this option on, will continue to allow sharing with those people, but prevent new external users to be shared with.
  • Allow users to invite and share with authenticated external users
    • This option, allows users to externally share, but prevent them sending anonymous links. New external users can be invited. (Personally, I feel this option is also missing an expired option, where a link only exists for x number of days for external users, hopefully this is something Microsoft will include eventually)
  • Allow sharing to authenticated external users and using anonymous access links.
    • This options completely opens up the external sharing. Your users can externally share with anyone, without without requiring the external user to authenticate. With anonymous sharing, you also get the option to expire links after x number of days, and you can set the access link types that get sent out to users. By setting Files or Folders to ‘View’, this will mean every anonymous external link sent out will always be read only. If set to ‘View and Edit’ for files or ‘View, Edit, and Upload’ for folders, that allows the person who is sending out the anonymous external link to decide if they want to apply Read only or Contribute permission for the link.

Who can share outside your organization

If your organisation wanted to only allow a group of people to externally share, here they can do that. This can be done by using individual people names, or even with an AD group. What is nice here, is if you don’t trust a group of people to allow anonymous sharing, you can also assign a different group of people to allow anonymous sharing.

Default link type

When someone selects a document/folder and clicks on the Share option you can default the link type, so the user doesn’t need to switch each time.

Direct – specific people: (Screenshot below – Yellow option) Specific people

Internal – only people in your organization: (Screenshot below – Blue option) People in <tenantName>

Anonymous Access – anyone with the link: (Screenshot below – Green option) Anyone

The tick option of use shorter links when sharing files and folders

Default link type

Here you can set the default permission of either view or edit when someone creates a sharing link. This isn’t just for external links, this applies to anonymous, internal and direct links. If your company is quite security conscientious, setting this to View is a good idea.

Additional settings

As stated on the page, these settings do not apply to anonymous access links.

  • Limit external sharing using domains (applies to all future sharing invitations).
    • If your company only works with a couple of 3rd party companies, and your documents shouldn’t be shared with anyone else, this is a good option to use. Here you can ‘whitelist’ domain names to send to.
  • Prevent external users from sharing files, folders, and sites that they don’t own.
    • Once you have externally shared your document to someone, do you want them to then be able to share onto someone else that you haven’t authorised? If not, then ensure this is ticked. If it their own document that they have uploaded/created that will be able to share that.
  • External users must accept sharing invitations using the same account that the invitations were sent to.
    • When you send an link to someone, by having this option selected, they will only be able to sign in using the email address you sent the invite to. This option selected can also cause problems if you send the link to someone personal email, and they also have their own 365 account, as their 365 cookie token can get picked up, and they receive the error message that the account they are using to sign in with is not the same account the invite was sent to.
  • Require recipients to continually prove account ownership when they access shared items.
    • Once the external user has logged in once without this ticked a cookie is put on their machine to allow them access to that document again without require to re-authenticate with code.

Notifications

These notifications are for when sharing using One-Drive for business. You can optionally select if an email will be sent or not for the following 3 options.

  • Other users invite additional external users to shared files.
  • External users accept invitations to access files.
  • An anonymous access link is created or changed.

OneDrive External Sharing

There is also a configuration page for OneDrive external sharing.

Go to the OneDrive admin centre. Click the Waffle -> Admin. Under Admin centers click OneDrive. Then on the left-hand navigation, click Sharing.

Note that these setting when changed here, will affect the settings you set in SharePoint Admin centre for Sharing. So, change something here, then head back to the SharePoint Admin Centre sharing page, and the changes will reflect there. However, there is one setting here, that isn’t in the SharePoint Admin Centre sharing page, and that is OneDrive permissive sharing.

The above sections allows you to set the Sharing settings for SharePoint different to OneDrive. In the above screenshot, I’ve allowed Sharing (not anonymous) in SharePoint and prevented any external sharing in OneDrive.

Please note: You cannot set the OneDrive to be more permissive than SharePoint, As stated underneath the sliders.

Site Collection sharing

In SharePoint Admin Center in the Site Collections, you can change some of the tenant sharing settings just for that give site collection. Select the site collection you wish to change and click Sharing in the ribbon.

Sharing outside your company

At the site collection level, you are able to choose a option for Sharing outside your company, as long as it’s less permissive than your tenant settings.

Site collection additional settings

  • Limit external sharing by domain
    • Here you can create either a blacklist to block sending external sharing links to certain domains, or ‘whitelist’ a bunch of domains so that external sharing can only go to them.

Allowing non-owners to invite new users

By clicking the “Turn off sharing for non-owners on all sites in this site collection” will only allow people in the Owners group to share anything with anyone externally. Once turned off, it looks like there is no way of turning it on again. They way you turn it back on is going into the Site Collection -> Site Settings -> Site Permissions -> Access request settings. Here you can tick the top check box “Allow members to share the site and individual files and folders” (More explaining on the Access Request Settings in next section)

Site Collection (in the site) settings

By going to the actual root site, there is one more place where you can affect sharing settings. This is the Access Request Settings.

Site Settings -> Site Permissions. Then Access Request Settings in the ribbon.

There are 3 settings here.

Allow members to share the site and individual files and folders

  • With this ticked, members can share.

    • However, with this just ticked they cannot share the site. When a user tries to share the site the Share button clicks but nothing happens.
  • With this unticked you are turning off sharing for everyone except for Owners.

Allow members to invite others to the site members groups.

  • With this ticked. Any user that is part of the Members group will be able to Share the site. It doesn’t matter if a user has permissions in the site, say in a different group, unless they are part of the members groups, they will not be able to share the site.

Allow access requests

  • With this ticked and with an email address put in place. When a member attempts to share a site, an owner must allow the access. Requests can be found at: Site Settings -> Access requests and invitations.

In this blog post I have explained the different areas where you can set up Externally Sharing in your tenant/site. In my next blog post I’m going to go through the steps as a user, sharing documents/folders with external users and the steps the external user goes through to sign in.