Back to script library
Entra / Microsoft 365 · Exchange Online

Report permissions folder level

Uses Exchange Online Management module (REST).

Connect & set up

Run these once per session. All scopes are read-only unless the script makes changes.

Connect-ExchangeOnline -ShowBanner:$false

Run it

The main script. Copy it, or download the .ps1 and run it from your console.

$Modules = Get-Module | ? { $_.Name -eq "ExchangeOnlineManagement" }
If ($Null -eq $Modules) {
Write-Host "Please connect to the Exchange Online Management module before running this script!"; break }
# Find mailboxes
$Mbx = Get-ExoMailbox -RecipientTypeDetails UserMailbox -ResultSize Unlimited | Select DisplayName, ExternalDirectoryObjectId, UserPrincipalName, Name
$Report = [System.Collections.Generic.List[Object]]::new()
$ProgressDelta = 100/($Mbx.count); $PercentComplete = 0; $MbxNumber = 0
# Loop through mailboxes to fetch folders and procvess them
ForEach ($M in $Mbx) {
$MbxNumber++
$MbxStatus = $M.DisplayName + " ["+ $MbxNumber +"/" + $Mbx.Count + "]"
Write-Progress -Activity "Processing mailbox" -Status $MbxStatus -PercentComplete $PercentComplete
$PercentComplete += $ProgressDelta
# Get Folders to check - not interested in system folders and focusing on folders that are usually delegated
# We also don't check empty folders...
$Folders = Get-ExoMailboxFolderStatistics -Identity $M.ExternalDirectoryObjectId | ? {$_.FolderType -eq "User Created" -or $_.FolderType -eq "Inbox" -or $_.FolderType -eq "SentItems" -or $_.FolderType -eq "Contacts" -or $_.FolderType -eq "Calendar" -and $_.ItemsInFolder -gt 0 } | Select Name, itemsinfolder
# Loop through folder to extract pernmissions for each one
ForEach ($Folder in $Folders) {
$FolderName = $M.Name + ":\" + $Folder.Name
$Permissions = (Get-ExoMailboxFolderPermission -Identity $FolderName -ErrorAction SilentlyContinue)
ForEach ($Permission in $Permissions) { # Check each permission, ignoring ones we don't have any interest in
$User = $Permission.User
If (($User -ne "Default" -and $User -ne "Anonymous") -and ($User -ne $M.DisplayName -and $Permission.AccessRights -ne "None")) {
$i = 1
ForEach ($Access in $Permission.AccessRights) { # Break up the array of access rights into a formatted string for the report
If ($i -eq 1) { $AccessRights = $Access; $i++ } Else {$AccessRights = $AccessRights + "; " + $Access }}
$ReportLine = [PSCustomObject]@{
Mailbox = $M.DisplayName
Folder = $Folder.Name
Permission = $AccessRights
Assignedto = $User }
$Report.Add($ReportLine) }}
# Debug line -uncomment if you want to see these messages
# Write-Host "Mailbox:" $M.Name "Permission" $Permission.AccessRights "on" $Folder.Name "for" $User
}}
$Report | Export-CSV -NoTypeInformation c:\temp\FolderDelegatedPermissions.CSV
CLS
Write-Host "Finished processing" $Mbx.Count "mailboxes. The report file is in c:\temp\FolderDelegatedPermissions.CSV"
Attribution