How do you keep track of mailbox permissions in Exchange Online? Adding new permissions is easy and done quickly, but removing obsolete permissions is often forgotten. To keep track we want to get a report with all mailbox folder permissions.
Mailbox permissions can be set by the Exchange Admin or by the users themself. This makes managing and keeping track of the permissions a real challenge. We will need to gather the permissions from different places and combine them into one clear overview.
With the help of PowerShell, we can easily get the mailbox folder permissions for each user and generate a nice CSV report from it.
The PowerShell script below exports the following data from user and shared mailboxes to a CSV file:
- Display name
- Primary Emailadres
- Mailbox type
- Read and Manage Permissions
- Send as Permissions
- Send on behald
- Inbox folder permissions (displayname and permission level)
- Calendar permissions (displayname and permission level)
As always, I will first explain the script and different options to you. So you understand how the script works. At the end of the article, you will find a link to the script in my GitHub repository.
Mailbox Permissions
Before we start with the report script, I first want to explain to you where all the different permissions are coming from. Because to get a complete overview of all the mailbox permissions we need to get the permissions from 4 different places.
We have the following permissions that we want to list:
- Read and manage permissions
- Send as permissions
- Send on behalf of permissions
- Inbox folder permissions
- Calendar folder permissions
The first 3 permissions can easily be set and viewed through the Microsoft 365 Admin Center, but for the folder permissions, you will need to use the get mailbox folder permissions cmdlet in PowerShell.
Read and Manage permissions
Read and manage are basically full access permissions. To view all users that have access to the mailbox we will need to use the cmdlet Get-EXOMailboxPermissions
. We want to make sure that we don’t list the default system permissions, so we filter them out.
Get-EXOMailboxPermission -Identity <userprincipalname>| where { -not ($_.User -match "NT AUTHORITY") -and ($_.IsInherited -eq $false)} | ft
Send as Permissions
To view the send as permission in PowerShell we will need to use the Get-EXORecipientPermissions cmdlet. Also here we want to filter out the default permissions.
Get-EXORecipientPermission -Identity <userprincipalname> | where { -not ($_.Trustee -match "NT AUTHORITY") -and ($_.IsInherited -eq $false)} | ft
Send on Behalf permissions
The send on behalf permissions is part of the Get-EXOMailbox
cmdlet. You will have to specify the property GrantSendOnBehalfTo
to view the permissions
Get-EXOMailbox -Identity <userprincipalname> -Properties GrantSendOnBehalfTo | select UserPrincipalName, DisplayName, PrimarySMTPAddress, GrantSendOnBehalfTo, ForwardingSMTPAddress | ft
Get Mailbox Folder Permissions
The last permissions that we want to list in our report are the folder permissions. These are a bit tricky because we will need to give up the correct folder names. For most of you, these will be the default English folder names, like inbox and calendar.
But in Dutch, it’s for example “Postvak in” and “Agenda”. If you are not sure what the correct folder names are in your tenant, then list all the folders of a single mailbox:
Get-EXOMailboxFolderStatistics -identity <userprincipalname> | ft
If you have found the correct folder names for your tenant we can use the following PowerShell cmdlet to get the mailbox folder permissions
$identity = <userprincipalname> $folder = Inbox # Or Calendar for example Get-EXOMailboxFolderPermission -Identity "$($identity):\$($folder)" | where { -not ($_.User -match "Default") -and -not ($_.AccessRights -match "None")}
So as you can see to get all the mailbox permissions we will need to look into different places and combine all the data.
How to use the Mailbox Permission Script
Getting all mailbox permissions in a larger tenant can take some time because we need to get the permissions from 4 different places. So I have added a couple of parameters to the script so you can fine-tune the report to your needs.
To run the script you will always need to enter your email address for authentication with the -adminUPN
parameter. Without any other parameter, the script will generate a report with:
- All mailboxes (shared and user)
- Include inbox and calendar folder permisssions
- Get display names for the permissions
- Store the report in the script root location ( .\mailboxsizereport-nov-30-2021.csv)
.\MailboxPermissionReport.ps1 -adminUPN [email protected]
As you can see the script will show a progress bar based on the number of mailboxes its processing.
Get Shared Mailbox Permissions
There are a couple of options when it comes to shared mailboxes. By default, they are included in the report. But you can also get only the shared mailboxes permissions or leave them out of the PowerShell report with the parameter -sharedMailboxes
:
# Get only the shared mailboxes .\MailboxPermissionReport.ps1 -adminUPN [email protected] -sharedMailboxes only # Get only the user mailboxes .\MailboxPermissionReport.ps1 -adminUPN [email protected] -sharedMailboxes no # (Default) Get user and shared mailboxes .\MailboxPermissionReport.ps1 -adminUPN [email protected] -sharedMailboxes include
Mailbox Folder Permissions
By default, the script will list all the mailbox folder permissions. These are the permissions that apply only to the calendar or inbox folder for example. Especially calendar is a mailbox folder where a lot of permissions are applied.
But as mentioned, each request takes extra time. So if are not interested in the mailbox folder permissions you can leave them out with the parameter -folderPermissions
:
.\MailboxPermissionReport.ps1 -adminUPN [email protected] -folderPermissions:$false
The columns are still listed in Excel though, but they are all empty.
Get Permissions for a Single or Selection of Users
You can also request only the mailbox permissions of a single mailbox or a selection of mailboxes. This allows you to check only the mailbox permission of the management mailboxes for example:
.\MailboxPermissionReport.ps1 -adminUPN [email protected] -UserPrincipalName [email protected],[email protected],[email protected]
You can use any of the following options for the UserPrincipalName parameter, as long as the value uniquely identifies the mailbox:
- User ID or user principal name (UPN)
- Name
- Alias
- Distinguished name (DN)
- Domain\Username
- Email address
Use CSV File with Users
I have added the option to use a CSV file with users from that you want to get the mailbox permissions. This way you can easily get the permissions from only a selection of users without the need to type all the userspricipalnames in the command line.
.\MailboxPermissionReport.ps1 -adminUPN [email protected] -csvFile "c:\temp\csvfile.csv"
Make sure that the CSV file is comma-separated, has headings, and contains a field UserPrincipalName. For example:
UserPrincipalName,Display Name [email protected],Adele Vance [email protected],Grady Archie [email protected],Joni Sherman
Show Display Names or Not
The different mailbox permissions cmdlets that we use in the script all return the userprincipal names of the users that have permissions. For example, if we look up the Send as permissions, the users that have to send as permissions are listed in the Trustee column.
Listing all users as [email protected] in your report doesn’t really make it readable. So by default, we will look up the display name of each user. But that takes a bit of extra time.
If you want to run the report against a large group of users, you might want to disable this feature. What the script then does, is remove that domain part from the username, so you get only GradyA in your report for example.
.\MailboxPermissionReport.ps1 -adminUPN [email protected] -displayNames:$false
CSV Export Path
You can also specify the location and file name of the output file with the -CSVpath
parameter. By default, the script will export the result to the console. But if you specify the CSV path, then the script creates a CSV Export.
.\MailboxPermissionReport.ps1 -adminUPN [email protected] -CSVpath c:\temp\permission.csv
PowerShell Get Mailbox Permissions Script
To get the mailbox permissions with PowerShell we will need to use the Exchange Online module. We are going to use the Exchange Online v2 module because it supports MFA and SSON. This way you only have the enter your email address to connect to Exchange Online.
The script will check if the module is installed and give you the option to install it when it’s not available on your computer. We also check for any existing connections, to prevent multiple connections from the same computer.
Get the mailboxes
So the first part of the script is to get the mailboxes. If the parameter -userPrincipalName
is set, then we will get every mailbox listed and add them to an array. Otherwise, we will get all mailboxes, including shared or not depending on the -sharedMailboxes
parameter.
# Check which mailboxes to get if ($UserPrincipalName) { Write-Host "Collecting mailboxes" -ForegroundColor Cyan $mailboxes = @() # Get the requested mailboxes foreach ($user in $UserPrincipalName) { Write-Host "- Get mailbox $user" -ForegroundColor Cyan $mailboxes += Get-SingleUser -identity $user } }else{ Write-Host "Collecting mailboxes" -ForegroundColor Cyan $mailboxes = Get-Mailboxes } # Function Get-SingleUser gets mailbox based on identity Get-EXOMailbox -Identity $identity -Properties GrantSendOnBehalfTo, ForwardingSMTPAddress | select UserPrincipalName, DisplayName, PrimarySMTPAddress, RecipientType, RecipientTypeDetails, GrantSendOnBehalfTo, ForwardingSMTPAddress # Function Get-Mailboxes gets all mailboxes based on shared mailbox parameter switch ($sharedMailboxes) { "include" {$mailboxTypes = "UserMailbox,SharedMailbox"} "only" {$mailboxTypes = "SharedMailbox"} "no" {$mailboxTypes = "UserMailbox"} } Get-EXOMailbox -ResultSize unlimited -RecipientTypeDetails $mailboxTypes -Properties GrantSendOnBehalfTo, ForwardingSMTPAddress| select UserPrincipalName, DisplayName, PrimarySMTPAddress, RecipientType, RecipientTypeDetails, GrantSendOnBehalfTo, ForwardingSMTPAddress
Collecting permissions
The next step is to get permissions for each mailbox. Each function will look up the permissions, using the functions described in the beginning, and return an array with the users
# Collect permissions for each mailbox $mailboxes | ForEach { # Get Send on Behalf Permissions $sendOnbehalfUsers = Get-SendOnBehalf -mailbox $_ # Get Fullaccess Permissions $fullAccessUsers = Get-FullAccessPermissions -identity $_.UserPrincipalName # Get Send as Permissions $sendAsUsers = Get-SendAsPermissions -identity $_.UserPrincipalName }
Depending on the parameter folderPermissions we will also get the mailbox folder permissions:
if ($folderPermissions.IsPresent) { # Get Inbox folder permission $inboxFolder = Get-FolderPermissions -identity $_.UserPrincipalName -folder $inboxFolderName $ib = $inboxFolder.users.Count # Get Calendar permissions $calendarFolder = Get-FolderPermissions -identity $_.UserPrincipalName -folder $calendarFolderName $ca = $calendarFolder.users.Count }
For the folder permissions, we need to know the correct folder names. At the beginning of the script, we set 2 variables with the correct names:
$inboxFolderName = "inbox" # Default "inbox" $calendarFolderName = "calendar" # Default "calendar"
Creating the CSV Export
For the CSV export, I wanted to list each user that has permission to a mailbox on its own row, with the correct indentation. So to do this, we need to know how many sub rows we are going to add and which array with permissions is the longest.
So we count each array and then use a simple if-else function to figure out which are larger.
# Count number or records $sob = $sendOnbehalfUsers.Count $fa = $fullAccessUsers.Count $sa = $sendAsUsers.Count # $ib and $ca are set in the folder permissions $mostRecords = Find-LargestValue -sob $sob -fa $fa -sa $sa -ib $ib -ca $ca # Find Largest Values function: Function Find-LargestValue { <# .SYNOPSIS Find the value with the most records #> param( [Parameter(Mandatory = $true)]$sob, [Parameter(Mandatory = $true)]$fa, [Parameter(Mandatory = $true)]$sa, [Parameter(Mandatory = $true)]$ib, [Parameter(Mandatory = $true)]$ca ) if ($sob -gt $fa -and $sob -gt $sa -and $sob -gt $ib -and $sob -gt $ca) {return $sob} elseif ($fa -gt $sa -and $fa -gt $ib -and $fa -gt $ca) {return $fa} elseif ($sa -gt $ib -and $sa -gt $ca) {return $sa} elseif ($ib -gt $ca) {return $ib} else {return $ca} }
If we know how many sub-records we need to create, we can loop through them with a do-while loop and create a PSCustomObject with all the data.
Do{ if ($x -eq 0) { [pscustomobject]@{ "Display Name" = $_.DisplayName "Emailaddress" = $_.PrimarySMTPAddress "Mailbox type" = $_.RecipientTypeDetails "Read and manage" = @($fullAccessUsers)[$x] "Send as" = @($sendAsUsers)[$x] "Send on behalf" = @($sendOnbehalfUsers)[$x] "Inbox folder" = @($inboxFolder.users)[$x] "Inbox folder Permission" = @($inboxFolder.permission)[$x] "Inbox folder Delegated" = @($inboxFolder.delegated)[$x] "Calendar" = @($calendarFolder.users)[$x] "Calendar Permission" = @($calendarFolder.permission)[$x] "Calendar Delegated" = @($calendarFolder.delegated)[$x] } $x++; }else{ [pscustomobject]@{ "Display Name" = '' "Emailaddress" = '' "Mailbox type" = '' "Read and manage" = @($fullAccessUsers)[$x] "Send as" = @($sendAsUsers)[$x] "Send on behalf" = @($sendOnbehalfUsers)[$x] "Inbox folder" = @($inboxFolder.users)[$x] "Inbox folder Permission" = @($inboxFolder.permission)[$x] "Inbox folder Delegated" = @($inboxFolder.delegated)[$x] "Calendar" = @($calendarFolder.users)[$x] "Calendar Permission" = @($calendarFolder.permission)[$x] "Calendar Delegated" = @($calendarFolder.delegated)[$x] } $x++; } $currentUser = $_.DisplayName if ($mailboxes.Count -gt 1) { Write-Progress -Activity "Collecting mailbox permissions" -Status "Current Count: $i" -PercentComplete (($i / $mailboxes.Count) * 100) -CurrentOperation "Processing mailbox: $currentUser" } } while($x -ne $mostRecords)
Download the Complete Mailbox Permissions Script
You can download the complete script here from my GitHub page. This way you will have always the latest version of the script. Always test the script first with a small set of users, using the -UserPrincipalName
parameter.
If you want to know how you can run a PowerShell script, then make sure you read this article.
Wrapping Up
To run the script easily from the command line, you can also add the script (or folder) to your PowerShell profile. Read all about it in this article.
I hope you found this script useful, if you have any questions, just drop a comment below.
You may also like one of the following PowerShell report scripts:
Thank Rudy . the script is working perfectly,
I am trying to make this run weekly automate it.
1- Is there a way we can include Client ID,ClientSecret, TenantID
This way I can run it automatically?
You can replace the “Function ConnectTo-EXO” with what you want to use. If you are running the script in an Azure Runbook, then you can use managed identity for the connection. Otherwise you will need to work with the Azure App and certificate.
Thanks for the help ! Currently I have multi users in Full , SendAs, Send on Behalf for each mailbox. Can we make the script show all the users to be exported . Currently it is exporting single user on each Full, SendAs, Send on Behalf. want it to be able to export what ever users in each to be exported .
Example: MailBox1: Full: User1, User2, User3 | SendAs: User4, User5 | Send on Behalf
Export MailBox1:
Full SendAs SendonBehalf
User1 User4
User2 User5
User3
If you export it to CSV, then you should see each user on a row, just like in the excel screenshot at the beginning of the article.
Great script ! I Have Staff “*@staff.domain.edu” and Student email address “*@student.domain.edu” in are online-Exchange mailboxes. is there a way to exclude users that are students “*@student.domain.edu” that the search gets smaller ?
Give this are try:
Replace the Get-EXOMailbox cmdlet in the Function Get-Mailboxes (around line 202) with:
Thankyou this script rocks! now i can use it daling with 60K with this filter down to 2K mailbox search. the only thing I changed to make it work ->
-notcontains “*@student.domain.edu” did not work I switched to -notlike with “*@student.domain.edu worked! Thank you !
Great script! Thanks
Hi Rudy,
Is there a way to expand a groups membership for permissions that are assigned using groups?
Thanks,
Mike
Is there a way to get a report of all mailbox and mailbox folder permissions that a specific user has? The UserPrincipalName parameter only gives me permissions for that person’s mailbox. I’m more looking for something that will tell me what all a user has access to. (All types of mailboxes and folder permissions, everything.)
This script is awesome 🙂
Can the script be updated so that the mailboxes that do not have permissions applied are also added to the list?
This is a great report! Is there a way, by chance, to expand a group’s membership, if that group has permissions to the mailbox in some way?
is there a 1 line command to use with the Get-EXOMailboxPermission or the new *EXO* commands to add a user for full mailbox acccess for all users ?
Get-Mailbox -ResultSize unlimited -Filter “(RecipientTypeDetails -eq ‘UserMailbox’) -and (Alias -ne ‘Admin’)” | Add-MailboxPermission -User [email protected] -AccessRights FullAccess -InheritanceType All
Does not understand the -path attribute to write to CSV files
Sorry, in the last version of the script I changed the -path parameter to -CSVPath. If you specify the CSVPath, then the script will export the results to a CSV file.
Great Script! I suggest also adding the filter “-and -not ($_.User -like “S-1-5-21*)” to filter out accounts that no longer exist. Makes for a cleaner report.
To which part of the script? Thanks
Hey Rudy,
Is there a way to have each line email the respective mailbox owner of who has permissions? But only be for that specific user?
I have tried to fiddle with the script a bit to include this functionality but keep running into issues.
Many thanks for an amazing script!
Hi,
The script is not working for me, has anything changed on the end of Microsoft or am I missing something?
https://github.com/ruudmens/LazyAdmin/issues/7
Hi,
Unfortunately I cannot get the On-Premise working because the -Property GrantSendOnBehalfTo does not exist as a property but as an Select-Object -ExpandProperty GrantSendOnBehalfTo which is hard to incorporate in line 166:
Get-Mailbox -ResultSize unlimited -RecipientTypeDetails $mailboxTypes -Properties GrantSendOnBehalfTo, ForwardingSMTPAddress|
select UserPrincipalName, DisplayName, PrimarySMTPAddress, RecipientType, RecipientTypeDetails, GrantSendOnBehalfTo, ForwardingSMTPAddress
Any ideas?
Cheers,
Timotatty
Hi Rudy,
I am preparing for a 365 migration and would like to get the permission landscape of my *onprem* environment. Any chance you have a similar report, but for onprem exchange? This will really help in organizing the batches. Thank you!
I am unable to test it, but I have created a version of the script that should work on Exchange on-prem: https://github.com/ruudmens/LazyAdmin/tree/master/Exchange
You can run the script on the Exchange server.
Hi Rudy,
I’m running your on-prem version and it is erroring at line 75 where it talks about [string]$sharedMailboxes = “include”,
I’m getting a red line underneath the word “include”,
When I hover over the error in Powershell ISE it says, “The assignment expression is not valid. The input to an assignment operator must be an object that is able to accept assignments, such as a variable or a property”.
Any idea how to fix? I noticed the O365 version doesn’t have a red line under “include”. The parameter looks the same as the on prem version so I’m not sure where the problem lies.
Thanks for your help!
Lee
Have you copy/pasted the script correctly? Line 75 is here
Mandatory = $false,
from the folderpermissions. I also don’t get the error in PowerShell ISE or Visual Studio Code.Is there a way to run this script against a specific OU? so that I am only targeting user mailboxes within an OU? Thanks
You could first get the users with AzureAD, filtered on the OU, and then foreach users get the mailbox with permissions.
Get-AzureADUser -All $true | Where-Object { $_.ExtensionProperty.onPremisesDistinguishedName -like "*OU=Test-Users*" }
Hi Rudy, thanks for getting back to me. Unfortunately I’m still getting the same result:
Collecting mailboxes
– Get mailbox [email protected]
Collecting permissions
Failed to create report
I replaced the actual mailbox UPN with [email protected] above to hide our company details.
Can you email me a screenshot of the PowerShell windows when you run the command?
Great article with a lot of detail! I downloaded your latest version of the script from GitHub but I keep on getting “Failed to create report” when running script. I’ve tried using different users etc but it did not make a difference. Any idea of what the issue could be?
It will throw that error if it’s unable to write the CSV file. Try setting the csv path to c:\temp\permissions.csv, like this:
.\MailboxPermissionReport.ps1 -adminUPN [email protected] -csvFile "c:\temp\permissions.csv"
Thanks so much!!! Very good work!! Made my day!!
Thank you
the next step would be to create an import script from this
for Tenant to Tenant Migrations
Any chance you can add a For each loop that reads a CSV for UPN of users to find permissions on rather than adding it as a parm?
I have updated the script. You can now use the parameter csvFile.