public/get-ExOPermissions.ps1
Function get-ExOPermissions{ <# Author = "Jos Lieben (jos@lieben.nu)" CompanyName = "Lieben Consultancy" Copyright = "https://www.lieben.nu/liebensraum/commercial-use/" Parameters: -expandGroups: if set, group memberships will be expanded to individual users -maxDetail: if set, folder level permissions for each mailbox will be retrieved. This can be (very) slow -outputFormat: XLSX CSV Default (output to Out-GridView) Any combination of above is possible -ignoreCurrentUser: do not add entries for the user performing the audit (as this user will have all access, it'll clutter the report) #> Param( [Switch]$expandGroups, [Switch]$ignoreCurrentUser, [Switch]$maxDetail, [parameter(Mandatory=$true)] [ValidateSet('XLSX','CSV','Default')] [String[]]$outputFormat ) $global:ignoreCurrentUser = $ignoreCurrentUser.IsPresent Write-Host "Performing ExO scan using: $($currentUser.userPrincipalName)" Write-Progress -Id 1 -PercentComplete 0 -Activity "Scanning Exchange Online" -Status "Retrieving all role assignments" $global:ExOPermissions = @{} $global:exoStatObjects = @() $global:statObj = [PSCustomObject]@{ "Module version" = $global:moduleVersion "Category" = "ExO" "Subject" = "Roles" "Total objects scanned" = 0 "Scan start time" = Get-Date "Scan end time" = "" "Scan performed by" = $currentUser.userPrincipalName } $params = @{ GetEffectiveUsers = $True } $assignedManagementRoles = $Null;$assignedManagementRoles = (New-ExOQuery -cmdlet "Get-ManagementRoleAssignment" -cmdParams $params) Write-Progress -Id 1 -PercentComplete 5 -Activity "Scanning Exchange Online" -Status "Parsing role assignments" $identityCache = @{} $count = 0 foreach($assignedManagementRole in $assignedManagementRoles){ $count++ Write-Progress -Id 2 -PercentComplete ($count/$assignedManagementRoles.Count*100) -Activity "Scanning Roles" -Status "Examining role $($count) of $($assignedManagementRoles.Count)" $global:statObj."Total objects scanned"++ try{ $mailbox = $Null; $mailbox = $identityCache.$($assignedManagementRole.EffectiveUserName) if($Null -eq $mailbox){ $mailbox = (New-ExOQuery -cmdlet "Get-Mailbox" -cmdParams @{Identity = $assignedManagementRole.EffectiveUserName}) if(!$mailbox){ $identityCache.$($assignedManagementRole.EffectiveUserName) = $False }else{ $identityCache.$($assignedManagementRole.EffectiveUserName) = $mailbox } } }catch{ $identityCache.$($assignedManagementRole.EffectiveUserName) = $False } if($false -eq $identityCache.$($assignedManagementRole.EffectiveUserName)){ #mailbox not found, but its a guid (instead of e.g. a group), so probably a deleted mailbox if([guid]::TryParse($assignedManagementRole.EffectiveUserName, $([ref][guid]::Empty))){ $splat = @{ path = "/" type = "AdminRole" principalEntraId = "Unknown" principalUpn = $assignedManagementRole.EffectiveUserName principalName = "Unknown" principalType = "DELETED OBJECT" role = "$($assignedManagementRole.Role)" through = "$($assignedManagementRole.RoleAssignee)" kind = "$($assignedManagementRole.RoleAssignmentDelegationType)" } New-ExOPermissionEntry @splat } }else{ $splat = @{ path = "/" type = "AdminRole" principalEntraId = $mailbox.ExternalDirectoryObjectId principalUpn = $mailbox.UserPrincipalName principalName = $mailbox.DisplayName principalType = $mailbox.RecipientTypeDetails role = "$($assignedManagementRole.Role)" through = "$($assignedManagementRole.RoleAssignee)" kind = "$($assignedManagementRole.RoleAssignmentDelegationType)" } New-ExOPermissionEntry @splat } } Write-Progress -Id 2 -Completed -Activity "Scanning Roles" $global:statObj."Scan end time" = Get-Date $global:exoStatObjects += $global:statObj $global:statObj = [PSCustomObject]@{ "Module version" = $global:moduleVersion "Category" = "ExO" "Subject" = "Mailboxes" "Total objects scanned" = 0 "Scan start time" = Get-Date "Scan end time" = "" "Scan performed by" = $currentUser.userPrincipalName } Write-Progress -Id 1 -PercentComplete 15 -Activity "Scanning Exchange Online" -Status "Retrieving mailboxes and other objects..." $recipients = (New-ExOQuery -cmdlet "Get-Recipient" -cmdParams @{"ResultSize" = "Unlimited"}) Write-Progress -Id 1 -PercentComplete 35 -Activity "Scanning Exchange Online" -Status "Checking recipients for non default permissions..." $count = 0 foreach($recipient in $recipients){ $count++ $global:statObj."Total objects scanned"++ if(!$recipient.PrimarySmtpAddress){ Write-Warning "skipping $($recipient.identity) as it has no primary smtp address" continue } Write-Progress -Id 2 -PercentComplete ($count/$recipients.Count*100) -Activity "Scanning recipients" -Status "Examining $($recipient.displayName) ($($count) of $($recipients.Count))" #mailboxes have mailbox permissions if($recipient.RecipientTypeDetails -like "*Mailbox*" -and $recipient.RecipientTypeDetails -ne "GroupMailbox"){ #get mailbox meta for SOB permissions $mailbox = $Null; $mailbox = (New-ExOQuery -cmdlet "Get-Mailbox" -cmdParams @{Identity = $recipient.Guid}) if($mailbox.GrantSendOnBehalfTo){ foreach($sendOnBehalf in $mailbox.GrantSendOnBehalfTo){ $entity = $Null; $entity= $recipients | Where-Object {$_.DisplayName -eq $sendOnBehalf} $splat = @{ path = "/$($recipient.PrimarySmtpAddress)" type = $recipient.RecipientTypeDetails principalUpn = $sendOnBehalf principalName = $entity.displayName principalEntraId = $entity.ExternalDirectoryObjectId principalType = $entity.RecipientTypeDetails role = "SendOnBehalf" through = "Direct" kind = "Allow" } New-ExOPermissionEntry @splat } } $mailboxPermissions = $Null; $mailboxPermissions = (New-ExOQuery -cmdlet "Get-Mailboxpermission" -cmdParams @{Identity = $recipient.Guid}) | Where-Object {$_.User -like "*@*"} foreach($mailboxPermission in $mailboxPermissions){ foreach($AccessRight in $mailboxPermission.AccessRights){ $entity = $Null; $entity= $recipients | Where-Object {$_.PrimarySmtpAddress -eq $mailboxPermission.User} $splat = @{ path = "/$($recipient.PrimarySmtpAddress)" type = $recipient.RecipientTypeDetails principalUpn = if($entity.windowsLiveId){$entity.windowsLiveId}else{$mailboxPermission.User} principalName = $entity.displayName principalEntraId = $entity.ExternalDirectoryObjectId principalType = $entity.RecipientTypeDetails role = $AccessRight through = $(if($mailboxPermission.IsInherited){ "Inherited" }else{ "Direct" }) kind = $(if($mailboxPermission.Deny -eq "False"){ "Allow" }else{ "Deny" }) } New-ExOPermissionEntry @splat } } } #all recipients can have recipient permissions $recipientPermissions = (New-ExOQuery -cmdlet "Get-RecipientPermission" -cmdParams @{"ResultSize" = "Unlimited"; "Identity" = $recipient.Guid}) | Where-Object {$_.Trustee -ne "NT Authority\SELF" } foreach($recipientPermission in $recipientPermissions){ $entity = $Null; $entity= $recipients | Where-Object {$_.PrimarySmtpAddress -eq $recipientPermission.Trustee} foreach($AccessRight in $recipientPermission.AccessRights){ $splat = @{ path = "/$($recipient.PrimarySmtpAddress)" type = $recipient.RecipientTypeDetails principalUpn = $recipientPermission.Trustee principalName = $entity.displayName principalEntraId = $entity.ExternalDirectoryObjectId principalType = $entity.RecipientTypeDetails role = $AccessRight through = $(if($recipientPermission.IsInherited){ "Inherited" }else{ "Direct" }) kind = $recipientPermission.AccessControlType } New-ExOPermissionEntry @splat } } } Write-Progress -Id 2 -Completed -Activity "Scanning Mailboxes" Write-Progress -Id 1 -PercentComplete 75 -Activity "Scanning Exchange Online" -Status "Writing report..." $global:statObj."Scan end time" = Get-Date $global:exoStatObjects += $global:statObj Write-Host "All permissions retrieved, writing reports..." $permissionRows = foreach($row in $global:ExOPermissions.Keys){ foreach($permission in $global:ExOPermissions.$row){ [PSCustomObject]@{ "Path" = $permission.Path "Type" = $permission.Type "PrincipalEntraId" = $permission.PrincipalEntraId "PrincipalUpn" = $permission.PrincipalUpn "PrincipalName" = $permission.PrincipalName "PrincipalType" = $permission.PrincipalType "Role" = $permission.Role "Through" = $permission.Through "Kind" = $permission.Kind } } } if((get-location).Path){ $basePath = Join-Path -Path (get-location).Path -ChildPath "M365Permissions.@@@" }else{ $basePath = Join-Path -Path (Split-Path -Path $MyInvocation.MyCommand.Definition -Parent) -ChildPath "M365Permissions.@@@" } foreach($format in $outputFormat){ switch($format){ "XLSX" { $targetPath = $basePath.Replace("@@@","xlsx") $permissionRows | Export-Excel -Path $targetPath -WorksheetName "ExOPermissions" -TableName "ExOPermissions" -TableStyle Medium10 -Append -AutoSize $global:exoStatObjects | Export-Excel -Path $targetPath -WorksheetName "Statistics" -TableName "Statistics" -TableStyle Medium10 -Append -AutoSize Write-Host "XLSX report saved to $targetPath" } "CSV" { $targetPath = $basePath.Replace(".@@@","-ExO.csv") $permissionRows | Export-Csv -Path $targetPath -NoTypeInformation -Append Write-Host "CSV report saved to $targetPath" } "Default" { $permissionRows | out-gridview } } } Write-Progress -Id 1 -Completed -Activity "Scanning Exchange Online" } |