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: -includeFolderLevelPermissions: if set, folder level permissions for each mailbox will be retrieved. This can be (very) slow #> Param( [Switch]$includeFolderLevelPermissions, [parameter(Mandatory=$true)][String]$recipientIdentity, [Boolean]$isParallel=$False ) $global:ExOPermissions = @{} if(!$global:octo.recipients){ Write-Progress -Id 2 -PercentComplete 1 -Activity "Scanning Recipient" -Status "Retrieving recipients for cache..." $global:octo.recipients = (New-ExOQuery -cmdlet "Get-Recipient" -cmdParams @{"ResultSize" = "Unlimited"}) | Where-Object{$_ -and !$_.Identity.StartsWith("DiscoverySearchMailbox")} } $recipient = $global:octo.recipients | Where-Object {$_.Identity -eq $recipientIdentity} if(!$recipient){ Write-Error "Recipient $recipientIdentity not found, skipping..." -ErrorAction Continue return $Null } New-StatisticsObject -category "ExoRecipients" -subject $recipient.displayName Update-StatisticsObject -category "ExoRecipients" -subject $recipient.displayName if(!$recipient.PrimarySmtpAddress){ Write-LogMessage -level 2 -message "skipping $($recipient.identity) as it has no primary smtp address" return $Null } #mailboxes have mailbox permissions if($recipient.RecipientTypeDetails -like "*Mailbox*" -and $recipient.RecipientTypeDetails -ne "GroupMailbox"){ Write-Progress -Id 2 -PercentComplete 5 -Activity "Scanning $($recipient.Identity)" -Status "Checking SendOnBehalf permissions..." #get mailbox meta for SOB permissions $identifierEncoded = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($recipient.Guid)) $mailbox = $Null; $mailbox = New-GraphQuery -resource "https://$($global:octo.outlookUrl)" -Method GET -Uri "https://$($global:octo.outlookUrl)/adminapi/beta/$($global:octo.OnMicrosoft)/Mailbox('$($identifierEncoded)')?isEncoded=true&`$select=GrantSendOnBehalfTo" -MaxAttempts 3 #add root level permission if($mailbox.ExternalDirectoryObjectId){ $splat = @{ targetPath = "/$($recipient.PrimarySmtpAddress)" targetType = $recipient.RecipientTypeDetails targetId = $mailbox.Guid principalEntraId = $mailbox.ExternalDirectoryObjectId principalEntraUpn = if($mailbox.PrimarySmtpAddress){$mailbox.PrimarySmtpAddress}else{$mailbox.windowsLiveId} principalSysId = $mailbox.Guid principalSysName = $mailbox.DisplayName principalType = "Internal User" principalRole = "Owner" through = "Direct" accessType = "Allow" tenure = "Permanent" } New-ExOPermissionEntry @splat } if($mailbox.GrantSendOnBehalfTo){ foreach($sendOnBehalf in $mailbox.GrantSendOnBehalfTo){ $identifierEncoded = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($sendOnBehalf)) $extraHeaders = @( @{"Name" = "XParameter-Identity"; "Value" = "$($identifierEncoded)"} ) $entity = $Null; $entity= New-GraphQuery -extraHeaders $extraHeaders -resource "https://$($global:octo.outlookUrl)" -Method GET -Uri "https://$($global:octo.outlookUrl)/adminapi/beta/$($global:octo.OnMicrosoft)/Recipient?`$select=displayName,Identity,PrimarySmtpAddress,RecipientTypeDetails,Guid,ExternalDirectoryObjectId&ReadIdsAndParamsFromHeaders=True" -MaxAttempts 2 | select -First 1 $splat = @{ targetPath = "/$($recipient.PrimarySmtpAddress)" targetType = $recipient.RecipientTypeDetails targetId = $mailbox.Guid principalEntraId = $entity.ExternalDirectoryObjectId principalEntraUpn = if($entity.PrimarySmtpAddress){$entity.PrimarySmtpAddress}else{$entity.windowsLiveId} principalSysId = $entity.Guid principalSysName = $entity.DisplayName principalType = $entity.RecipientTypeDetails principalRole = "SendOnBehalf" through = "Direct" accessType = "Allow" tenure = "Permanent" } New-ExOPermissionEntry @splat } } if($mailbox){ Write-LogMessage -level 5 -message "Got mailbox $($mailbox.Guid) for $($recipient.Identity)" Write-Progress -Id 2 -PercentComplete 15 -Activity "Scanning $($recipient.Identity)" -Status "Checking Mailbox permissions..." $identifierEncoded = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($mailbox.Guid)) $mailboxPermissions = $Null; $mailboxPermissions= New-GraphQuery -resource "https://$($global:octo.outlookUrl)" -Method GET -Uri "https://$($global:octo.outlookUrl)/adminapi/beta/$($global:octo.OnMicrosoft)/Mailbox('$($identifierEncoded)')/MailboxPermission?isEncoded=true" | Where-Object {$_.User -like "*@*"} foreach($mailboxPermission in $mailboxPermissions){ foreach($permission in $mailboxPermission.PermissionList){ $identifierEncoded = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($mailboxPermission.User)) $extraHeaders = @( @{"Name" = "XParameter-Identity"; "Value" = "$($identifierEncoded)"} ) $entity = $Null; $entity= New-GraphQuery -extraHeaders $extraHeaders -resource "https://$($global:octo.outlookUrl)" -Method GET -Uri "https://$($global:octo.outlookUrl)/adminapi/beta/$($global:octo.OnMicrosoft)/Recipient?`$select=displayName,Identity,PrimarySmtpAddress,RecipientTypeDetails,Guid,ExternalDirectoryObjectId&ReadIdsAndParamsFromHeaders=True" -MaxAttempts 2 | select -First 1 $splat = @{ targetPath = "/$($recipient.PrimarySmtpAddress)" targetType = $recipient.RecipientTypeDetails targetId = $mailbox.Guid principalEntraId = $entity.ExternalDirectoryObjectId principalEntraUpn = if($entity.PrimarySmtpAddress){$entity.PrimarySmtpAddress}else{$entity.windowsLiveId} principalSysId = $entity.Guid principalSysName = $entity.DisplayName principalType = $entity.RecipientTypeDetails principalRole = $permission.AccessRights -Join "," through = $(if($permission.IsInherited){ "Inherited" }else{ "Direct" }) accessType = $(if($permission.Deny -eq "False"){ "Allow" }else{ "Deny" }) tenure = "Permanent" } New-ExOPermissionEntry @splat } } } #retrieve individual folder permissions if -includeFolderLevelPermissions is set if($mailbox.Guid -and $includeFolderLevelPermissions){ Write-Progress -Id 2 -PercentComplete 25 -Activity "Scanning $($recipient.Identity)" -Status "Checking folder permissions..." Write-Progress -Id 3 -PercentComplete 1 -Activity "Scanning folders $($recipient.Identity)" -Status "Retrieving folder list for $($mailbox.UserPrincipalName)" $guidEncoded = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($mailbox.Guid.ToString())) try{ $folders = $Null; $folders = New-GraphQuery -resource "https://$($global:octo.outlookUrl)" -Method GET -Uri "https://$($global:octo.outlookUrl)/adminapi/beta/$($global:octo.OnMicrosoft)/Mailbox('$guidEncoded')/MailboxFolder/Exchange.GetMailboxFolderStatistics(folderscope=Exchange.ElcFolderType'All')?isEncoded=true" Write-LogMessage -level 5 -message "Got $($folders.count) folders for for $($recipient.Identity)" }catch{ Write-LogMessage -level 2 -message "Failed to retrieve folder list for $($mailbox.UserPrincipalName)" } $ignoredFolderTypes = @("PersonMetadata","ConversationActions","RecipientCache","RecoverableItemsSubstrateHolds","RecoverableItemsPurges","RecoverableItemsVersions","RecoverableItemsDeletions","RecoverableItemsDiscoveryHolds","Audits","CalendarLogging","RecoverableItemsRoot","SyncIssues","Conflicts","LocalFailures","ServerFailures") $ignoredFolderNames = @("SearchDiscoveryHoldsFolder") $folderCounter = 0 foreach($folder in $folders){ Update-StatisticsObject -category "ExoRecipients" -subject $recipient.displayName $folderCounter++ Write-Progress -Id 3 -PercentComplete (($folderCounter/$folders.Count)*100) -Activity "Scanning folders $($recipient.Identity)" -Status "Examining $($folder.Name) ($($folderCounter) of $($folders.Count))" if($ignoredFolderTypes -contains $folder.FolderType -or $ignoredFolderNames -contains $folder.Name){ Write-LogMessage -level 5 -message "Ignoring folder $($folder.Name) as it is in the ignored list" continue } if($folder.ItemsInFolder -lt 1){ Write-LogMessage -level 5 -message "Ignoring folder $($folder.Name) as it is empty" continue } try{ $folderIdEncoded = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($folder.FolderId)) $folderPermissions = $Null; $folderPermissions = New-GraphQuery -resource "https://$($global:octo.outlookUrl)" -Method GET -Uri "https://$($global:octo.outlookUrl)/adminapi/beta/$($global:octo.OnMicrosoft)/Mailbox('$guidEncoded')/MailboxFolder('$folderIdEncoded')/MailboxFolderPermission?IsUsingMailboxFolderId=True&isEncoded=true" foreach($folderPermission in $folderPermissions){ if($folderPermission.User.StartsWith("NT:S-1-5-21-")){ Write-LogMessage -level 6 -message "Ignoring pre-migration orphaned permissions $($folderPermission.User)" continue } if($folderPermission.User -eq "Default"){ $entity = [PSCustomObject]@{ ExternalDirectoryObjectId = "AllInternalUsers" PrimarySmtpAddress = "AllInternalUsers" RecipientTypeDetails = "AllInternalUsers" Guid = "AllInternalUsers" } }elseif($folderPermission.User -eq "Anonymous"){ $entity =[PSCustomObject] @{ ExternalDirectoryObjectId = "Anonymous" PrimarySmtpAddress = "Anonymous" RecipientTypeDetails = "Anonymous" Guid = "Anonymous" } }elseif($folderPermission.User.StartsWith("ExchangePublishedUser")){ $entity = [PSCustomObject]@{ PrimarySmtpAddress = $folderPermission.User.Replace("ExchangePublishedUser.","") ExternalDirectoryObjectId = "" RecipientTypeDetails = "External User" Guid = $folderPermission.User } }elseif($folderPermission.MailboxFolderUser.UserPrincipalName){ if($folderPermission.MailboxFolderUser.UserPrincipalName -like "*#EXT#@*"){ $userType = "External User" }else{ $userType = "Internal User" } $aadObj = get-aadObject -id $folderPermission.MailboxFolderUser.UserPrincipalName $entity = $Null; $entity= @{ UserPrincipalName = $folderPermission.MailboxFolderUser.UserPrincipalName ExternalDirectoryObjectId = $aadObj.id RecipientTypeDetails = $userType Guid = $folderPermission.MailboxFolderUser.UserPrincipalName } }else{ try{ $identifierEncoded = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($folderPermission.User)) $extraHeaders = @( @{"Name" = "XParameter-Identity"; "Value" = "$($identifierEncoded)"} ) $entity = $Null; $entity= New-GraphQuery -extraHeaders $extraHeaders -resource "https://$($global:octo.outlookUrl)" -Method GET -Uri "https://$($global:octo.outlookUrl)/adminapi/beta/$($global:octo.OnMicrosoft)/Recipient?`$select=displayName,Identity,PrimarySmtpAddress,RecipientTypeDetails,Guid,ExternalDirectoryObjectId&ReadIdsAndParamsFromHeaders=True" -MaxAttempts 2 | select -First 1 }catch{ Write-LogMessage -level 5 -message "Failed to get entity for $($folderPermission.User) with error $($_.Exception.Message), skipping..." continue } } if($entity -and ($entity.Identity -eq $recipient.Identity -or $entity.UserPrincipalName -eq $mailbox.UserPrincipalName)){ Write-LogMessage -level 5 -message "Skipping permission $($folderPermission.AccessRights) scoped at $($mailbox.UserPrincipalName)$($folder.FolderPath) for $($recipient.Identity) as it is the owner" continue } if($folderPermission.AccessRights -notcontains "None"){ foreach($AccessRight in $folderPermission.AccessRights.Split(",").Trim()){ $splat = @{ targetPath = "/$($recipient.PrimarySmtpAddress)$($folder.FolderPath)" targetType = "MailboxFolder" targetId = $folder.FolderId principalEntraId = $entity.ExternalDirectoryObjectId principalEntraUpn = if($entity.PrimarySmtpAddress){$entity.PrimarySmtpAddress}else{$entity.windowsLiveId} principalSysId = $entity.Guid principalSysName = $folderPermission.User principalType = $entity.RecipientTypeDetails principalRole = $AccessRight through = "Direct" accessType = "Allow" tenure = "Permanent" } New-ExOPermissionEntry @splat } } } }catch{ Write-LogMessage -level 2 -message "Failed to retrieve folder permissions for $($mailbox.UserPrincipalName)$($folder.FolderPath)" } } Write-Progress -Id 3 -Completed -Activity "Scanning folders $($recipient.Identity)" } } #all recipients can have recipient permissions Write-Progress -Id 2 -PercentComplete 85 -Activity "Scanning $($recipient.Identity)" -Status "Checking SendAs permissions..." $identifierEncoded = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($recipient.Guid)) $recipientPermissions = $Null; $recipientPermissions = New-GraphQuery -resource "https://$($global:octo.outlookUrl)" -Method GET -Uri "https://$($global:octo.outlookUrl)/adminapi/beta/$($global:octo.OnMicrosoft)/Recipient('$($identifierEncoded)')?`$expand=RecipientPermission&isEncoded=true&`$select=RecipientPermission,%20ExternalDirectoryObjectId" -MaxAttempts 3 foreach($recipientPermission in $recipientPermissions.RecipientPermission){ if($recipientPermission.Trustee -eq "NT AUTHORITY\SELF"){ continue } $identifierEncoded = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($recipientPermission.Trustee)) $extraHeaders = @( @{"Name" = "XParameter-Identity"; "Value" = "$($identifierEncoded)"} ) try{ $entity = $Null; $entity= New-GraphQuery -extraHeaders $extraHeaders -resource "https://$($global:octo.outlookUrl)" -Method GET -Uri "https://$($global:octo.outlookUrl)/adminapi/beta/$($global:octo.OnMicrosoft)/Recipient?`$select=displayName,Identity,PrimarySmtpAddress,RecipientTypeDetails,Guid,ExternalDirectoryObjectId&ReadIdsAndParamsFromHeaders=True" -MaxAttempts 2 | select -First 1 }catch{ $entity = $Null } foreach($AccessRight in $recipientPermission.AccessRights.Split(",").Trim()){ $splat = @{ targetPath = "/$($recipient.PrimarySmtpAddress)" targetType = $recipient.RecipientTypeDetails targetId = $recipient.Guid principalEntraId = $entity.ExternalDirectoryObjectId principalEntraUpn = $recipientPermission.Trustee principalSysId = $entity.Guid principalSysName = $entity.displayName principalType = $entity.RecipientTypeDetails principalRole = $AccessRight through = $(if($recipientPermission.IsInherited){ "Inherited" }else{ "Direct" }) accessType = $recipientPermission.AccessControlType tenure = "Permanent" } New-ExOPermissionEntry @splat } } Write-Progress -Id 2 -PercentComplete 95 -Activity "Scanning $($recipient.Identity)" -Status "Writing report..." Stop-StatisticsObject -category "ExoRecipients" -subject $recipient.displayName $permissionRows = foreach($row in $global:ExOPermissions.Keys){ foreach($permission in $global:ExOPermissions.$row){ [PSCustomObject]@{ "targetPath" = $row "targetType" = $permission.targetType "targetId" = $permission.targetId "principalEntraId" = $permission.principalEntraId "principalSysId" = $permission.principalSysId "principalSysName" = $permission.principalSysName "principalType" = $permission.principalType "principalRole" = $permission.principalRole "through" = $permission.through "parentId" = $permission.parentId "accessType" = $permission.accessType "tenure" = $permission.tenure "startDateTime" = $permission.startDateTime "endDateTime" = $permission.endDateTime "createdDateTime" = $permission.createdDateTime "modifiedDateTime" = $permission.modifiedDateTime } } } Add-ToReportQueue -permissions $permissionRows -category "ExoRecipients" Remove-Variable -Name permissionRows -Force -Confirm:$False Remove-Variable -Name ExOPermissions -Scope Global -Force -Confirm:$False Write-Progress -Id 2 -Completed -Activity "Scanning $($recipient.Identity)" if(!$isParallel){ Write-Report }else{ [System.GC]::GetTotalMemory($true) | out-null } } |