PowerShell script to enumerate ACL entries on a Shared Mailbox

Recently I was asked by a friend to help with identifying all the users who had access to shared mailboxes. This colleague already new about the Get-MailboxPermission cmdlet, and of course how to leverage the Foreach statement for items in a collection. What he needed however was to be able to expand the ACLs that include a nested groups. The origin of this request has to do with a Shared Mailbox limitation:

Too many users: When there are too many designated users concurrently accessing a shared mailbox (the max supported is 25), they may intermittently fail to connect to this mailbox or have inconsistencies like messages being duplicated in the outbox. In this case, reduce the number of users or use a different workload, like a Microsoft 365 group.

Here’s the script we came up with:

$csvlist = Import-csv c:\certs\File.csv
foreach ($csv in $csvlist)
{
    $mailBoxName = $csv.Name
    $completeInfo = @()
    $obj = New-Object psobject
    $data = Get-MailboxPermission $mailBoxName
    $users =  $data | Where-Object {$_.User -match 'SELF|@'}
    $groups = $data | Where-Object {$_.User -notmatch 'SELF|@'}

    if ($users -ne $null)
    {
        foreach ($u in $users)
        {
            $obj = New-Object psobject
            $obj | Add-Member -Name User -MemberType NoteProperty -Value $u.User
            $obj | Add-Member -Name Type -MemberType NoteProperty -Value 'MailUser/UserMailBox'
            $obj | Add-Member -Name Level -MemberType NoteProperty -Value 0
            $obj | Add-Member -Name UpGroup -MemberType NoteProperty -Value 'NA'
            $completeInfo += $obj
        }
    }

    if ($groups -ne $null)
    {
        $allDgMembers = @()
        $mailUSGroups = $groups | foreach {
            $obj = New-Object psobject
            $obj | Add-Member -Name User -MemberType NoteProperty -Value $_.User
            $obj | Add-Member -Name Type -MemberType NoteProperty -Value 'MailUniversalSecurityGroup'
            $obj | Add-Member -Name Level -MemberType NoteProperty -Value 0
            $obj | Add-Member -Name UpGroup -MemberType NoteProperty -Value 'NA'
            $completeInfo += $obj

            $allDgMembers += Get-DistributionGroupMember $_.User
            $allDgMembers | Add-Member -Name UpGroupInfo -MemberType NoteProperty -Value $_.User -ErrorAction SilentlyContinue
        }

        $allDgMembers | foreach {
            $obj = New-Object psobject
            $obj | Add-Member -Name User -MemberType NoteProperty -Value $_.Name
            $obj | Add-Member -Name Type -MemberType NoteProperty -Value $_.RecipientType
            $obj | Add-Member -Name Level -MemberType NoteProperty -Value 1
            $obj | Add-Member -Name UpGroup -MemberType NoteProperty -Value $_.UpGroupInfo
            $completeInfo += $obj
        }
   
        $dgmembers = $allDgMembers | Where-Object {$_.RecipientType -eq 'MailUniversalSecurityGroup'}


        $subUsers = @()
        $dgmembers | foreach {
            $subUsers += Get-DistributionGroupMember $_.Name
            $subUsers | Add-Member -Name UpGroupInfo -MemberType NoteProperty -Value $_.Name -ErrorAction SilentlyContinue
        }

        $subUsers | foreach  {
            $obj = New-Object psobject
            $obj | Add-Member -Name User -MemberType NoteProperty -Value $_.Name
            $obj | Add-Member -Name Type -MemberType NoteProperty -Value $_.RecipientType
            $obj | Add-Member -Name Level -MemberType NoteProperty -Value 2
            $obj | Add-Member -Name UpGroup -MemberType NoteProperty -Value $_.UpGroupInfo
            $completeInfo += $obj
        }
    }

    #$completeInfo | ft
    $completeInfo | Export-Csv "c:\certs\$($csv.Name).csv" -Notypeinformation
}

This exports each shared mailbox in the csv file as individual files in the output directory.

The file.csv contains the list of the shared mailboxes and after running the script above

It creates the three files in the previous screenshot. Opening one of the files reveals the ACL list for the Family shared mailbox. The Level column helps identify if it was directly added or nested. In this example, there were three levels.

So what happens if you had say 50,000 mailboxes and you needed to retrieve the number of permission entries in each one? The familiar Get-ChildItem and Get-Content did the trick with this piece of code:

Get-ChildItem -Path C:\certs\ *.csv |

    Select-Object -Property @(

        'FullName'

         @{ Name = "LineCount"; Expression = {

             @(Get-Content -Path $_.FullName).Length

         }}

    ) | Export-Csv C:\certs\output.csv -NoTypeInformation

The code is self-explanatory. Here, we see the number of items in the Family.csv – 36, which matches the number we got in our earlier screenshot. By reviewing this list, you can match it against problem mailboxes and use it to advise users on better working practices.

Till next time.

Adios.