Entra / Microsoft 365 · Exchange Online
Report mailbox rights assignments
Quick script to find audit records for rights assigned to mailboxes to allow us to notify the mailbox owner.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-MgGraph -Scope "Mail.Send, Mail.ReadWrite"
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
param([int] $LookbackDays = 30,[string] $StartDate = (Get-Date).AddDays(-$LookbackDays); $EndDate = (Get-Date),[string] $EndDate = (Get-Date))$ModulesLoaded = Get-Module | Select-Object NameIf (!($ModulesLoaded -match "ExchangeOnlineManagement")) {Write-Host "Please connect to the Exchange Online management module and then restart the script"; break}# Make sure that we have valid credentialsIf (-not $O365Cred) { #Make sure we have credentials$O365Cred = (Get-Credential)}# Message is from the logged in account - we're going to compare email addresses later, so make sure that we know the primary SMTP address of the account# rather than just its user principal name (used to sign in).[string]$MsgFrom = (Get-ExoMailbox -Identity $O365Cred.UserName).PrimarySmtpAddress$MsgSubject = "Notification of permission change to your mailbox"#HTML header with styles$HtmlHead="<html><style>BODY{font-family: Arial; font-size: 10pt;}H1{font-size: 22px;}H2{font-size: 18px; padding-top: 10px;}H3{font-size: 16px; padding-top: 8px;}</style>"# Find the audit records for the last 30 daysWrite-Host "Searching for audit records for mailbox permission update events..."[array]$Records = Search-UnifiedAuditLog -Operations Add-MailboxPermission, Add-RecipientPermission -StartDate $StartDate -EndDate $EndDate -Formatted -ResultSize 5000# If we find some records, process themIf (!$Records) { Write-Host "No audit records for mailbox permissions found."; break }$Records = $Records | Where-Object {$_.UserIds -ne "NT AUTHORITY\SYSTEM (Microsoft.Exchange.Servicehost)"}Write-Host $Records.Count "audit records found..."$Report = [System.Collections.Generic.List[Object]]::new()ForEach ($Rec in $Records) {$AuditData = $Rec.AuditData | ConvertFrom-Json$TargetMailbox = $AuditData.ObjectId$Admin = $AuditData.UserId$TrusteeDisplayName = $Null; $MailboxType = $Null; $MailboxType = $Null; $UserDetailsEmail = $Null; $Access = $Null$TrusteeDetails = Get-ExoMailbox -Identity $Trustee -ErrorAction SilentlyContinue -IncludeInactiveMailbox$UserDetails = Get-ExoMailbox -Identity $TargetDN -ErrorAction SilentlyContinue -IncludeInactiveMailboxIf ($TrusteeDetails -eq $Null) {$TrusteeDetailsName = $Trustee } Else {$TrusteeDetailsName = $TrusteeDetails.DisplayName}Switch ($Rec.Operations) { # Set up data for either a mailbox permission or recipient permission operation"Add-MailboxPermission" {$TargetDN = $AuditData.Parameters | ? {$_.Name -eq "Identity"} | Select-Object -ExpandProperty Value$Trustee = $AuditData.Parameters | ? {$_.Name -eq "User"} | Select-Object -ExpandProperty Value[string]$Access = $AuditData.Parameters | ? {$_.Name -eq "AccessRights"} | Select-Object -ExpandProperty Value$FullAccessList = Get-ExoMailboxPermission -Identity $UserDetails.PrimarySmtpAddress | ? {$_.User -ne "NT AUTHORITY\SELF"} | Select User, AccessRights}"Add-RecipientPermission" {$TargetDN = $AuditData.Parameters | ? {$_.Name -eq "Identity"} | Select-Object -ExpandProperty Value$Trustee = $AuditData.Parameters | ? {$_.Name -eq "Trustee"} | Select-Object -ExpandProperty Value[string]$Access = $AuditData.Parameters | ? {$_.Name -eq "AccessRights"} | Select-Object -ExpandProperty Value$FullAccessList = Get-ExoRecipientPermission -Identity $UserDetails.PrimarySmtpaddress | ? {$_.Trustee -ne "NT AUTHORITY\SELF"} | Select Trustee, AccessRights}} #End SwitchIf ($UserDetails -eq $Null) {$UserDetailsName = $TargetDN} Else {$UserDetailsName = $UserDetails.DisplayName$UserDetailsEmail = $UserDetails.PrimarySmtpAddress$FullAccessReport = [System.Collections.Generic.List[Object]]::new()ForEach ($AccessPermission in $FullAccessList) {[string]$AccessRightsGranted = $AccessPermission.AccessRightsSwitch ($Rec.Operations) {"Add-MailboxPermission" {$ReportLine = [PSCustomObject][Ordered]@{User = $AccessPermission.UserAccess = $AccessRightsGranted }$FullAccessReport.Add($ReportLine)}"Add-RecipientPermission" {$ReportLine = [PSCustomObject][Ordered]@{User = $AccessPermission.TrusteeAccess = $AccessRightsGranted }$FullAccessReport.Add($ReportLine)}} #end Switch} #End Foreach access permission} #End ifIf ($UserDetails.RecipientTypeDetails -eq $Null) { $MailboxType = "Unknown"}$ReportLine = [PSCustomObject][Ordered]@{TargetMailbox = $UserDetailsNameMailboxType = $UserDetails.RecipientTypeDetailsTargetEmail = $UserDetailsEmailAccessGranted = $AccessGrantedOn = $Rec.CreationDateGrantedTo = $TrusteeDetailsNameGrantedBy = $AdminOperation = $Rec.OperationsFullAccessList = $FullAccessReport} # End ReportLine$Report.Add($ReportLine)} #End ForEach $Records# We now have a set of records for adding mailbox permissions. Before we start to send email, discard amy# records for deleted mailboxes where we don't have an email address and shared mailboxes. We should be left with# a set of permission changes made to user mailboxes.[array]$EmailUsers = $Report | Where-Object {$_.TargetEmail -ne $Null -and $_.MailboxType -eq "UserMailbox"} | Sort-object TargetMailbox# Drop messages to the account sending email as there's no point in telling them something they already know[array]$EmailUsers = $EmailUsers | Where-Object {$_.TargetEmail -ne $MsgFrom}If (!($EmailUsers)) { Write-Host "No notifications found to send after analyzing audit records - exiting"; break}# Connect to the Microsoft Graph SDK for PowerShellWrite-Host "Connecting to the Microsoft Graph"Connect-MgGraph -Scope "Mail.Send, Mail.ReadWrite"ForEach ($User in $EmailUsers) {Write-Host "Sending notification email to" $User.TargetMailbox# Add the recipient using the mailbox's primary SMTP address$EmailAddress = @{address = $User.TargetEmail}$EmailRecipient = @{EmailAddress = $EmailAddress}# Customize the message$ChangeDate = Get-Date($User.GrantedOn) -format rSwitch ($User.AccessGranted) {"FullAccess" { $PermissionText = "Full Access permission grants $($User.GrantedTo) access to all items in your mailbox" }"SendAs" { $PermissionText = "Send As permission allows $($User.GrantedTo) to impersonate you and send email as if the messages come from you." }}# Message body$HtmlContent = "<body><html><h1>Notification of mailbox permission change made to your mailbox</h1><h2><u>Please contact the Help Desk if this permission update is not approved by you</u></h2><p><b><u>Details of Permission Change</b></u></p><p><strong>Change made on: </strong> $($ChangeDate)</p><p><strong>Permission added: </strong> $($User.AccessGranted)</p><p><strong>Granted to: </strong> $($User.GrantedTo)</p><p><i>$($PermissionText)</i></p><p><strong>Permission added by: </strong> $($User.GrantedBy)</p></body></html></p><p><p><strong>Current set of users with $($User.AccessGranted) permission for the mailbox</strong></p><p>$($User.FullAccessList.User -join ", ")</p>"$HtmlMsg = $HtmlHead + $HtmlContent# Construct the message body$MessageBody = @{content = "$($HtmlMsg)"ContentType = 'html'}# Create a draft message in the signed-in user's mailbox$NewMessage = New-MgUserMessage -UserId $MsgFrom -Body $MessageBody -ToRecipients $EmailRecipient -Subject $MsgSubject# Send the messageSend-MgUserMessage -UserId $MsgFrom -MessageId $NewMessage.Id} # End ForEach UserWrite-Host "All done. Messages sent!"
Parameters
ParameterDefaultNotes
-LookbackDays30Number of days back to search the unified audit log.-StartDate(Get-Date).AddDays(-30); $EndDate = (Get-Date)Start of the reporting window.-EndDate(Get-Date)End of the reporting window.Attribution
Author
Office365itpros