Using Allow Lists and Block Lists to Secure External Access
From the ground up, Microsoft 365 is designed to make collaboration, both internal and external, easy for end users. Whether that is a good thing depends on an organization’s attitude toward data sharing and external collaboration. One clear thing is the importance of understanding external collaboration capabilities, particularly when assessing options to combat oversharing or potential vulnerabilities.
Controlling which external domains you allow is key to controlling which external domains users can communicate with and the level of collaboration allowed. Microsoft provides a set of tools (such as Teams External Access Allow Lists) to help administrators enforce governance at a domain level. However, there is no single pane to view the overall status of an external domain, and validating the configuration of each workload can be time-consuming.
To make reporting the configuration for external domains in a tenant easier, I created a PowerShell script to extract details about workload configurations for a Microsoft 365 tenant. The script (available on GitHub) outputs a report detailing the configuration settings for each domain in the following areas:
- TenantID – If the domain has an Entra ID tenant associated, this field contains the TenantID of that tenant
- Domain – The Domain name.
- GuestInvitaions – If B2B Guest invitations are allowed or blocked based on the configuration of the Entra ID External Identities configuration. If the domain is not configured in the settings, the value will read Org Default. The Org Default settings are listed under the Default domain entry in the output.
- TeamsFederation – If Teams external chat is allowed or blocked based on the configuration of the Teams external access settings. If the domain is not configured in the settings, the value will read Org Default. The Org Default settings are listed under the Default domain entry in the output.
- SharePointSharing – If SharePoint external sharing is allowed or blocked based on the configuration of the SharePoint external sharing policy. If the domain is not configured in the settings, the value will read Org Default. The Org Default settings are listed under the Default domain entry in the output.
- B2BCrossTenantAccessInbound – If there is an inbound Cross-Tenant access configuration for a domain, this value details the configuration for both users and groups. This value outlines if access is allowed or blocked for both users and groups and if all users / all groups are specified or if selected users / select groups are specified. If the domain is not configured in the settings, the value will read Org Default. The Org Default settings are listed under the Default domain entry in the output.
- B2BCrossTenantAccessOutbound – If there is an outbound Cross-Tenant access configuration for a domain, this value details the configuration for both users and groups. This value outlines if access is allowed or blocked for both users and groups and if all users / all groups are specified or if selected users / select groups are specified. If the domain is not configured in the settings, the value will read Org Default. The Org Default settings are listed under the Default domain entry in the output.
- B2BDirectConnectInbound – If there is an inbound Direct Connect configuration for the domain, this value details the configuration for both users and groups. This value outlines if access is allowed or blocked for both users and groups and if all users / all groups are specified or if selected users / select groups are specified. If the domain is not configured in the settings, the value will read Org Default. The Org Default settings are listed under the Default domain entry in the output.
- B2BDirectConnectOutbound– If there is an outbound Direct Connect configuration for the domain, this value details the configuration for both users and groups. This value outlines if access is allowed or blocked for both user and groups and if all users / all groups are specified or if selected users / select groups are specified. If the domain is not configured in the settings, the value will read Org Default. The Org Default settings are listed under the Default domain entry in the output.
- CrossTenantSyncEnabled – If cross-tenant sync is enabled or disabled for the domain, if there is no policy for the domain, this value will show N/A. There is no org default configuration for this setting.
- TrustSettings – If there are trust settings configured for the domain, this value lists the trusted settings configured (Compliant Devices, MFA and Intune Managed Devices). If there is no policy for the domain, this value will show N/A. There is no org default configuration for this setting.
- AutomaticRedemption – If automatic invite redemption is enabled or disabled for the domain. If there is no policy for the domain, this value will show N/A. There is no org default configuration for this setting.
The script captures configuration settings for domains and is captured in a CSV export file to give an easy-to-access overview of external domains (Figure 1).
Preparing to run
In the script, I use two different PowerShell modules. The bulk of the heavy lifting is done by the Microsoft Graph PowerShell SDK. The second module is the MicrosoftTeams PowerShell module. This is needed due to a limitation with the Microsoft Graph API where it is not currently possible to retrieve the Teams external access allow and block list configuration.
As the script runs interactively, delegated Microsoft Graph permissions are used. The code can be adapted to work non-interactively (using application permissions), such as through Azure Automation, as explained here. Delegated permissions require that the account you use to sign in has access to the configuration settings. To ensure the account has the correct permissions, I recommend assigning a minimum of the “Global Reader” role to the account you run the script with.
TEC Talk: AD-Based Attacks in 2023 – What We’ve Learned So Far – July 25th @ 11 AM EST
Hear what Paul Robichaux & Bryan Patton have to say about AD Misconfigurations, vulnerabilities, and Strengthening your organization’s AD defenses.
Using PowerShell to Retrieve the Tenant Settings
As I’ve mentioned, there a quite a few places to pull information from to compile this report. The script contains a large amount of logic to compile the report, but the basic steps followed are:
- Connect to Microsoft Graph and Teams
- Get the settings of the B2B Management Policy
- Get the settings of the SharePoint Online external sharing policy
- Get the settings of the Teams external access federation configuration
- Get the Tenant ID of each domain discovered (if one exists)
- Get the Cross-tenant Access Policies
Connecting to Microsoft Graph and Teams PowerShell
The first step the script takes is to connect to the tenant with both the Microsoft Graph SDK (Version 2) and Teams PowerShell modules. The script also uses several queries which are only available under the “beta” endpoint. As the Microsoft Graph PowerShell SDK now comes in two distinct modules for V1.0 and Beta endpoints, make sure you have the up-to-date module installed, particularly the “Beta” module. This is required to get the cross-tenant access policy settings which are not yet available under the v1.0 endpoint. The following code initiates the connection to Teams and retrieves an access token for the Microsoft Graph:
##Connect to MG Graph and Teams Connect-MgGraph -Scopes "IdentityProvider.Read.All policy.read.all CrossTenantInformation.ReadBasic.All SharePointTenantSettings.Read.All" Connect-MicrosoftTeams
The permission scopes required to run the script are:
- IdentityProvidor.Read.All – To return the B2B Management Policy details.
- Policy.Read.All – To return Entra ID Policy details.
- CrossTenantInformation.ReadBasic.All -To return cross-tenant access policy details.
- SharePointTenantSettings.Read.All – To return SharePoint tenant settings.
During connection, there are two authentication prompts (one for Graph and one for Teams).
Get the B2B Management Policy
The B2B Management Policy contains the configuration for Entra ID external identities. An important note here is that to get this information, the script uses the Invoke-MgGraphRequest cmdlet to query the legacy/policies endpoint. This is currently the only method to retrieve this information via the Graph API. While it’s not ideal to use a legacy endpoint, the alternative is to use the legacy AzureAD PowerShell module, which Microsoft will retire in March 2024.
##Get external domains from B2BManagementPolicy $B2BManagementPolicy = ((Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/legacy/policies").value.definition | convertfrom-json).b2bmanagementpolicy $allowedGuestDomains = $B2BManagementPolicy.InvitationsAllowedAndBlockedDomainsPolicy.alloweddomains $blockedGuestDomains = $B2BManagementPolicy.InvitationsAllowedAndBlockedDomainsPolicy.blockeddomains
Get the SharePoint Online External Sharing Policy
The next step is to get the external sharing configuration from the SharePoint Online tenant settings. SharePoint Online tenant settings have recently been made available through the Microsoft Graph API. There is still no cmdlet in the Graph PowerShell SDK for this, so the URI is passed to the Invoke-MgGraphRequest cmdlet as shown below.
##Get SharePoint Online Tenant Settings $Uri = "https://graph.microsoft.com/beta/admin/sharepoint/settings" $SPOSettings = Invoke-MgGraphRequest -Uri $Uri -Method Get
Get the Teams External Domains Configuration
The Teams external domains configuration can’t currently be retrieved using the Microsoft Graph API. For this step, we use the Microsoft Teams PowerShell Module to get details of the configuration.
##Get external domains from TeamsFederationConfiguration $TeamsFederationSettings = Get-CsTenantFederationConfiguration
Hopefully, this data becomes available through the Graph API one day, and the Teams module can be removed from this script, but when building reports like this, you sometimes need to work with what’s available.
Update each Domain with its Tenant ID
This is where things get interesting. So far, each endpoint returns a list of domain names that can easily be added to the output. For Cross-Tenant policies, however, they return a Tenant ID. To make sure that the output is relatively clean and avoids duplicate entries, the script pulls the Tenant ID for each domain discovered so far and adds it to the output. This means that the Tenant ID can be used to check if a domain exists in one of the previous settings and update the output accordingly.
To do this, the script loops through each domain in the export object array and queries the findTenantInformationByDomain endpoint to get the associated Tenant ID for that domain.
##Update Tenant IDs for all existing domains foreach ($domain in $domainSettingsObjectArray) { Try { $tenantID = (Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/tenantRelationships/findTenantInformationByDomainName(domainName='$($domain.Domain)')").tenantID $domain.tenantID = $tenantID } Catch { $tenantID = "N/A" } $domain.tenantID = $tenantID }
Get Cross-Tenant Policy Details
Finally, the script queries both the default and non-default cross-tenant policies. The Get-MgBetaPolicyCrossTenantAccessPolicyDefault and Get-MgBetaPolicyCrossTenantAccessPolicyPartner cmdlets return the information needed to document the configuration of the Cross-Tenant policies in the tenant.
##Get Cross Tenant Policy Defaults $DefaultCrossTenantPolicy = Get-MgBetaPolicyCrossTenantAccessPolicyDefault ##Get Cross Tenant Policies for Partner Domains [array]$CrossTenantPartnerPolicies = Get-MgBetaPolicyCrossTenantAccessPolicyPartner
An important note on cross-tenant policies is that each is listed by Tenant ID. The script uses the findTenantInformationByTenantId endpoint to find the domain associated with the cross-tenant policy. The findTenantInformationByTenantId endpoint only returns the default tenant domain. If the Tenant ID matches any domain obtained from any other step, they will also be updated. If there are other domains associated with the Tenant ID which haven’t been identified by other steps, they will not show in the report.
Reviewing the Output
Output in the script is handled in two ways, the first is simply outputting the results to the screen in a list format, and the second is by exporting the output file to the C:\temp folder. The screen output is good for a quick overview, as shown in Figure 2.
The output file is useful to compare configuration changes over time or to review larger outputs. The items listed in the output are based on the setting value for each domain.
Understanding your configuration
Whether your organization sees external collaboration as a risk or a benefit, it’s important to understand exactly what you have configured. With the numerous settings available to control different aspects of external sharing and collaboration, it’s easy to lose track of the capabilities of each domain. For instance, it’s not uncommon for admins to allow Teams to chat with an external partner only to discover that users can’t share files in chat. This type of situation often needs someone to track down where the issue is and figure out what list the domain needs to be added to.
Having strong processes around the day-to-day management is key to ensuring data doesn’t leak inadvertently from your tenant. This script doesn’t replace day-to-day management, but it does document workload external collaboration settings and hopefully saves time. As always, improvements can be made, and additional functionality can be added. As things evolve within Microsoft 365, there will no doubt be more to add to a report like this.