Public/Invoke-ExchangeRBACRelationship.ps1

<# Script to generate Exchange role delegation diagram with interactive selection
Allows selection of role assignments via Out-GridView #>

function Invoke-ExchangeRBACRelationship {
    param(
        [Parameter(Mandatory = $false)]
        [string]$OutputPath,
        [Parameter(Mandatory = $false)]
        [switch]$IncludeSystemRoles = $false
    )

    # Generate output file name in the format yyyy-MM-dd-HHmmss-xxx.html
    $timestamp = Get-Date -Format 'yyyy-MM-dd-HHmmss'
    $randomSuffix = -join ((65..90) + (97..122) | Get-Random -Count 3 | ForEach-Object { [char]$_ })
    if (-not $OutputPath) {
        $OutputPath = "$env:TEMP\$timestamp-$randomSuffix.html"
    }

    # Create the output directory if needed
    $OutputDir = Split-Path $OutputPath -Parent
    if (!(Test-Path $OutputDir)) {
        New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
    }

    # Function to connect to Exchange Online
    function Connect-ExchangeOnlineIfNeeded {
        try {
            # Check if already connected using Get-ConnectionInformation
            $connectionInfo = Get-ConnectionInformation -ErrorAction SilentlyContinue
            if ($null -eq $connectionInfo) {
                Write-Host 'Connecting to Exchange Online...' -ForegroundColor Yellow
                $result = Connect-RBACExchangeOnline
                if ($result) {
                    Write-Host 'Successfully connected to Exchange Online!' -ForegroundColor Green
                }
            }
            else {
                Write-Host 'Already connected to Exchange Online' -ForegroundColor Green
            }
        }
        catch {
            Write-Host "Error connecting to Exchange Online: $_" -ForegroundColor Red
            Write-Host 'Make sure you have installed the module: Install-Module -Name ExchangeOnlineManagement' -ForegroundColor Yellow
            return $false
        }
        return $true
    }

    # Connect to Exchange Online
    if (!(Connect-ExchangeOnlineIfNeeded)) {
        exit 1
    }

    Write-Host "`nRetrieving all Role Assignments..." -ForegroundColor Cyan

    # Retrieve all role assignments
    try {
        $allRoleAssignments = Get-ManagementRoleAssignment
        if (!$IncludeSystemRoles) {
            # Filter out system roles
            $allRoleAssignments = $allRoleAssignments | Where-Object {
                $_.RoleAssigneeName -notlike 'SystemMailbox*' -and
                $_.RoleAssigneeName -notlike 'DiscoveryMailbox*' -and
                $_.RoleAssigneeName -notlike 'Migration.*' -and
                $_.Role -notlike 'ApplicationImpersonation-*'
            }
        }
        # Prepare data for Out-GridView
        $assignmentData = $allRoleAssignments | Select-Object @{
            Name = 'Assignment Name'; Expression = { $_.Name }
        }, @{
            Name = 'Role'; Expression = { $_.Role }
        }, @{
            Name = 'Role Assignee'; Expression = { $_.RoleAssigneeName }
        }, @{
            Name = 'Assignee Type'; Expression = { $_.RoleAssigneeType }
        }, @{
            Name = 'Delegation Type'; Expression = { $_.RoleAssignmentDelegationType }
        }, @{
            Name = 'Scope'; Expression = {
                if ($_.CustomRecipientWriteScope) { $_.CustomRecipientWriteScope }
                elseif ($_.RecipientWriteScope) { $_.RecipientWriteScope }
                else { 'Organization' }
            }
        }, @{
            Name = 'Enabled'; Expression = { $_.Enabled }
        }, @{
            Name = 'Identity'; Expression = { $_.Identity }
        }
    }
    catch {
        Write-Host "Error retrieving roles: $_" -ForegroundColor Red
        exit 1
    }

    Write-Host "Total assignments found: $($assignmentData.Count)" -ForegroundColor Green

    # Show Out-GridView for selection
    Write-Host "`nOpening selection window..." -ForegroundColor Yellow
    Write-Host 'Select the Role Assignments to include in the diagram (Ctrl+Click for multiple)' -ForegroundColor Cyan

    $selectedAssignments = $assignmentData | Out-GridView -Title 'Select Role Assignments to visualize' -PassThru

    if (!$selectedAssignments) {
        Write-Host "`nNo selection made. Exiting script." -ForegroundColor Yellow
        exit 0
    }

    Write-Host "`nYou selected $($selectedAssignments.Count) assignment(s)" -ForegroundColor Green

    # Get full details of selected assignments
    $selectedFullAssignments = @()
    foreach ($selected in $selectedAssignments) {
        $fullAssignment = $allRoleAssignments | Where-Object { $_.Identity -eq $selected.Identity }
        if ($fullAssignment) {
            $selectedFullAssignments += $fullAssignment
        }
    }

    # Group by Role Assignee for diagram organization
    $groupedAssignments = $selectedFullAssignments | Group-Object -Property RoleAssigneeName

    # Retrieve members of role groups if needed
    Write-Host "`nRetrieving members of role groups..." -ForegroundColor Cyan
    $roleGroupMembers = @{}
    foreach ($group in $groupedAssignments) {
        if ($group.Group[0].RoleAssigneeType -eq 'RoleGroup') {
            try {
                $members = Get-RoleGroupMember -Identity $group.Name -ErrorAction SilentlyContinue
                if ($members) {
                    $roleGroupMembers[$group.Name] = $members
                    Write-Host " - $($group.Name): $($members.Count) member(s)" -ForegroundColor Gray
                }
            }
            catch {
                Write-Host " - Unable to retrieve members of: $($group.Name)" -ForegroundColor DarkGray
            }
        }
    }

    # Generate the HTML report with interactive diagram
    Write-Host "`nGenerating HTML diagram..." -ForegroundColor Cyan

    New-HTML -Title 'Exchange Role Delegation - Selected Assignments' -FilePath $OutputPath -ShowHTML {
    
        New-HTMLSection -HeaderText "Exchange Role Assignments - Interactive Diagram ($($selectedAssignments.Count) selected)" {
        
            New-HTMLDiagram -Height '800px' {
            
                $nodeId = 0
                $roleColors = @{
                    'Mail'         = @{bg = '#4CAF50'; border = '#2E7D32' }
                    'Mailbox'      = @{bg = '#2196F3'; border = '#1565C0' }
                    'Organization' = @{bg = '#9C27B0'; border = '#6A1B9A' }
                    'Compliance'   = @{bg = '#FF9800'; border = '#F57C00' }
                    'Security'     = @{bg = '#F44336'; border = '#C62828' }
                    'Default'      = @{bg = '#607D8B'; border = '#455A64' }
                }
            
                # Create a central node if there are multiple assignees
                if ($groupedAssignments.Count -gt 1) {
                    New-DiagramNode -Id 'CentralHub' `
                        -Label "Exchange Online`nRole Assignments`n($($selectedAssignments.Count) selected)" `
                        -Level 0 `
                        -ColorBackground '#1a237e' `
                        -ColorBorder '#0d47a1' `
                        -Shape database `
                        -FontColor white `
                        -Size 70
                }
            
                # Process each assignment group
                foreach ($assigneeGroup in $groupedAssignments) {
                    $assignments = $assigneeGroup.Group
                
                    # For each individual assignment, create a complete diagram
                    foreach ($assignment in $assignments) {
                        $assignmentNodeId = "assignment_$nodeId"
                        $nodeId++
                    
                        # Create the central node for this Role Assignment (center)
                        New-DiagramNode -Id $assignmentNodeId `
                            -Label "$($assignment.Name)`n[Role Assignment]" `
                            -Level 0 `
                            -ColorBackground '#e0e0e0' `
                            -ColorBorder '#757575' `
                            -ColorHighlightBackground '#bdbdbd' `
                            -Shape box `
                            -FontColor '#424242' `
                            -Size 60
                    
                        $yOffset = ($assignments.IndexOf($assignment)) * -400
                        # The specific role
                        $roleNodeId = "role_$nodeId"
                        $nodeId++
                        # Determine color based on role type
                        $roleCategory = 'Default'
                        foreach ($key in $roleColors.Keys) {
                            if ($assignment.Role -like "*$key*") {
                                $roleCategory = $key
                                break
                            }
                        }
                        New-DiagramNode -Id $roleNodeId `
                            -Label "$($assignment.Role)`n[ManagementRole]" `
                            -Level 2 `
                            -ColorBackground '#4CAF50' `
                            -ColorBorder '#2E7D32' `
                            -Shape box `
                            -FontColor white `
                            -Size 40
                        # Arrow with label 'What'
                        New-DiagramEdge -From $assignmentNodeId -To $roleNodeId -ArrowsToEnabled -Color '#4CAF50' -Label 'What'
                    
                        # Add delegation type under the role
                        if ($assignment.RoleAssignmentDelegationType) {
                            # Retrieve ManagementRoleEntry for the role
                            try {
                                $roleEntries = Get-ManagementRoleEntry "$($assignment.Role)\*" -ErrorAction SilentlyContinue
                            } catch { $roleEntries = @() }
                            # Add each ManagementRoleEntry as a subnode directly linked to the arrow between ManagementRole and ManagementRoleEntry
                            if ($roleEntries) {
                                foreach ($entry in $roleEntries | Select-Object -First 5) {
                                    $entryNodeId = "roleentry_$nodeId"
                                    $nodeId++
                                    $entryLabel = "$($entry.Name) [$($entry.Type)]"
                                    New-DiagramNode -Id $entryNodeId `
                                        -Label $entryLabel `
                                        -Level 3 `
                                        -ColorBackground '#C5E1A5' `
                                        -ColorBorder '#558B2F' `
                                        -Shape box `
                                        -FontColor '#33691E' `
                                        -Size 20
                                    New-DiagramEdge -From $roleNodeId -To $entryNodeId -ArrowsToEnabled -Color '#81C784' -Label "$($assignment.RoleAssignmentDelegationType)"
                                }
                                if ($roleEntries.Count -gt 5) {
                                    $moreEntryNodeId = "moreentry_$nodeId"
                                    $nodeId++
                                    New-DiagramNode -Id $moreEntryNodeId `
                                        -Label "+$($roleEntries.Count - 5) more..." `
                                        -Level 3 `
                                        -ColorBackground '#F0F4C3' `
                                        -ColorBorder '#827717' `
                                        -Shape box `
                                        -FontColor '#827717' `
                                        -Size 15
                                    New-DiagramEdge -From $roleNodeId -To $moreEntryNodeId -ArrowsToEnabled -Color '#81C784' -Dashes -Label "$($assignment.RoleAssignmentDelegationType)"
                                }
                            }
                        }
                    
                        # The specific assignee
                        $assigneeNodeId = "assignee_$nodeId"
                        $nodeId++
                        # Determine color based on assignee type
                        $assigneeColor = switch ($assignment.RoleAssigneeType) {
                            'RoleGroup' { @{bg = '#9C27B0'; border = '#6A1B9A' } }
                            'User' { @{bg = '#2196F3'; border = '#1565C0' } }
                            'SecurityGroup' { @{bg = '#4CAF50'; border = '#2E7D32' } }
                            'RoleAssignmentPolicy' { @{bg = '#FF9800'; border = '#F57C00' } }
                            default { @{bg = '#607D8B'; border = '#455A64' } }
                        }
                        New-DiagramNode -Id $assigneeNodeId `
                            -Label "$($assignment.RoleAssigneeName)`n[$($assignment.RoleAssigneeType)]" `
                            -Level 2 `
                            -ColorBackground $assigneeColor.bg `
                            -ColorBorder $assigneeColor.border `
                            -Shape box `
                            -FontColor white `
                            -Size 40
                        # Arrow with label 'Who'
                        New-DiagramEdge -From $assignmentNodeId -To $assigneeNodeId -ArrowsToEnabled -Color '#9C27B0' -Label 'Who'
                    
                        # If it's a group, add members
                        if ($assignment.RoleAssigneeType -eq 'RoleGroup' -and $roleGroupMembers.ContainsKey($assignment.RoleAssigneeName)) {
                            $members = $roleGroupMembers[$assignment.RoleAssigneeName] | Select-Object -First 3
                            $memberXPos = -50
                            foreach ($member in $members) {
                                $memberNodeId = "member_$nodeId"
                                $nodeId++
                                # Try to get member type (RecipientTypeDetails or similar)
                                $memberType = $member.RecipientTypeDetails
                                if (-not $memberType) { $memberType = $member.ObjectClass }
                                if (-not $memberType) { $memberType = 'Unknown' }
                                New-DiagramNode -Id $memberNodeId `
                                    -Label "$($member.Name) [$memberType]" `
                                    -Level 3 `
                                    -ColorBackground '#E1BEE7' `
                                    -ColorBorder '#9C27B0' `
                                    -Shape box `
                                    -FontColor '#4A148C' `
                                    -Size 25
                                New-DiagramEdge -From $assigneeNodeId -To $memberNodeId -ArrowsToEnabled -Color '#CE93D8'
                                $memberXPos += 50
                            }
                        
                            if ($roleGroupMembers[$assignment.RoleAssigneeName].Count -gt 3) {
                                $moreNodeId = "moremember_$nodeId"
                                $nodeId++
                                New-DiagramNode -Id $moreNodeId `
                                    -Label "+$($roleGroupMembers[$assignment.RoleAssigneeName].Count - 3) more..." `
                                    -Level 3 `
                                    -ColorBackground '#F3E5F5' `
                                    -ColorBorder '#9C27B0' `
                                    -Shape box `
                                    -FontColor '#6A1B9A' `
                                    -Size 20
                                New-DiagramEdge -From $assigneeNodeId -To $moreNodeId -ArrowsToEnabled -Color '#CE93D8' -Dashes
                            }
                        }
                    
                        # The specific scope
                        $scopeNodeId = "scope_$nodeId"
                        $nodeId++
                        $scopeText = if ($assignment.CustomRecipientWriteScope) { 
                            $assignment.CustomRecipientWriteScope 
                        }
                        elseif ($assignment.RecipientWriteScope) { 
                            $assignment.RecipientWriteScope 
                        }
                        else { 
                            'Organization' 
                        }
                        New-DiagramNode -Id $scopeNodeId `
                            -Label "$scopeText`n[Scope Management]" `
                            -Level 2 `
                            -ColorBackground '#F44336' `
                            -ColorBorder '#C62828' `
                            -Shape box `
                            -FontColor white `
                            -Size 40
                        # Arrow with label 'Where'
                        New-DiagramEdge -From $assignmentNodeId -To $scopeNodeId -ArrowsToEnabled -Color '#F44336' -Label 'Where'
                        # Add ScopeFilter as a subnode
                        if ($assignment.ScopeFilter) {
                            $scopeFilterNodeId = "scopefilter_$nodeId"
                            $nodeId++
                            New-DiagramNode -Id $scopeFilterNodeId `
                                -Label "ScopeFilter: $($assignment.ScopeFilter)" `
                                -Level 3 `
                                -ColorBackground '#FFCDD2' `
                                -ColorBorder '#C62828' `
                                -Shape box `
                                -FontColor '#C62828' `
                                -Size 25
                            New-DiagramEdge -From $scopeNodeId -To $scopeFilterNodeId -ArrowsToEnabled -Color '#FFCDD2'
                        }
                    }
                }
            
            } -DisableLoader
        }
    
        # Section with summary table
        New-HTMLSection -HeaderText 'Selected Assignments Details' {
            New-HTMLTable -DataTable $selectedAssignments {
                New-TableHeader -Title 'Role Assignments Details' -BackGroundColor '#1a237e' -Color white
                New-HTMLTableCondition -Name 'Delegation Type' -ComparisonType string -Operator eq -Value 'Delegating' -BackgroundColor '#FFF9C4'
                New-HTMLTableCondition -Name 'Enabled' -ComparisonType string -Operator eq -Value 'False' -BackgroundColor '#FFCDD2'
            }
        }
    }

    # Option to restart selection
    <#
    $restart = Read-Host "`nDo you want to select other assignments? (Y/N)"
    if ($restart -eq 'Y') {
        & $MyInvocation.MyCommand.Path
    }
#>

}