Updated version of the mailbox folder permissions inventory script

The next script I’ve updated to take advantage of the “V2” Exchange Online PowerShell module is the mailbox folder permissions inventory one. Its initial version was released over 5 years back, and while I made some minor updates to it due to changes on the backend, it needed an overhaul. Let’s dig in.

The idea behind the script is to provide you a detailed report of folder-level permissions across all mailboxes of the selected type(s). As with most other “inventory” types of scripts I’ve created, a set of input parameters is provided for you to narrow down the list of mailboxes or exclude certain permissions from the output file. Said parameters include:

  • IncludeUserMailboxes – use it to include User mailboxes in the output.
  • IncludeSharedMailboxes – use it to include Shared mailboxes in the output.
  • IncludeRoomMailboxes – use it to include Room, Equipment and Scheduling (Booking) mailboxes int he output.
  • IncludeAll – use it to include all mailbox types listed above in the output.
  • IncludeDefaultPermissions – specify this switch to include permission entries for the Default security principal.
  • ExcludeUsers – use this parameter to specify a list of users (service accounts, admin accounts, etc) to exclude from the output. Make sure to provide an UPN value(s)!

The list of mailboxes is of course gathered via the Get-ExOMailbox cmdlet, and by default only User mailboxes will be included. Use the –IncludeSharedMailboxes switch to include Shared mailboxes and/or the –IncludeRoomMailboxes switch to include resources. Or, just specify the –IncludeAll switch to include all mailbox types. Unlike the mailbox-level permissions scenario, soft-deleted mailboxes are not supported.

Once we obtain the list of mailboxes, the script will iterate over each entry and obtain a list of folders via the Get-ExOMailboxFolderStatistics cmdlet. To further reduce the output, we only cover “user accessible” folders. Their corresponding folder types are added to the $includedfolders array you can find on line 53. Compared to the previous version of the script, I’ve removed the Clutter folder, which should be a thing of the past now. The list is still big enough, so feel free to remove some of the entries you don’t care about from it, such as the Outbox folder. A trimmed down version is provided on line 54, but you can also input your own list.

Similarly, an $excludedfolders array is used to filter out unwanted non-default (or “user created”) folders. The array can be found on line 58 and lists the folder names, not the folder type. Examples include folders such as the “News feeds” or “Suggested Contacts”, but as before, feel free to add to the list. The more folders you exclude here, the faster the script will run!

The following script block illustrates the logic described above – get the list of all the folders with a minimal set of properties, then filter it out to only return folders we are interested in:

 #Get the folder statistics for each mailbox and use them to filter out folders we are not interested in
$MBSMTP = $MB.PrimarySmtpAddress.ToString()
$MBfolders = Get-ExOMailboxFolderStatistics $MBSMTP
$MBfolders = $MBfolders | ? {($_.FolderType -eq "User created" -or $_.FolderType -in $includedfolders) -and ($_.Name -notin $excludedfolders)}
#If no folders left after applying the filters, move to next mailbox
if (!$MBfolders) { continue } 

Any user-created (sub)folder will still be present in the output after the filtering operation, unless you have explicitly included its name in the $excludedfolders array as detailed above.

Once the list of folders is obtained for the mailbox in question, we proceed to enumerating the permissions for each folder, via the Get-ExOMailboxFolderPermission cmdlet. As the REST-based cmdlets seem to have some trouble with special characters or unicode within the folder name, the new version of the script will use the folderId value instead. While this method is not exactly user-friendly, the output will still expose the corresponding (human-readable) folder name value, so nothing to worry about here. Just for the sake of illustrating the issue we are trying to solve here, try getting the folder permissions of any folder that contains a colon character (“:”):

Once the permissions for the folder are obtained, the script will trim the list depending on the values provided for the –IncludeDefaultPermissions and the –ExcludeUsers parameter. The former is useful for scenarios where you are delegating permissions to all users within the organization, such as a company-shared calendar. Another scenario where you might be interested in the permissions granted to the Default security principal is for the Root folder (“Top of information store”) – having a FolderVisible or equivalent value here is a prerequisite for the Outlook client to properly handle folder level permissions. For the –ExcludeUsers parameter, consider scenarios where a given application (represented by a “system” account) has been granted folder-level permissions on every mailbox within the organization – you might want to exclude such entries in the output.

The script will then iterate over each of the remaining permission entries and prepare the output. As some changes have been made on the backend, we can now only surface the UserPrincipalName value for the user that has been granted permissions. For groups, this will be the PrimarySmtpAddress value. And if the value returns null string, we consider it an “orphaned” entry. Entries corresponding to externally shared calendars will be parsed to return the email address the folder has been shared with.

The output is stored by default in the $varPermissions global variable, allowing you to immediately reuse it or modify it before exporting to CSV. By default, the output will also be exported to a CSV file within the script directory, if you want to avoid that comment the last line. The screenshot below illustrates how the output file looks like, note the various permission types:

To close off the article, here are some examples on how to invoke the script with various parameters. For example, if you want to obtain a “full” inventory of all mailboxes and include the Default level as well, call the script with the –IncludeAll and –IncludeDefaultPermissions parameters, as follows:

.\Mailbox_Folder_Permissions_inventoryV2.ps1 -IncludeAll -IncludeDefaultPermissions

To exclude permission entries for a specific (set of) users, provide their UPNs as input for the –ExcludeUsers parameter:

.\Mailbox_Folder_Permissions_inventoryV2.ps1 -ExcludeUsers admin@domain.com,serviceaccount@domain.com


Additional examples on the script usage can be found in the built-in help. Lastly, it’s worth mentioning that the script includes basic checks for the presence of the “V2” Exchange Online module and connectivity, but those will hardly account for all scenarios. Just make sure you’ve loaded the V2 module and connected before running the script.

Grab your copy of the script from my GitHub repo.

This entry was posted in Exchange Online, Microsoft 365, Office 365, PowerShell. Bookmark the permalink.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.