Get-EXOMailboxPermission issues

Roman King 26 Reputation points
2024-10-04T14:13:23.2533333+00:00

Hi all

I have a script that goes through all mailboxes (about 6k) in our org and collects access data.

Then, based on this data, we can find out which user has access to what mailboxes. To speedup the process, I run 48 streams in parallel. For some reason, I receive JUST ONE error message that states: The 'Get-EXOMailboxPermission' command was found in the module 'ExchangeOnlineManagement', but the module could not be loaded due to the following error: [Collection was modified; enumeration operation may not execute.]

For more information, run 'Import-Module ExchangeOnlineManagement'.

Here is the function, partially:


function Get-MailboxesAccess {
    param (
        [parameter(Mandatory = $true)][String]$TargetUser
    )
    

    $TargetUser = $TargetUser.Trim()
    if ($TargetUser -notlike "*@*") { $TargetUser = $TargetUser + "@blood.ca" }       
    
    Write-Host "Looking for mailboxes.." -ForegroundColor Cyan
    $mailboxes = Get-EXOMailbox -ResultSize Unlimited 
    Write-Host "Total mailboxes to proces: $($mailboxes.Count)" -ForegroundColor Cyan
    if (!$mailboxes) {
        Write-Host "No mailboxes found, nothing to process"
        Break
    }


    $job = $mailboxes | Foreach-Object -ThrottleLimit 48 -Parallel {
        function Get-FullAccessPermissions {
            # This function collects FullAccess rights
            param (
                [Parameter(Mandatory = $true)][string]$Mailbox
            )
    
            $result = Get-EXOMailboxPermission -Identity $Mailbox -ErrorAction SilentlyContinue | Where-Object { $_.AccessRights -match "FullAccess" -and $_.User -ne "NT AUTHORITY\SELF" } | Select-Object User
            return $($result.User)
        }
        function Get-SendAsPermissions {
            # This function collects SendAs rights
            param (
                [Parameter(Mandatory = $true)][string]$Mailbox
            )
    
            $result = Get-EXORecipientPermission -Identity $Mailbox -ErrorAction SilentlyContinue | Where-Object { $_.AccessRights -match "SendAs" -and $_.Trustee -ne "NT AUTHORITY\SELF" } | Select-Object Trustee
            return $($result.Trustee)
        }   
        function Get-SendOnBehalfPermissions {
            # This function collects SoB rights
            param (
                [Parameter(Mandatory = $true)][string]$Mailbox
            )
            try {
                $result = Get-EXOMailbox $Mailbox -Properties GrantSendOnBehalfTo | Select-Object -ExpandProperty GrantSendOnBehalfTo | Get-EXOMailbox -ErrorAction SilentlyContinue | Select-Object UserPrincipalName
                return $($result.UserPrincipalName)
            }
            catch {
                return $null
            }
        }

        # An array of data is simple 
        # In case we will require a JSON file that is simpler to process, we will uncomment those three lines in the middle
        $accessDetails = [PSCustomObject]@{
            "Mailbox"              = $_.UserPrincipalName
            "RecipientType"        = $_.RecipientType
            "RecipientTypeDetails" = $_.RecipientTypeDetails
            #"FullAccess" = Get-FullAccessPermissions -Mailbox $_.UserPrincipalName
            #"SendAs" = Get-SendAsPermissions -Mailbox $_.UserPrincipalName
            #"SoB" = Get-SendOnBehalfPermissions -Mailbox $_.UserPrincipalName
            "FullAccess"           = (Get-FullAccessPermissions -Mailbox $_.UserPrincipalName) -join "; "
            "SendAs"               = (Get-SendAsPermissions -Mailbox $_.UserPrincipalName) -join "; "
            "SoB"                  = (Get-SendOnBehalfPermissions -Mailbox $_.UserPrincipalName) -join "; "
        }
        
        $report += $accessDetails    
        return $report
        
    } -AsJob

Exchange Online
Exchange Online
A cloud-based service included in Microsoft 365, delivering scalable messaging and collaboration features with simplified management and automatic updates.
Windows for business | Windows Server | User experience | PowerShell
{count} votes

2 answers

Sort by: Most helpful
  1. Anonymous
    2024-10-07T03:12:54.85+00:00

    Hi, @Roman King

    It sounds like your problem is in a script, please understand that the Exchange Online tag is not focused on script issues at this time. Since you have added the Power Shell tag, you can wait for more professional guidance from them.

    From my personal point of view, you can try the following suggestions:

    1. Use a for loop to iterate over the collection. This prevents the enumerator from throwing an exception when the collection is changed.
    2. Create a separate collection to store the threads to be aborted.
    3. Load 'Import-Module ExchangeOnlineManagement' at the beginning of the script
    4. Change parallel processing to serial processing, it may avoid the problem of concurrent modifications.
    5. Move the permission acquisition functions outside of the main script body to ensure they are available across the scope of the script.

    If the answer is helpful, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".


  2. Rich Matheisen 48,026 Reputation points
    2024-10-07T19:28:11.1766667+00:00

    Is there a reason you're using the "-AsJob" parameter?

    Using the Foreach-Object with the -Parallel switch causes each iteration to run in its own runspace. Shouldn't that be sufficient?

    Also, when you use the -AsJob, the Foreach-Object returns a job object, not the output from the success stream.

    Runspaces, and jobs, consume a significant amount of memory and time (to set up and tear down). You might benefit from reusing runspaces by pooling them instead of creating/destroying 6,000 of them!

    Also, each of your interior functions use this pattern:

    . . . | Where-Object { $_.AccessRights -match "SendAs" -and $_.Trustee -ne "NT AUTHORITY\SELF" } | Select-Object Trustee
    return $($result.Trustee)
    

    That "Select-Object" is creating a PSCustomObject. But all you seem to want is the text value of the property. Try using . . . | Select-Object -Expand <propertyname> to get an array (multiple mailboxes may have the same permission on the target mailbox) of strings instead of an array of PSCustomObjects.


Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.