How to Get Mailbox Permissions with PowerShell – including CSV Report

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)
permissions report
Example of Mailbox Permissions report

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
Office 365 admin center

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
get mailbox calendar permissions
Get mailbox folders

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 john@contoso.com
Get mailbox permissions
Get mailbox permissions with PowerShell

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 john@contoso.com -sharedMailboxes only

# Get only the user mailboxes
.\MailboxPermissionReport.ps1 -adminUPN john@contoso.com -sharedMailboxes no

# (Default) Get user and shared mailboxes
.\MailboxPermissionReport.ps1 -adminUPN john@contoso.com -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 john@contoso.com -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 john@contoso.com -UserPrincipalName jane@contoso.com,alex@contoso.com,adele@contoso.com

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 john@contoso.com -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
AdeleV@lazydev.onmicrosoft.com,Adele Vance
GradyA@lazydev.onmicrosoft.com,Grady Archie
JoniS@lazydev.onmicrosoft.com,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.

Get Mailbox Folder Permissions

Listing all users as GradyA@lazydev.onmicrosoft.com 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 john@contoso.com -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 john@contoso.com -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:

38 thoughts on “How to Get Mailbox Permissions with PowerShell – including CSV Report”

  1. Hello Rudy,

    It’s a great script but I have a request.
    How can you set that every line has the info about Display Name,Emailaddress and Mailbox type.
    When every column has data, I can filter on each column in Excel.
    For example see on which mailbox an user has Send as right.

    Thanks for your support.
    regards,
    Frank

  2. 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.

  3. 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

  4. 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:

          Get-EXOMailbox -ResultSize unlimited -RecipientTypeDetails $mailboxTypes -Properties GrantSendOnBehalfTo, ForwardingSMTPAddress | 
            Select-Object UserPrincipalName, DisplayName, PrimarySMTPAddress, RecipientType, RecipientTypeDetails, GrantSendOnBehalfTo, ForwardingSMTPAddress
            | Where-Object {$_.PrimarySMTPAddress -notcontains "@student.domain.edu"}
      
      • 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 !

  5. Hi Rudy,
    Is there a way to expand a groups membership for permissions that are assigned using groups?
    Thanks,
    Mike

  6. 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.)

  7. 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?

  8. 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 admin@contoso.com -AccessRights FullAccess -InheritanceType All

    • 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.

  9. 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.

  10. 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!

  11. 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

  12. 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!

      • 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.

  13. 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*" }

  14. Hi Rudy, thanks for getting back to me. Unfortunately I’m still getting the same result:

    Collecting mailboxes
    – Get mailbox john@contoso.com
    Collecting permissions
    Failed to create report

    I replaced the actual mailbox UPN with john@contoso.com above to hide our company details.

  15. 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 john@contoso.com -csvFile "c:\temp\permissions.csv"

  16. 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?

Leave a Comment

0 Shares
Tweet
Pin
Share
Share