Generate-EntraAdminRolesReport.ps1

<#PSScriptInfo
 
.VERSION 0.1
 
.GUID 1bb55298-eaa9-43fa-9b1c-f0ad57109664
 
.AUTHOR Roy Klooster
 
.COMPANYNAME
 
.COPYRIGHT
 
.TAGS
    RKSolutions
    Microsoft365
    MicrosoftEntraID
    MicrosoftGraph
 
.LICENSEURI
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
    0.1 - Initial version of the script, providing basic functionality to connect to Microsoft Graph and retrieve role assignments.
 
 
#>


<#
 
.DESCRIPTION
 This PowerShell script generates a comprehensive HTML report of Microsoft Entra ID administrative role assignments. It connects to Microsoft Graph API using multiple authentication methods (Interactive, ClientSecret, or Certificate) and retrieves all role assignments in the tenant, including permanent and eligible (PIM) assignments. The script categorizes assignments by principal type (users, groups, service principals), collects group membership details, and supports filtering by various criteria.
 
#>
 
Param(  
    [Parameter(Mandatory = $true)]
    [ValidateSet("Interactive", "ClientSecret", "Certificate")]
    [string]$AuthMethod = "Interactive",
    [Parameter(Mandatory = $false)]
    [string]$TenantId,
    [Parameter(Mandatory = $false)]
    [string]$ClientId,
    [Parameter(Mandatory = $false)]
    [string]$ClientSecret,
    [Parameter(Mandatory = $false)]
    [string]$CertificateThumbprint,
    [Parameter(Mandatory = $false)]
    [switch]$IncludePrivilegedAssignments #Indicate whether to include PIM eligible role assignments in the output.
)


function New-AdminRoleHTMLReport {
    param(
        [Parameter(Mandatory = $true)]
        [string]$TenantName,
        
        [Parameter(Mandatory = $true)]
        [array]$Report,
        
        [Parameter(Mandatory = $false)]
        [array]$GroupAssignmentReport,
        
        [Parameter(Mandatory = $false)]
        [array]$ServicePrincipalReport,
        
        [Parameter(Mandatory = $false)]
        [array]$UserAssignmentReport,

        [Parameter(Mandatory = $false)]
        [array]$GroupMembershipOverviewReport,
        
        [Parameter(Mandatory = $false)]
        [string]$ExportPath = "$env:PUBLIC\Documents\$TenantName-AdminRolesReport.html"
    )


    # Calculate roles counts for dashboard statistics
    $permanentRoles = ($Report | Where-Object { $_.AssignmentType -eq "Permanent" }).Count
    $eligibleRoles = ($Report | Where-Object { $_.AssignmentType -like "Eligible*" }).Count
    $groupAssignedRoles = $GroupAssignmentReport.Count
    $servicePrincipalRoles = $ServicePrincipalReport.Count

    # Get the current date and time for the report header
    $CurrentDate = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")

    # Create HTML Template with DataTables
    $htmlTemplate = @'
        <!DOCTYPE html>
        <html lang="en">
        <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>$TenantName Admin Roles Report</title>
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.3.0/css/bootstrap.min.css">
        <link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
        <link rel="stylesheet" href="https://cdn.datatables.net/buttons/2.4.1/css/buttons.bootstrap5.min.css">
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
        <script src="https://code.jquery.com/jquery-3.7.0.js"></script>
        <script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
        <script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
        <script src="https://cdn.datatables.net/buttons/2.4.1/js/dataTables.buttons.min.js"></script>
        <script src="https://cdn.datatables.net/buttons/2.4.1/js/buttons.bootstrap5.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.53/pdfmake.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.53/vfs_fonts.js"></script>
        <script src="https://cdn.datatables.net/buttons/2.4.1/js/buttons.html5.min.js"></script>
        <script src="https://cdn.datatables.net/buttons/2.4.1/js/buttons.print.min.js"></script>
        <script src="https://cdn.datatables.net/buttons/2.4.1/js/buttons.colVis.min.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
        <style>
        :root {
            /* Light mode variables (default) */
            --primary-color: #0078d4;
            --secondary-color: #2b88d8;
            --permanent-color: #d83b01;
            --eligible-color: #107c10;
            --group-color: #5c2d91;
            --disabled-color: #d9534f;
            --enabled-color: #5cb85c;
            --service-principal-color: #0078d4;
            --na-color: #6c757d;
            --bg-color: #f8f9fa;
            --card-bg: #ffffff;
            --text-color: #333333;
            --table-header-bg: #f5f5f5;
            --table-header-color: #333333;
            --table-stripe-bg: rgba(0,0,0,0.02);
            --table-hover-bg: rgba(0,0,0,0.04);
            --table-border-color: #dee2e6;
            --filter-tag-bg: #e9ecef;
            --filter-tag-color: #495057;
            --filter-bg: white;
            --btn-outline-color: #6c757d;
            --border-color: #dee2e6;
            --toggle-bg: #ccc;
            --button-bg: #f8f9fa;
            --button-color: #333;
            --button-border: #ddd;
            --button-hover-bg: #e9ecef;
            --footer-text: white;
            --input-bg: #fff;
            --input-color: #333;
            --input-border: #ced4da;
            --input-focus-border: #86b7fe;
            --input-focus-shadow: rgba(13, 110, 253, 0.25);
            --datatable-even-row-bg: #fff;
            --datatable
            --tab-active-color: #fff;
        }
         
        [data-theme="dark"] {
            /* Dark mode variables */
            --primary-color: #0078d4;
            --secondary-color: #2b88d8;
            --permanent-color: #d83b01;
            --eligible-color: #107c10;
            --group-color: #5c2d91;
            --disabled-color: #6c757d;
            --enabled-color: #0078d4;
            --service-principal-color: #0078d4;
            --bg-color: #121212;
            --card-bg: #1e1e1e;
            --text-color: #e0e0e0;
            --table-header-bg: #333333;
            --table-header-color: #e0e0e0;
            --table-header-bg: #333333;#e0e0e0;5,255,0.03);
            --table-header-color: #e0e0e0;
            --table-stripe-bg: rgba(255,255,255,0.03);
            --table-hover-bg: rgba(255,255,255,0.05);
            --table-border-color: #444444;
            --filter-bg: #252525;
            --btn-outline-color: #adb5bd;
            --border-color: #444444;
            --toggle-bg: #555555;
            --button-bg: #2a2a2a;
            --button-color: #e0e0e0;
            --button-border: #444;
            --button-hover-bg: #3a3a3a;
            --footer-text: white;
            --input-bg: #2a2a2a;
            --input-color: #e0e0e0;
            --input-border: #444444;
            --input-focus-border: #0078d4;
            --input-focus-shadow: rgba(0, 120, 212, 0.25);
            --datatable-even-row-bg: #1e1e1e;
            --datatable-odd-row-bg: #252525;
            --tab-active-bg: #0078d4;
            --tab-active-color: #fff;
        }
         
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            margin: 0;
            padding: 0;
            background-color: var(--bg-color);
            color: var(--text-color);
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            transition: background-color 0.3s ease, color 0.3s ease;
        }
         
        .container-fluid {
            max-width: 1600px;
            padding: 20px;
            flex: 1;
        }
         
        .dashboard-header {
            padding: 20px 0;
            margin-bottom: 30px;
            border-bottom: 1px solid rgba(128,128,128,0.2);
            display: flex;
            align-items: center;
            justify-content: space-between;
        }
         
        .dashboard-title {
            display: flex;
            align-items: center;
            gap: 15px;
        }
         
        .dashboard-title h1 {
            margin: 0;
            font-size: 1.8rem;
            font-weight: 600;
            color: var(--primary-color);
        }
         
        .logo {
            height: 45px;
            width: 45px;
        }
         
        .report-date {
            font-size: 0.9rem;
            color: var(--text-color);
            opacity: 0.8;
        }
         
        .card {
            border: none;
            border-radius: 10px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.05);
            margin-bottom: 25px;
            transition: transform 0.2s, box-shadow 0.2s, background-color 0.3s ease;
            overflow: hidden;
            background-color: var(--card-bg);
        }
         
        .card:hover {
            transform: translateY(-5px);
            box-shadow: 0 8px 16px rgba(0,0,0,0.1);
        }
         
        .card-header {
            background-color: var(--primary-color);
            color: white;
            font-weight: 600;
            padding: 15px 20px;
            border-bottom: none;
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 10px;
        }
         
        .card-header i {
            font-size: 1.2rem;
        }
         
        .card-body {
            padding: 20px;
        }
         
        .stats-card {
            height: 100%;
            text-align: center;
            padding: 25px 15px;
            border-radius: 10px;
            color: white;
            position: relative;
            overflow: hidden;
            min-height: 160px;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            cursor: pointer;
            transition: all 0.3s;
        }
         
        .stats-card.active {
            box-shadow: 0 0 0 4px rgba(255,255,255,0.6), 0 8px 16px rgba(0,0,0,0.2);
            transform: scale(1.05);
        }
         
        .stats-card::before {
            content: '';
            position: absolute;
            top: -20px;
            right: -20px;
            width: 100px;
            height: 100px;
            border-radius: 50%;
            background-color: rgba(255,255,255,0.1);
            z-index: 0;
        }
         
        .stats-card i {
            font-size: 2.5rem;
            margin-bottom: 15px;
            position: relative;
            z-index: 1;
        }
         
        .stats-card h3 {
            font-size: 1rem;
            font-weight: 500;
            margin-bottom: 10px;
            position: relative;
            z-index: 1;
        }
         
        .stats-card .number {
            font-size: 2.2rem;
            font-weight: 700;
            position: relative;
            z-index: 1;
        }
         
        .permanent-bg {
            background: linear-gradient(135deg, var(--permanent-color), #f25c05);
        }
         
        .eligible-bg {
            background: linear-gradient(135deg, var(--eligible-color), #2a9d2a);
        }
         
        .group-bg {
            background: linear-gradient(135deg, var(--group-color), #7b4db2);
        }
 
        .disabled-bg {
            background: linear-gradient(135deg, var(--disabled-color), #6c757d);
        }
 
        .enabled-bg {
            background: linear-gradient(135deg, var(--enabled-color), #0078d4);
        }
         
        .service-principal-bg {
            background: linear-gradient(135deg, var(--service-principal-color), #2b88d8);
        }
         
        /* DataTables Dark Mode Overrides */
        table.dataTable {
            border-collapse: collapse !important;
            width: 100% !important;
            color: var(--text-color) !important;
            border-color: var(--table-border-color) !important;
        }
         
        .table {
            color: var(--text-color) !important;
            border-color: var(--table-border-color) !important;
        }
         
        .table-striped>tbody>tr:nth-of-type(odd) {
            background-color: var(--datatable-odd-row-bg) !important;
        }
         
        .table-striped>tbody>tr:nth-of-type(even) {
            background-color: var(--datatable-even-row-bg) !important;
        }
         
        .table thead th {
            background-color: var(--table-header-bg) !important;
            color: var(--table-header-color) !important;
            font-weight: 600;
            border-top: none;
            padding: 12px;
            border-color: var(--table-border-color) !important;
        }
         
        .table tbody td {
            padding: 12px;
            vertical-align: middle;
            border-color: var(--table-border-color) !important;
            color: var(--text-color) !important;
        }
         
        .table.table-bordered {
            border-color: var(--table-border-color) !important;
        }
         
        .table-bordered td, .table-bordered th {
            border-color: var(--table-border-color) !important;
        }
         
        .table-hover tbody tr:hover {
            background-color: var(--table-hover-bg) !important;
        }
         
        .badge {
            padding: 6px 10px;
            font-weight: 500;
            border-radius: 6px;
        }
         
        .badge-permanent {
            background-color: var(--permanent-color);
            color: white;
        }
         
        .badge-eligible {
            background-color: var(--eligible-color);
            color: white;
        }
         
        .badge-active {
            background-color: var(--service-principal-color);
            color: white;
        }
         
        .badge-group {
            background-color: var(--group-color);
            color: white;
        }
         
        .dataTables_wrapper .dataTables_length,
        .dataTables_wrapper .dataTables_filter,
        .dataTables_wrapper .dataTables_info,
        .dataTables_wrapper .dataTables_processing,
        .dataTables_wrapper .dataTables_paginate {
            color: var(--text-color) !important;
        }
         
        .dataTables_wrapper .dataTables_paginate .paginate_button {
            padding: 0.3em 0.8em;
            border-radius: 4px;
            margin: 0 3px;
            color: var(--text-color) !important;
            border: 1px solid var(--border-color) !important;
            background-color: var(--button-bg) !important;
        }
         
        .dataTables_wrapper .dataTables_paginate .paginate_button.current {
            background: var(--primary-color) !important;
            border-color: var(--primary-color) !important;
            color: white !important;
        }
         
        .dataTables_wrapper .dataTables_paginate .paginate_button:hover {
            background: var(--button-hover-bg) !important;
            border-color: var(--border-color) !important;
            color: var(--text-color) !important;
        }
         
        .dataTables_wrapper .dataTables_length select,
        .dataTables_wrapper .dataTables_filter input {
            border: 1px solid var(--input-border);
            background-color: var(--input-bg);
            color: var(--input-color);
            border-radius: 4px;
            padding: 5px 10px;
        }
         
        .dataTables_wrapper .dataTables_filter input:focus {
            border-color: var(--input-focus-border);
            box-shadow: 0 0 0 0.25rem var(--input-focus-shadow);
        }
         
        .dataTables_info {
            padding-top: 10px;
            color: var(--text-color);
        }
         
        footer {
            background-color: var(--primary-color);
            color: var(--footer-text);
            text-align: center;
            padding: 15px 0;
            margin-top: auto;
        }
         
        footer p {
            margin: 0;
            font-weight: 500;
        }
         
        .filter-buttons {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            margin-bottom: 20px;
        }
         
        .filter-button {
            padding: 8px 16px;
            border-radius: 20px;
            border: none;
            cursor: pointer;
            font-weight: 500;
            transition: all 0.2s;
            display: flex;
            align-items: center;
            gap: 8px;
        }
         
        .filter-button:hover {
            opacity: 0.9;
        }
         
        .filter-button.active {
            box-shadow: 0 0 0 2px rgba(128,128,128,0.2);
        }
         
        .toggle-container {
            display: flex;
            align-items: center;
            gap: 10px;
            margin-bottom: 15px;
        }
         
        .toggle-switch {
            position: relative;
            display: inline-block;
            width: 60px;
            height: 30px;
        }
         
        .toggle-switch input {
            opacity: 0;
            width: 0;
            height: 0;
        }
         
        .toggle-slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: var(--toggle-bg);
            transition: .4s;
            border-radius: 34px;
        }
         
        .toggle-slider:before {
            position: absolute;
            content: "";
            height: 22px;
            width: 22px;
            left: 4px;
            bottom: 4px;
            background-color: white;
            transition: .4s;
            border-radius: 50%;
        }
         
        input:checked + .toggle-slider {
            background-color: var(--primary-color);
        }
         
        input:checked + .toggle-slider:before {
            transform: translateX(30px);
        }
         
        .filter-section {
            background-color: var(--filter-bg);
            padding: 15px;
            border-radius: 8px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.05);
            margin-bottom: 20px;
            transition: background-color 0.3s ease;
        }
         
        .filter-section h5 {
            color: var(--primary-color);
            margin-bottom: 12px;
            font-weight: 600;
        }
         
        .filter-tags {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
            margin-top: 8px;
        }
         
        .filter-tag {
            background-color: var(--filter-tag-bg);
            padding: 4px 12px;
            border-radius: 16px;
            font-size: 0.85rem;
            color: var(--filter-tag-color);
            display: flex;
            align-items: center;
            gap: 6px;
            transition: background-color 0.3s ease, color 0.3s ease;
        }
         
        .filter-tag i {
            cursor: pointer;
            color: var(--filter-tag-color);
        }
         
        .filter-tag i:hover {
            color: var(--permanent-color);
        }
         
        .custom-search {
            display: flex;
            gap: 10px;
            margin-bottom: 15px;
        }
         
        .custom-search input {
            flex: 1;
            padding: 8px 12px;
            border: 1px solid var(--border-color);
            background-color: var(--bg-color);
            color: var(--text-color);
            border-radius: 4px;
        }
         
        .custom-search button {
            background-color: var(--primary-color);
            color: white;
            border: none;
            border-radius: 4px;
            padding: 8px 16px;
            cursor: pointer;
        }
         
        .enabled-filters-container {
            margin-bottom: 15px;
        }
         
        /* Show all entries toggle */
        .show-all-container {
            display: flex;
            align-items: center;
            gap: 12px;
            background-color: transparent;
            padding: 0;
            border: none;
            margin-left: 15px;
        }
         
        .show-all-text {
            font-weight: 500;
            margin: 0;
            color: white;
            font-size: 0.85rem;
        }
         
        /* Custom DataTable controls wrapper */
        .datatable-header {
            display: flex;
            align-items: center;
            flex-wrap: wrap;
            margin-bottom: 1rem;
        }
         
        .datatable-controls {
            display: flex;
            align-items: center;
            gap: 15px;
            flex-wrap: wrap;
        }
         
        /* Theme toggle styles */
        .theme-toggle {
            position: fixed;
            top: 20px;
            right: 20px;
            z-index: 1000;
            display: flex;
            align-items: center;
            gap: 10px;
            background-color: var(--card-bg);
            padding: 8px 12px;
            border-radius: 30px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
            transition: background-color 0.3s ease;
        }
         
        .theme-toggle-switch {
            position: relative;
            display: inline-block;
            width: 50px;
            height: 26px;
        }
         
        .theme-toggle-switch input {
            opacity: 0;
            width: 0;
            height: 0;
        }
         
        .theme-toggle-slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: var(--toggle-bg);
            transition: .4s;
            border-radius: 34px;
        }
         
        .theme-toggle-slider:before {
            position: absolute;
            content: "";
            height: 18px;
            width: 18px;
            left: 4px;
            bottom: 4px;
            background-color: white;
            transition: .4s;
            border-radius: 50%;
        }
         
        input:checked + .theme-toggle-slider {
            background-color: var(--primary-color);
        }
         
        input:checked + .theme-toggle-slider:before {
            transform: translateX(24px);
        }
         
        .theme-icon {
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 16px;
            color: var(--text-color);
        }
         
        /* Form elements for dark mode */
        .form-select, .form-control {
            background-color: var(--input-bg) !important;
            color: var(--input-color) !important;
            border-color: var(--input-border) !important;
        }
         
        .form-select:focus, .form-control:focus {
            border-color: var(--input-focus-border) !important;
            box-shadow: 0 0 0 0.25rem var(--input-focus-shadow) !important;
        }
         
        .form-label {
            color: var(--text-color);
        }
         
        .btn-outline-secondary {
            color: var(--text-color);
            border-color: var(--border-color);
            background-color: transparent;
        }
         
        .btn-outline-secondary:hover {
            background-color: var(--filter-tag-bg);
            color: var(--text-color);
        }
         
        /* Override for dropdown menus and selects */
        .form-select option {
            background-color: var(--input-bg);
            color: var(--input-color);
        }
         
        /* Fix DataTables odd/even row striping */
        table.dataTable.stripe tbody tr.odd,
        table.dataTable.display tbody tr.odd {
            background-color: var(--datatable-odd-row-bg) !important;
        }
         
        table.dataTable.stripe tbody tr.even,
        table.dataTable.display tbody tr.even {
            background-color: var(--datatable-even-row-bg) !important;
        }
         
        /* Fix DataTables background color for hovered rows */
        table.dataTable.hover tbody tr:hover,
        table.dataTable.display tbody tr:hover {
            background-color: var(--table-hover-bg) !important;
        }
         
        /* Fix DataTables border colors */
        table.dataTable.border-bottom,
        table.dataTable.border-top,
        table.dataTable thead th,
        table.dataTable tfoot th,
        table.dataTable thead td,
        table.dataTable tfoot td {
            border-color: var(--table-border-color) !important;
        }
         
        /* Bootstrap 5 DataTables specific overrides */
        .table-striped>tbody>tr:nth-of-type(odd)>* {
            --bs-table-accent-bg: var(--datatable-odd-row-bg) !important;
            color: var(--text-color) !important;
        }
         
        .table>:not(caption)>*>* {
            background-color: var(--card-bg) !important;
            color: var(--text-color) !important;
        }
         
        .table-striped>tbody>tr {
            background-color: var(--datatable-even-row-bg) !important;
        }
         
        /* Direct cell background colors */
        .table tbody tr td {
            background-color: transparent !important;
        }
         
        /* Force Bootstrap Tables to use the correct colors */
        .table-striped>tbody>tr:nth-of-type(odd) {
            --bs-table-accent-bg: var(--datatable-odd-row-bg) !important;
        }
 
        /* Report selector tabs */
        .report-tabs {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            margin-bottom: 20px;
        }
         
        .report-tab {
            padding: 10px 20px;
            border-radius: 5px;
            background-color: var(--button-bg);
            color: var(--button-color);
            border: 1px solid var(--button-border);
            cursor: pointer;
            font-weight: 600;
            transition: all 0.2s;
        }
         
        .report-tab:hover {
            background-color: var(--button-hover-bg);
        }
         
        .report-tab.active {
            background-color: var(--tab-active-bg);
            color: var(--tab-active-color);
            border-color: var(--tab-active-bg);
        }
         
        .report-panel {
            display: none;
        }
         
        .report-panel.active {
            display: block;
        }
    </style>
    </head>
    <body>
        <!-- Dark Mode Toggle -->
        <div class="theme-toggle">
            <div class="theme-icon">
                <i class="fas fa-sun"></i>
            </div>
            <label class="theme-toggle-switch">
                <input type="checkbox" id="themeToggle">
                <span class="theme-toggle-slider"></span>
            </label>
            <div class="theme-icon">
                <i class="fas fa-moon"></i>
            </div>
        </div>
         
        <div class="container-fluid">
            <div class="dashboard-header">
                <div class="dashboard-title">
                    <svg class="logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
                        <path fill="#ff5722" d="M6 6H22V22H6z" transform="rotate(-180 14 14)"/>
                        <path fill="#4caf50" d="M26 6H42V22H26z" transform="rotate(-180 34 14)"/>
                        <path fill="#ffc107" d="M6 26H22V42H6z" transform="rotate(-180 14 34)"/>
                        <path fill="#03a9f4" d="M26 26H42V42H26z" transform="rotate(-180 34 34)"/>
                    </svg>
                    <h1>$TenantName Admin Roles Report</h1>
                </div>
                <div class="report-date">
                    <i class="fas fa-calendar-alt me-2"></i> Report generated on: $ReportDate
                </div>
            </div>
             
            <div class="row mb-4">
                <div class="col-md-3 mb-3">
                    <div class="stats-card permanent-bg" id="permanentFilter">
                        <i class="fas fa-key"></i>
                        <h3>Permanent Assignments</h3>
                        <div class="number">$permanentRoles</div>
                    </div>
                </div>
                <div class="col-md-3 mb-3">
                    <div class="stats-card eligible-bg" id="eligibleFilter">
                        <i class="fas fa-clock"></i>
                        <h3>Eligible Assignments</h3>
                        <div class="number">$eligibleRoles</div>
                    </div>
                </div>
                <div class="col-md-3 mb-3">
                    <div class="stats-card group-bg" id="groupFilter">
                        <i class="fas fa-users"></i>
                        <h3>Group Assignments</h3>
                        <div class="number">$groupAssignedRoles</div>
                    </div>
                </div>
                <div class="col-md-3 mb-3">
                    <div class="stats-card service-principal-bg" id="spFilter">
                        <i class="fas fa-robot"></i>
                        <h3>Service Principal Assignments</h3>
                        <div class="number">$servicePrincipalRoles</div>
                    </div>
                </div>
            </div>
             
            <div class="report-tabs">
            </div>
 
            <div class="filter-section">
                <h5><i class="fas fa-filter me-2"></i>Filter Options</h5>
                 
                <div class="row">
                    <div class="col-md-6">
                        <div class="mb-3">
                            <label for="principalTypeFilter" class="form-label">Principal Type</label>
                            <select id="principalTypeFilter" class="form-select">
                                <option value="">All Types</option>
                                <option value="user">User</option>
                                <option value="group">Group</option>
                                <option value="service Principal">Service Principal</option>
                            </select>
                        </div>
                    </div>
                    <div class="col-md-6">
                        <div class="mb-3">
                            <label for="assignmentTypeFilter" class="form-label">Assignment Type</label>
                            <select id="assignmentTypeFilter" class="form-select">
                                <option value="">All Types</option>
                                <option value="Permanent">Permanent</option>
                                <option value="Eligible">Eligible</option>
                            </select>
                        </div>
                    </div>
                </div>
                 
                <div class="mb-3">
                    <label for="roleNameFilter" class="form-label">Role Name</label>
                    <input type="text" id="roleNameFilter" class="form-control" placeholder="Search for role names...">
                </div>
 
                <div class="mb-3">
                    <label for="scopeFilter" class="form-label">Role Scope</label>
                    <select id="scopeFilter" class="form-select">
                        <option value="">All Scopes</option>
                        <option value="Tenant-Wide">Tenant-Wide</option>
                        <option value="AU/">Administrative Unit</option>
                    </select>
                </div>
                 
                <div class="enabled-filters-container">
                    <div class="d-flex justify-content-between align-items-center">
                        <label class="form-label mb-0">Enabled Filters:</label>
                        <button id="clearAllFilters" class="btn btn-sm btn-outline-secondary">Clear All</button>
                    </div>
                    <div class="filter-tags" id="enabledFilters">
                        <!-- Enabled filters will be displayed here -->
                    </div>
                </div>
            </div>
             
            <div id="all-report" class="report-panel active">
                <div class="card">
                    <div class="card-header">
                        <div>
                            <i class="fas fa-user-shield"></i> All Role Assignments
                        </div>
                        <div class="show-all-container">
                            <label class="toggle-switch">
                                <input type="checkbox" id="allShowAllToggle">
                                <span class="toggle-slider"></span>
                            </label>
                            <p class="show-all-text">Show all entries</p>
                        </div>
                    </div>
                    <div class="card-body">
                        <div class="table-responsive">
                            <table id="allRolesTable" class="table table-striped table-bordered" style="width:100%">
                                <thead>
                                    <tr>
                                        <th>Principal</th>
                                        <th>Display Name</th>
                                        <th>Principal Type</th>
                                        <th>Account Status</th>
                                        <th>Assigned Role</th>
                                        <th>Role Scope</th>
                                        <th>Assignment Type</th>
                                        <th>Start Date</th>
                                        <th>End Date</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    {{ALL_ROLES_DATA}}
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>
            </div>
 
            <div id="user-report" class="report-panel">
                <div class="card">
                    <div class="card-header">
                        <div>
                            <i class="fas fa-user"></i> User Role Assignments
                        </div>
                        <div class="show-all-container">
                            <label class="toggle-switch">
                                <input type="checkbox" id="userShowAllToggle">
                                <span class="toggle-slider"></span>
                            </label>
                            <p class="show-all-text">Show all entries</p>
                        </div>
                    </div>
                    <div class="card-body">
                        <div class="table-responsive">
                            <table id="userRolesTable" class="table table-striped table-bordered" style="width:100%">
                                <thead>
                                    <tr>
                                        <th>Principal</th>
                                        <th>Display Name</th>
                                        <th>Principal Type</th>
                                        <th>Account Status</th>
                                        <th>Assigned Role</th>
                                        <th>Role Scope</th>
                                        <th>Assignment Type</th>
                                        <th>Start Date</th>
                                        <th>End Date</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    {{USER_ROLES_DATA}}
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>
            </div>
 
            <div id="group-report" class="report-panel">
                <div class="card">
                    <div class="card-header">
                        <div>
                            <i class="fas fa-users"></i> Group Role Assignments
                        </div>
                        <div class="show-all-container">
                            <label class="toggle-switch">
                                <input type="checkbox" id="groupShowAllToggle">
                                <span class="toggle-slider"></span>
                            </label>
                            <p class="show-all-text">Show all entries</p>
                        </div>
                    </div>
                    <div class="card-body">
                        <div class="table-responsive">
                            <table id="groupRolesTable" class="table table-striped table-bordered" style="width:100%">
                                <thead>
                                    <tr>
                                        <th>Principal</th>
                                        <th>Display Name</th>
                                        <th>Principal Type</th>
                                        <th>Account Status</th>
                                        <th>Assigned Role</th>
                                        <th>Role Scope</th>
                                        <th>Assignment Type</th>
                                        <th>Start Date</th>
                                        <th>End Date</th>
                                        <th>Members</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    {{GROUP_ROLES_DATA}}
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>
            </div>
 
            <div id="service-principal-report" class="report-panel">
                <div class="card">
                    <div class="card-header">
                        <div>
                            <i class="fas fa-robot"></i> Service Principal Role Assignments
                        </div>
                        <div class="show-all-container">
                            <label class="toggle-switch">
                                <input type="checkbox" id="spShowAllToggle">
                                <span class="toggle-slider"></span>
                            </label>
                            <p class="show-all-text">Show all entries</p>
                        </div>
                    </div>
                    <div class="card-body">
                        <div class="table-responsive">
                            <table id="spRolesTable" class="table table-striped table-bordered" style="width:100%">
                                <thead>
                                    <tr>
                                        <th>Principal</th>
                                        <th>Display Name</th>
                                        <th>Principal Type</th>
                                        <th>Account Status</th>
                                        <th>Assigned Role</th>
                                        <th>Role Scope</th>
                                        <th>Assignment Type</th>
                                        <th>Start Date</th>
                                        <th>End Date</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    {{SP_ROLES_DATA}}
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>
            </div>
        </div>
         
        <footer>
            <p>Generated by Roy Klooster - RK Solutions</p>
        </footer>
         
        <script>
            // Initialize DataTables
            $(document).ready(function() {
                // Theme toggling functionality
                const themeToggle = document.getElementById('themeToggle');
                const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
                 
                // Function to update table colors for dark mode
                function updateTableColors() {
                    // Force all table cells to have the correct background
                    if (document.documentElement.getAttribute('data-theme') === 'dark') {
                        // Dark mode
                        $('table.dataTable tbody tr').css('background-color', 'var(--datatable-even-row-bg)');
                        $('table.dataTable tbody tr:nth-child(odd)').css('background-color', 'var(--datatable-odd-row-bg)');
                        $('table.dataTable tbody td').css('color', 'var(--text-color)');
                        $('table.dataTable thead th').css({
                            'background-color': 'var(--table-header-bg)',
                            'color': 'var(--table-header-color)'
                        });
                    } else {
                        // Light mode
                        $('table.dataTable tbody tr').css('background-color', 'var(--datatable-even-row-bg)');
                        $('table.dataTable tbody tr:nth-child(odd)').css('background-color', 'var(--datatable-odd-row-bg)');
                        $('table.dataTable tbody td').css('color', 'var(--text-color)');
                        $('table.dataTable thead th').css({
                            'background-color': 'var(--table-header-bg)',
                            'color': 'var(--table-header-color)'
                        });
                    }
                }
                 
                // Check for saved user preference, or use system preference
                const savedTheme = localStorage.getItem('theme');
                if (savedTheme === 'dark' || (!savedTheme && prefersDarkScheme.matches)) {
                    document.documentElement.setAttribute('data-theme', 'dark');
                    themeToggle.checked = true;
                }
                 
                // Add event listener for theme toggle
                themeToggle.addEventListener('change', function() {
                    if (this.checked) {
                        document.documentElement.setAttribute('data-theme', 'dark');
                        localStorage.setItem('theme', 'dark');
                    } else {
                        document.documentElement.setAttribute('data-theme', 'light');
                        localStorage.setItem('theme', 'light');
                    }
                     
                    // Apply the table color changes after theme switch
                    setTimeout(updateTableColors, 50);
                });
                 
                // Tab switching functionality
                $('.report-tab').on('click', function() {
                    // Remove active class from all tabs and panels
                    $('.report-tab').removeClass('active');
                    $('.report-panel').removeClass('active');
                     
                    // Add active class to clicked tab and corresponding panel
                    $(this).addClass('active');
                    const panelId = $(this).data('panel');
                    $(`#${panelId}`).addClass('active');
                     
                    // Adjust DataTables columns when switching tabs
                    setTimeout(function() {
                        $.fn.dataTable.tables({ visible: true, api: true }).columns.adjust();
                    }, 10);
                });
                 
                // Initialize DataTable for all roles
                const allRolesTable = $('#allRolesTable').DataTable({
                    dom: 'Bfrtip',
                    buttons: [
                        {
                            extend: 'collection',
                            text: '<i class="fas fa-download"></i> Export',
                            buttons: [
                                {
                                    extend: 'excel',
                                    text: '<i class="fas fa-file-excel"></i> Excel',
                                    exportOptions: {
                                        columns: ':visible'
                                    }
                                },
                                {
                                    extend: 'csv',
                                    text: '<i class="fas fa-file-csv"></i> CSV',
                                    exportOptions: {
                                        columns: ':visible'
                                    }
                                },
                                {
                                    extend: 'pdf',
                                    text: '<i class="fas fa-file-pdf"></i> PDF',
                                    exportOptions: {
                                        columns: ':visible'
                                    }
                                },
                                {
                                    extend: 'print',
                                    text: '<i class="fas fa-print"></i> Print',
                                    exportOptions: {
                                        columns: ':visible'
                                    }
                                }
                            ]
                        },
                        {
                            extend: 'colvis',
                            text: '<i class="fas fa-columns"></i> Columns'
                        }
                    ],
                    paging: true,
                    searching: true,
                    ordering: true,
                    info: true,
                    responsive: true,
                    lengthMenu: [[10, 25, 50, 100, -1], [10, 25, 50, 100, "All"]],
                    order: [[3, 'asc']],
                    language: {
                        search: "<i class='fas fa-search'></i> _INPUT_",
                        searchPlaceholder: "Search records...",
                        lengthMenu: "Show _MENU_ entries",
                        info: "Showing _START_ to _END_ of _TOTAL_ entries",
                        paginate: {
                            first: "<i class='fas fa-angle-double-left'></i>",
                            last: "<i class='fas fa-angle-double-right'></i>",
                            next: "<i class='fas fa-angle-right'></i>",
                            previous: "<i class='fas fa-angle-left'></i>"
                        }
                    },
                    drawCallback: function() {
                        updateTableColors();
                    }
                });
                 
                // Initialize DataTable for user roles
                const userRolesTable = $('#userRolesTable').DataTable({
                    dom: 'Bfrtip',
                    buttons: [
                        {
                            extend: 'collection',
                            text: '<i class="fas fa-download"></i> Export',
                            buttons: [
                                { extend: 'excel', text: '<i class="fas fa-file-excel"></i> Excel', exportOptions: { columns: ':visible' } },
                                { extend: 'csv', text: '<i class="fas fa-file-csv"></i> CSV', exportOptions: { columns: ':visible' } },
                                { extend: 'pdf', text: '<i class="fas fa-file-pdf"></i> PDF', exportOptions: { columns: ':visible' } },
                                { extend: 'print', text: '<i class="fas fa-print"></i> Print', exportOptions: { columns: ':visible' } }
                            ]
                        },
                        { extend: 'colvis', text: '<i class="fas fa-columns"></i> Columns' }
                    ],
                    paging: true, searching: true, ordering: true, info: true, responsive: true,
                    lengthMenu: [[10, 25, 50, 100, -1], [10, 25, 50, 100, "All"]],
                    order: [[3, 'asc']],
                    language: { search: "<i class='fas fa-search'></i> _INPUT_", searchPlaceholder: "Search records..." },
                    drawCallback: function() { updateTableColors(); }
                });
 
                // Initialize DataTable for group roles
                const groupRolesTable = $('#groupRolesTable').DataTable({
                    dom: 'Bfrtip',
                    buttons: [
                        {
                            extend: 'collection',
                            text: '<i class="fas fa-download"></i> Export',
                            buttons: [
                                { extend: 'excel', text: '<i class="fas fa-file-excel"></i> Excel', exportOptions: { columns: ':visible' } },
                                { extend: 'csv', text: '<i class="fas fa-file-csv"></i> CSV', exportOptions: { columns: ':visible' } },
                                { extend: 'pdf', text: '<i class="fas fa-file-pdf"></i> PDF', exportOptions: { columns: ':visible' } },
                                { extend: 'print', text: '<i class="fas fa-print"></i> Print', exportOptions: { columns: ':visible' } }
                            ]
                        },
                        { extend: 'colvis', text: '<i class="fas fa-columns"></i> Columns' }
                    ],
                    paging: true, searching: true, ordering: true, info: true, responsive: true,
                    lengthMenu: [[10, 25, 50, 100, -1], [10, 25, 50, 100, "All"]],
                    order: [[3, 'asc']],
                    language: { search: "<i class='fas fa-search'></i> _INPUT_", searchPlaceholder: "Search records..." },
                    drawCallback: function() { updateTableColors(); }
                });
 
                // Initialize DataTable for service principal roles
                const spRolesTable = $('#spRolesTable').DataTable({
                    dom: 'Bfrtip',
                    buttons: [
                        {
                            extend: 'collection',
                            text: '<i class="fas fa-download"></i> Export',
                            buttons: [
                                { extend: 'excel', text: '<i class="fas fa-file-excel"></i> Excel', exportOptions: { columns: ':visible' } },
                                { extend: 'csv', text: '<i class="fas fa-file-csv"></i> CSV', exportOptions: { columns: ':visible' } },
                                { extend: 'pdf', text: '<i class="fas fa-file-pdf"></i> PDF', exportOptions: { columns: ':visible' } },
                                { extend: 'print', text: '<i class="fas fa-print"></i> Print', exportOptions: { columns: ':visible' } }
                            ]
                        },
                        { extend: 'colvis', text: '<i class="fas fa-columns"></i> Columns' }
                    ],
                    paging: true, searching: true, ordering: true, info: true, responsive: true,
                    lengthMenu: [[10, 25, 50, 100, -1], [10, 25, 50, 100, "All"]],
                    order: [[3, 'asc']],
                    language: { search: "<i class='fas fa-search'></i> _INPUT_", searchPlaceholder: "Search records..." },
                    drawCallback: function() { updateTableColors(); }
                });
                 
                // Apply initial table colors
                setTimeout(updateTableColors, 100);
                 
                // Show all toggle functionality
                $('#allShowAllToggle').on('change', function() {
                    if ($(this).is(':checked')) {
                        allRolesTable.page.len(-1).draw();
                    } else {
                        allRolesTable.page.len(10).draw();
                    }
                });
                 
                $('#userShowAllToggle').on('change', function() {
                    if ($(this).is(':checked')) {
                        userRolesTable.page.len(-1).draw();
                    } else {
                        userRolesTable.page.len(10).draw();
                    }
                });
 
                $('#groupShowAllToggle').on('change', function() {
                    if ($(this).is(':checked')) {
                        groupRolesTable.page.len(-1).draw();
                    } else {
                        groupRolesTable.page.len(10).draw();
                    }
                });
 
                $('#spShowAllToggle').on('change', function() {
                    if ($(this).is(':checked')) {
                        spRolesTable.page.len(-1).draw();
                    } else {
                        spRolesTable.page.len(10).draw();
                    }
                });
                 
                // Custom filtering function for all tables
                $.fn.dataTable.ext.search.push(
                    function(settings, data, dataIndex) {
                        // Get filter values
                        const principalType = $('#principalTypeFilter').val().toLowerCase();
                        const assignmentType = $('#assignmentTypeFilter').val();
                        const roleName = $('#roleNameFilter').val().toLowerCase();
                        const scopeFilter = $('#scopeFilter').val();
                         
                        // Get row data with CORRECTED column indices
                        const colPrincipalType = data[2].toLowerCase(); // Principal Type column (index 2)
                        const colRole = data[4].toLowerCase(); // Assigned Role column (index 4)
                        const colAssignmentType = data[6]; // Assignment Type column (index 6)
                        const colScope = data[5]; // Scope column (index 5)
                         
                        // Filter by principal type
                        if (principalType && !colPrincipalType.includes(principalType)) {
                            return false;
                        }
                         
                        // Filter by assignment type
                        if (assignmentType && !colAssignmentType.includes(assignmentType)) {
                            return false;
                        }
                         
                        // Filter by role name
                        if (roleName && !colRole.includes(roleName)) {
                            return false;
                        }
                         
                        // Filter by scope
                        if (scopeFilter && !colScope.toLowerCase().includes(scopeFilter.toLowerCase())) {
                        return false;
                        }
                         
                        return true;
                    }
                );
                 
                // Stats card filtering
                $('#permanentFilter').on('click', function() {
                    $('#assignmentTypeFilter').val('Permanent');
                    updateEnabledFilters('Assignment Type', 'Permanent');
                    applyFilters();
                    toggleStatsCardEnabled('permanentFilter');
                });
                 
                $('#eligibleFilter').on('click', function() {
                    $('#assignmentTypeFilter').val('Eligible');
                    updateEnabledFilters('Assignment Type', 'Eligible');
                    applyFilters();
                    toggleStatsCardEnabled('eligibleFilter');
                });
                 
                $('#groupFilter').on('click', function() {
                    $('#principalTypeFilter').val('group');
                    updateEnabledFilters('Principal Type', 'Group');
                    applyFilters();
                    toggleStatsCardEnabled('groupFilter');
                     
                    // Switch to group report tab
                    $('.report-tab[data-panel="group-report"]').click();
                });
                 
                $('#spFilter').on('click', function() {
                    $('#principalTypeFilter').val('service Principal');
                    updateEnabledFilters('Principal Type', 'Service Principal');
                    applyFilters();
                    toggleStatsCardEnabled('spFilter');
                     
                    // Switch to service principal report tab
                    $('.report-tab[data-panel="service-principal-report"]').click();
                });
                 
                // Apply filters when select boxes change
                $('#principalTypeFilter, #assignmentTypeFilter, #scopeFilter').on('change', function() {
                    const filterType = $(this).attr('id');
                    const filterValue = $(this).val();
                     
                    if (filterValue) {
                        if (filterType === 'principalTypeFilter') {
                            updateEnabledFilters('Principal Type', filterValue);
                        } else if (filterType === 'assignmentTypeFilter') {
                            updateEnabledFilters('Assignment Type', filterValue);
                        } else if (filterType === 'scopeFilter') {
                            updateEnabledFilters('Scope', filterValue);
                        }
                    } else {
                        if (filterType === 'principalTypeFilter') {
                            removeEnabledFilter('Principal Type');
                        } else if (filterType === 'assignmentTypeFilter') {
                            removeEnabledFilter('Assignment Type');
                        } else if (filterType === 'scopeFilter') {
                            removeEnabledFilter('Scope');
                        }
                    }
                     
                    applyFilters();
                });
                 
                // Apply filter when role name input changes
                $('#roleNameFilter').on('input', function() {
                    const filterValue = $(this).val();
                     
                    if (filterValue) {
                        updateEnabledFilters('Role Name', filterValue);
                    } else {
                        removeEnabledFilter('Role Name');
                    }
                     
                    applyFilters();
                });
                 
                // Clear all filters button
                $('#clearAllFilters').on('click', function() {
                    // Reset all filter inputs
                    $('#principalTypeFilter').val('');
                    $('#assignmentTypeFilter').val('');
                    $('#roleNameFilter').val('');
                    $('#scopeFilter').val('');
                     
                    // Remove active state from stats cards
                    $('.stats-card').removeClass('active');
                     
                    // Clear the visible filter tags
                    clearEnabledFilters();
                     
                    // This is critical - force DataTables to redraw with cleared filters
                    allRolesTable.search('').columns().search('').draw();
                    userRolesTable.search('').columns().search('').draw();
                    groupRolesTable.search('').columns().search('').draw();
                    spRolesTable.search('').columns().search('').draw();
                     
                    // Apply the filters (with all values cleared)
                    applyFilters();
 
                    // IMPORTANT: Switch back to "All Assignments" tab
                    $('.report-tab[data-panel="all-report"]').click();
                });
                 
                // Function to apply all filters
                function applyFilters() {
                    allRolesTable.draw();
                    userRolesTable.draw();
                    groupRolesTable.draw();
                    spRolesTable.draw();
                }
                 
                // Function to toggle stats card active state
                function toggleStatsCardEnabled(cardId) {
                    $('.stats-card').removeClass('active');
                    $('#' + cardId).addClass('active');
                }
                 
                // Function to update enabled filters
                function updateEnabledFilters(filterType, filterValue) {
                    // Remove existing filter of the same type
                    removeEnabledFilter(filterType);
                     
                    // Add new filter tag
                    const filterTag = `
                        <div class="filter-tag" data-filter-type="${filterType}">
                            <span>${filterType}: ${filterValue}</span>
                            <i class="fas fa-times-circle remove-filter" data-filter-type="${filterType}"></i>
                        </div>
                    `;
                     
                    $('#enabledFilters').append(filterTag);
                     
                    // Add click handler to remove filter
                    $('.remove-filter').off('click').on('click', function() {
                        const filterTypeToRemove = $(this).data('filter-type');
                         
                        if (filterTypeToRemove === 'Principal Type') {
                            $('#principalTypeFilter').val('');
                        } else if (filterTypeToRemove === 'Assignment Type') {
                            $('#assignmentTypeFilter').val('');
                        } else if (filterTypeToRemove === 'Role Name') {
                            $('#roleNameFilter').val('');
                        } else if (filterTypeToRemove === 'Scope') {
                            $('#scopeFilter').val('');
                        }
                         
                        $(this).closest('.filter-tag').remove();
                         
                        // Remove active state from stat cards
                        if (filterTypeToRemove === 'Assignment Type') {
                            if ($('#permanentFilter').hasClass('active') || $('#eligibleFilter').hasClass('active')) {
                                $('#permanentFilter, #eligibleFilter').removeClass('active');
                            }
                        } else if (filterTypeToRemove === 'Principal Type') {
                            if ($('#groupFilter').hasClass('active') || $('#spFilter').hasClass('active')) {
                                $('#groupFilter, #spFilter').removeClass('active');
                            }
                        }
                         
                        applyFilters();
                    });
                }
                 
                // Function to remove enabled filter by type
                function removeEnabledFilter(filterType) {
                    $('.filter-tag[data-filter-type="' + filterType + '"]').remove();
                }
                 
                // Function to clear all enabled filters
                function clearEnabledFilters() {
                    $('#enabledFilters').empty();
                }
                 
                // Force dark mode to take effect on page elements
                $(window).on('load', function() {
                    setTimeout(updateTableColors, 200);
                });
                 
                // Re-apply styles after DataTables operations
                allRolesTable.on('draw.dt', function() {
                    setTimeout(updateTableColors, 50);
                });
                 
                userRolesTable.on('draw.dt', function() {
                    setTimeout(updateTableColors, 50);
                });
                 
                groupRolesTable.on('draw.dt', function() {
                    setTimeout(updateTableColors, 50);
                });
                 
                spRolesTable.on('draw.dt', function() {
                    setTimeout(updateTableColors, 50);
                });
            });
        </script>
    </body>
    </html>
'@

    
    # Generate table rows for all role assignments
    $allRolesRows = ""
    foreach ($item in $Report) {
        $assignmentTypeBadge = switch ($item.AssignmentType) {
            "Permanent" { '<span class="badge badge-permanent">Permanent</span>' }
            "Eligible" { '<span class="badge badge-eligible">Eligible</span>' }
            default { '<span class="badge bg-secondary">Unknown</span>' }
        }
        
        $allRolesRows += @"
        <tr>
            <td>$($item.Principal)</td>
            <td>$($item.DisplayName)</td>
            <td>$($item.PrincipalType)</td>
            <td>$($item.AccountStatus)</td>
            <td>$($item.'Assigned Role')</td>
            <td>$($item.AssignedRoleScopeName)</td>
            <td>$assignmentTypeBadge</td>
            <td>$($item.AssignmentStartDate)</td>
            <td>$($item.AssignmentEndDate)</td>
        </tr>
"@

    }

    # Generate table rows for user role assignments
    $userRolesRows = ""
    foreach ($item in $UserAssignmentReport) {
        $assignmentTypeBadge = switch ($item.AssignmentType) {
            "Permanent" { '<span class="badge badge-permanent">Permanent</span>' }
            "Eligible" { '<span class="badge badge-eligible">Eligible</span>' }
            default { '<span class="badge bg-secondary">Unknown</span>' }
        }
        
        $userRolesRows += @"
    <tr>
        <td>$($item.Principal)</td>
        <td>$($item.DisplayName)</td>
        <td>$($item.PrincipalType)</td>
        <td>$($item.AccountStatus)</td>
        <td>$($item.'Assigned Role')</td>
        <td>$($item.AssignedRoleScopeName)</td>
        <td>$assignmentTypeBadge</td>
        <td>$($item.AssignmentStartDate)</td>
        <td>$($item.AssignmentEndDate)</td>
    </tr>
"@

    }

    # Generate table rows for group role assignments
    $groupRolesRows = ""
    foreach ($item in $GroupAssignmentReport) {
        $assignmentTypeBadge = switch ($item.AssignmentType) {
            "Permanent" { '<span class="badge badge-permanent">Permanent</span>' }
            "Eligible" { '<span class="badge badge-eligible">Eligible</span>' }
            default { '<span class="badge bg-secondary">Unknown</span>' }
        }
        
        # Get group members from the overview report
        $groupMembers = ($GroupMembershipOverviewReport | Where-Object { $_.Principal -eq $item.Principal }).Members
        if (-not $groupMembers) {
            $groupMembers = "None"
        }
        
        $groupRolesRows += @"
        <tr>
            <td>$($item.Principal)</td>
            <td>$($item.DisplayName)</td>
            <td>$($item.PrincipalType)</td>
            <td>$($item.AccountStatus)</td>
            <td>$($item.'Assigned Role')</td>
            <td>$($item.AssignedRoleScopeName)</td>
            <td>$assignmentTypeBadge</td>
            <td>$($item.AssignmentStartDate)</td>
            <td>$($item.AssignmentEndDate)</td>
            <td>$groupMembers</td>
        </tr>
"@

    }

    # Generate table rows for service principal role assignments
    $spRolesRows = ""
    foreach ($item in $ServicePrincipalReport) {
        $assignmentTypeBadge = switch ($item.AssignmentType) {
            "Permanent" { '<span class="badge badge-permanent">Permanent</span>' }
            "Eligible" { '<span class="badge badge-eligible">Eligible</span>' }
            default { '<span class="badge bg-secondary">Unknown</span>' }
        }
        
        $spRolesRows += @"
    <tr>
        <td>$($item.Principal)</td>
        <td>$($item.DisplayName)</td>
        <td>$($item.PrincipalType)</td>
        <td>$($item.AccountStatus)</td>
        <td>$($item.'Assigned Role')</td>
        <td>$($item.AssignedRoleScopeName)</td>
        <td>$assignmentTypeBadge</td>
        <td>$($item.AssignmentStartDate)</td>
        <td>$($item.AssignmentEndDate)</td>
    </tr>
"@

    }

    # Replace placeholders in template with actual values
    $htmlContent = $htmlTemplate
    $htmlContent = $htmlContent.Replace('$TenantName', $TenantName)
    $htmlContent = $htmlContent.Replace('$ReportDate', $currentDate)
    $htmlContent = $htmlContent.Replace('$permanentRoles', $permanentRoles)
    $htmlContent = $htmlContent.Replace('$eligibleRoles', $eligibleRoles)
    $htmlContent = $htmlContent.Replace('$groupAssignedRoles', $groupAssignedRoles)
    $htmlContent = $htmlContent.Replace('$servicePrincipalRoles', $servicePrincipalRoles)

    # Add report tabs based on available data
    $reportTabs = @"
<div class="report-tab active" data-panel="all-report" style="display:none;">All Assignments</div>
"@

    
    if ($UserAssignmentReport.Count -gt 0) {
        $reportTabs += @"
<div class="report-tab" data-panel="user-report" style="display:none;">User Assignments</div>
"@

    }
    
    if ($GroupAssignmentReport.Count -gt 0) {
        $reportTabs += @"
<div class="report-tab" data-panel="group-report" style="display:none;">Group Assignments</div>
"@

    }
    
    if ($ServicePrincipalReport.Count -gt 0) {
        $reportTabs += @"
<div class="report-tab" data-panel="service-principal-report" style="display:none;">Service Principal Assignments</div>
"@

    }

    
    $htmlContent = $htmlContent.Replace('<div class="report-tabs">', "<div class=`"report-tabs`">`n$reportTabs")

    # Replace table data placeholders
    $htmlContent = $htmlContent.Replace('{{ALL_ROLES_DATA}}', $allRolesRows)
    $htmlContent = $htmlContent.Replace('{{USER_ROLES_DATA}}', $userRolesRows)
    $htmlContent = $htmlContent.Replace('{{GROUP_ROLES_DATA}}', $groupRolesRows)
    $htmlContent = $htmlContent.Replace('{{SP_ROLES_DATA}}', $spRolesRows)

    # Add additional CSS for dark mode pagination
    $darkModePaginationCss = @"
    <style>
        [data-theme="dark"] .page-item.active .page-link {
            color: white !important;
            border-color: var(--primary-color) !important;
        }
         
        [data-theme="dark"] .page-item.disabled .page-link {
            background-color: var(--card-bg) !important;
            color: #6c757d !important;
            border-color: var(--border-color) !important;
        }
         
        [data-theme="dark"] .dataTables_paginate .paginate_button {
            background-color: var(--button-bg) !important;
            color: var(--text-color) !important;
            border-color: var(--border-color) !important;
        }
         
        [data-theme="dark"] .dataTables_paginate .paginate_button.current,
        [data-theme="dark"] .dataTables_paginate .paginate_button.current:hover {
            background: var(--primary-color) !important;
            color: white !important;
            border-color: var(--primary-color) !important;
        }
         
        [data-theme="dark"] .dataTables_paginate .paginate_button:hover {
            background: var(--button-hover-bg) !important;
            color: var(--text-color) !important;
            border-color: var(--button-border) !important;
        }
         
        [data-theme="dark"] .dataTables_paginate .paginate_button.disabled,
        [data-theme="dark"] .dataTables_paginate .paginate_button.disabled:hover {
            background-color: var(--card-bg) !important;
            color: #6c757d !important;
            border-color: var(--border-color) !important;
            opacity: 0.6;
        }
    </style>
"@


    # Insert the dark mode pagination CSS before the </head> tag
    $htmlContent = $htmlContent.Replace('</head>', "$darkModePaginationCss`n</head>")

    # Export to HTML file
    $htmlContent | Out-File -FilePath $ExportPath -Encoding utf8

    Write-Host "INFO: All actions completed successfully."
    Write-Host "INFO: Admin Roles Report saved to: $ExportPath" -ForegroundColor Cyan

    # Open the HTML file
    Invoke-Item $ExportPath
}


function Invoke-GraphRequestWithPaging {
    param (
        [string]$Uri,
        [string]$Method = "GET"
    )
    
    $results = @()
    $currentUri = $Uri
    
    do {
        try {
            $response = Invoke-MgGraphRequest -Uri $currentUri -Method $Method -OutputType PSObject -ErrorAction Stop
            
            # Add the current page of results
            if ($response.value) {
                $results += $response.value
            }
            
            # Get the next page URL if it exists
            $currentUri = $response.'@odata.nextLink'
        } catch {
            # Instead of Write-Error, set a flag or return null to indicate failure
            # We'll handle this in the calling code
            return $null
        }
    } while ($currentUri)
    
    return $results
}

Function Install-Requirements {

    $requiredModules = @(
        "Microsoft.Graph.Authentication"
    )

    foreach ($module in $requiredModules) {
        $moduleVersion = "2.25.0"
        $moduleInstalled = Get-Module -ListAvailable -Name $module | Where-Object { $_.Version -eq $moduleVersion }
        
        if (-not $moduleInstalled) {
            Write-Host "INFO: Installing module: $module version $moduleVersion"
            Install-Module -Name $module -Scope CurrentUser -Force -AllowClobber -RequiredVersion $moduleVersion -SkipPublisherCheck
        } else {
            Write-Host "INFO: Module $module version $moduleVersion is already installed."
        }

        # Import the required modules
        foreach ($module in $requiredModules) {
            $moduleVersion = "2.25.0"
            $importedModule = Get-Module -Name $module | Where-Object { $_.Version -eq $moduleVersion }
            
            if (-not $importedModule) {
                try {
                    Import-Module -Name $module -RequiredVersion $moduleVersion -Force -ErrorAction Stop
                    Write-Host "INFO: Module $module version $moduleVersion imported successfully."
                } catch {
                    Write-Host "ERROR: Failed to import module $module version $moduleVersion. Error: $_" -ForegroundColor Red
                    throw
                }
            } else {
                Write-Host "INFO: Module $module version $moduleVersion was already imported."
            }
        }
    }
}

# function Test-MgGraphConnection {
# param (
# [string[]]$RequiredScopes
# )

# # Check if already connected
# $contextInfo = Get-MgContext -ErrorAction SilentlyContinue
    
# if (-not $contextInfo) {
# # If not connected, connect now
# Connect-MgGraph -Scopes $RequiredScopes -NoWelcome
# return $true
# } else {
# # Check if we have all the required permissions
# $currentScopes = $contextInfo.Scopes
# $missingScopes = $RequiredScopes | Where-Object { $_ -notin $currentScopes }
        
# if ($missingScopes) {
# Write-Host "Missing required scopes: $($missingScopes -join ', ')" -ForegroundColor Yellow
# Write-Host "Reconnecting with all required scopes..." -ForegroundColor Yellow
# Connect-MgGraph -Scopes $RequiredScopes -NoWelcome
# return $true
# } else {
# Write-Host "Already connected to Microsoft Graph as $($contextInfo.Account) with all required scopes." -ForegroundColor Green
# return $false
# }
# }
# }

function Connect-ToMgGraph {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false)]
        [string[]]$RequiredScopes = @("User.Read"),
        
        [Parameter(Mandatory = $false)]
        [ValidateSet("Interactive", "ClientSecret", "Certificate")]
        [string]$AuthMethod = "Interactive",
        
        [Parameter(Mandatory = $false)]
        [string]$TenantId,
        
        [Parameter(Mandatory = $false)]
        [string]$ClientId,
        
        [Parameter(Mandatory = $false)]
        [string]$ClientSecret,
        
        [Parameter(Mandatory = $false)]
        [string]$CertificateThumbprint
    )
    
    # Check if Microsoft Graph module is installed and import it
    Install-Requirements
    
    # Check if already connected
    $contextInfo = Get-MgContext -ErrorAction SilentlyContinue
    $reconnect = $false
    
    if ($contextInfo) {
        # Check if we have all the required permissions for interactive auth
        if ($AuthMethod -eq "Interactive") {
            $currentScopes = $contextInfo.Scopes
            $missingScopes = $RequiredScopes | Where-Object { $_ -notin $currentScopes }
            
            if ($missingScopes) {
                Write-Verbose "Missing required scopes: $($missingScopes -join ', ')"
                Write-Verbose "Reconnecting with all required scopes..."
                $reconnect = $true
            } else {
                Write-Verbose "Already connected to Microsoft Graph as $($contextInfo.Account) with all required scopes."
                return $contextInfo
            }
        }
        # For app auth methods, we'll just disconnect and reconnect with the new credentials
        elseif ($AuthMethod -in @("ClientSecret", "Certificate")) {
            Write-Verbose "Switching to $AuthMethod authentication method..."
            Disconnect-MgGraph -ErrorAction SilentlyContinue
            $reconnect = $true
        }
    } else {
        $reconnect = $true
    }
    
    if ($reconnect) {
        try {
            # Define connection parameters based on authentication method
            switch ($AuthMethod) {
                "Interactive" {
                    Write-Verbose "Connecting to Microsoft Graph using interactive authentication..."
                    Connect-MgGraph -Scopes $RequiredScopes -NoWelcome
                }
                "ClientSecret" {
                    # Validate required parameters
                    if (-not $TenantId -or -not $ClientId -or -not $ClientSecret) {
                        throw "TenantId, ClientId, and ClientSecret are required for ClientSecret authentication"
                    }
                    
                    Write-Verbose "Connecting to Microsoft Graph using client secret authentication..."
                    $secureSecret = ConvertTo-SecureString -String $ClientSecret -AsPlainText -Force
                    $clientSecretCredential = New-Object System.Management.Automation.PSCredential($ClientId, $secureSecret)
                    Connect-MgGraph -TenantId $TenantId -ClientSecretCredential $clientSecretCredential -NoWelcome
                }
                "Certificate" {
                    # Validate required parameters
                    if (-not $TenantId -or -not $ClientId -or -not $CertificateThumbprint) {
                        throw "TenantId, ClientId, and CertificateThumbprint are required for Certificate authentication"
                    }
                    
                    Write-Verbose "Connecting to Microsoft Graph using certificate authentication..."
                    Connect-MgGraph -TenantId $TenantId -ClientId $ClientId -CertificateThumbprint $CertificateThumbprint -NoWelcome
                }
            }
            
            # Verify connection
            $newContext = Get-MgContext
            if ($newContext) {
                $authDetails = switch ($AuthMethod) {
                    "Interactive" { "as $($newContext.Account)" }
                    "ClientSecret" { "using client secret (App ID: $ClientId)" }
                    "Certificate" { "using certificate authentication (App ID: $ClientId)" }
                }
                
                Write-Verbose "Successfully connected to Microsoft Graph $authDetails"
                return $newContext
            } else {
                throw "Connection attempt completed but unable to confirm connection"
            }
        } catch {
            Write-Error "Error connecting to Microsoft Graph: $_"
            return $null
        }
    }
    
    return $contextInfo
}

function Get-SecurityGroups {
    param (
        [switch]$Verbose
    )
    
    $securityGroups = Invoke-GraphRequestWithPaging -Uri "beta/groups?`$filter=isassignabletorole eq true" -Method Get
    if ($Verbose) {
        Write-Verbose "Found $($securityGroups.Count) security groups that are assignable to roles"
    } Else {
        Write-Host "INFO: Found $($securityGroups.Count) security groups that are assignable to roles"
    }

    # Collect members for each security group for later reference
    $securityGroupMembers = @{}
    foreach ($group in $securityGroups) {
        if ($Verbose) {
            Write-Verbose "Collecting members for security group: $($group.displayName)"
        } Else {
            Write-Host "INFO: Collecting members for security group: $($group.displayName)"
        }
        
        try {
            $members = Invoke-GraphRequestWithPaging -Uri "beta/groups/$($group.id)/transitiveMembers?`$select=id,displayName,userPrincipalName" -Method Get
            
            # Create member list with useful information
            $memberList = @()
            foreach ($member in $members) {
                # Handle different member types
                if ($member.'@odata.type' -eq '#microsoft.graph.user') {
                    $memberList += [PSCustomObject]@{
                        Type              = "User"
                        Id                = $member.id
                        DisplayName       = $member.displayName
                        UserPrincipalName = $member.userPrincipalName
                    }
                } Elseif ($member.'@odata.type' -eq '#microsoft.graph.group') {
                    $memberList += [PSCustomObject]@{
                        Type        = "Group"
                        Id          = $member.id
                        DisplayName = $member.displayName
                    }
                } Else {
                    $memberList += [PSCustomObject]@{
                        Type        = $member.'@odata.type'.Replace('#microsoft.graph.', '')
                        Id          = $member.id
                        DisplayName = $member.displayName
                    }
                }
            }
            
            # Store the members in the hashtable
            $securityGroupMembers[$group.id] = @{
                GroupDisplayName = $group.displayName
                GroupId          = $group.id
                Members          = $memberList
                MemberCount      = $memberList.Count
            }
            
        } catch {
            Write-Error "ERROR: Collecting members for group $($group.displayName): $_"
            continue
        }
    }
    
    # Return the security group members
    return $securityGroupMembers
}

#Determine the required scopes, based on the parameters passed to the script
$RequiredScopes = @("Directory.Read.All", "PrivilegedEligibilitySchedule.Read.AzureADGroup")

if ($IncludePrivilegedAssignments) {
    $RequiredScopes += "RoleManagement.Read.Directory"
}
#Connect to the Graph API
Write-Verbose "Connecting to Graph API..."
try {
    $connectionParams = @{
        RequiredScopes = $RequiredScopes
        AuthMethod     = $AuthMethod
        Verbose        = $VerbosePreference -eq 'Continue'
    }
    
    # Add optional parameters based on authentication method
    if ($AuthMethod -eq "ClientSecret") {
        if (-not $TenantId -or -not $ClientId -or -not $ClientSecret) {
            throw "TenantId, ClientId, and ClientSecret are required for ClientSecret authentication"
        }
        $connectionParams.TenantId = $TenantId
        $connectionParams.ClientId = $ClientId
        $connectionParams.ClientSecret = $ClientSecret
    } elseif ($AuthMethod -eq "Certificate") {
        if (-not $TenantId -or -not $ClientId -or -not $CertificateThumbprint) {
            throw "TenantId, ClientId, and CertificateThumbprint are required for Certificate authentication"
        }
        $connectionParams.TenantId = $TenantId
        $connectionParams.ClientId = $ClientId
        $connectionParams.CertificateThumbprint = $CertificateThumbprint
    }
    
    $connected = Connect-ToMgGraph @connectionParams

    if ($connected) {
        Write-Verbose "Connected to Graph API successfully."

        # Get Tenant Name
        $tenantInfo = Invoke-MgGraphRequest -Uri "beta/organization" -Method Get -OutputType PSObject
        $tenantname = $tenantInfo.value[0].displayName

        # Call the function to get the security group members
        $securityGroupMembers = Get-SecurityGroups

        $adminUnits = Invoke-GraphRequestWithPaging -Uri "beta/directory/administrativeUnits" -Method Get
        $auLookup = @{}
        foreach ($au in $adminUnits) {
            # The directoryScopeId format is: "/administrativeUnits/{id}"
            $auId = "/administrativeUnits/$($au.id)"
            $auLookup[$auId] = $au.displayName
        }

        if ($adminUnits) {
            Write-Host "INFO: Found administrative units in the tenant."
        } else {
            Write-Host "INFO: No administrative units found in the tenant."
        }

        # Get role assignments with principal expansion
        $rolesWithPrincipal = Invoke-GraphRequestWithPaging -Uri "beta/roleManagement/directory/roleAssignments?`$expand=principal" -Method Get
        # Get role assignments with roleDefinition expansion
        $rolesWithDefinition = Invoke-GraphRequestWithPaging -Uri "beta/roleManagement/directory/roleAssignments?`$expand=roleDefinition" -Method Get

        # Merge the data for complete role assignment information
        $roles = @()
        foreach ($role in $rolesWithPrincipal) {
            $roleDefinition = $rolesWithDefinition | Where-Object { $_.id -eq $role.id } | Select-Object -ExpandProperty roleDefinition
            $role | Add-Member -MemberType NoteProperty -Name roleDefinition1 -Value $roleDefinition -Force
            $roles += $role
        }

        Write-Host "INFO: Found $($rolesWithPrincipal.Count) role assignments in the tenant."

        if ($IncludePrivilegedAssignments) {
            try {
                Write-Host "INFO: Collecting PIM eligible role assignments..." -ForegroundColor Cyan
                $eligibleRoles = Invoke-GraphRequestWithPaging -Uri "beta/roleManagement/directory/roleEligibilitySchedules?`$expand=roleDefinition,principal" -Method Get
                
                if ($null -eq $eligibleRoles) {
                    Write-Warning "Unable to collect PIM eligible role assignments. This may be due to missing Microsoft Entra ID Premium P2 license."
                    Write-Host "INFO: Continuing without PIM eligible role assignments..." -ForegroundColor Yellow
                    $eligibleRoles = @() # Set to empty array so the code can continue
                }
            } catch {
                Write-Warning "Unable to collect PIM eligible role assignments. This may be due to missing Microsoft Entra ID Premium P2 license."
                Write-Host "INFO: Continuing without PIM eligible role assignments..." -ForegroundColor Yellow
                $eligibleRoles = @() # Set to empty array so the code can continue
            } 

            foreach ($eligibleRole in $eligibleRoles) {
                $eligibleRole | Add-Member -MemberType NoteProperty -Name roleDefinition1 -Value $eligibleRole.roleDefinition -Force
                $roles += $eligibleRole
            }
        }


        if (!$roles) { 
            if ($verbose) {
                Write-Verbose "No role assignments found, exiting..."
            } else {
                Write-Host "INFO: No role assignments found" -ForegroundColor Red
            }
            return 
        }

        $Report = @()
        foreach ($role in $roles) {
            # Decide the principal type based on the '@odata.type' property
            Switch ($role.principal.'@odata.type') {
                '#microsoft.graph.user' {
                    $principalType = "User"
                    $Principal = $role.principal.userPrincipalName
                    
                    if ($role.principal.accountEnabled -eq $null) {
                        $AccountStatus = "N/A"
                    } elseif ($role.principal.accountEnabled -eq $true) {
                        $AccountStatus = "Enabled"
                    } else {
                        $AccountStatus = "Disabled"
                    }
                }
                '#microsoft.graph.group' {
                    $principalType = "Group"
                    $Principal = $role.principal.id
                    if ($role.principal.accountEnabled -eq $null) {
                        $AccountStatus = "N/A"
                    } elseif ($role.principal.accountEnabled -eq $true) {
                        $AccountStatus = "Enabled"
                    } else {
                        $AccountStatus = "Disabled"
                    }
                }
                '#microsoft.graph.servicePrincipal' {
                    $principalType = "Service Principal"
                    $Principal = $role.principal.id
                    
                    if ($role.principal.accountEnabled -eq $null) {
                        $AccountStatus = "N/A"
                    } elseif ($role.principal.accountEnabled -eq $true) {
                        $AccountStatus = "Enabled"
                    } else {
                        $AccountStatus = "Disabled"
                    }
                }
            }

            # Decide the role assignment type based on the role eligibility schedule
            if ($Role.status) {
                $status = "Eligible"
                # $tartdate = ($role.scheduleInfo.startDateTime).ToString("dd-MM-yyyy HH:mm")

                if ($role.scheduleInfo.startDateTime) {
                    $startDate = ($role.scheduleInfo.startDateTime).ToString("dd-MM-yyyy HH:mm")
                } else {
                    $startDate = "Permanent"
                }

                if ($role.scheduleInfo.expiration.endDateTime) {
                    $tartdate = ($role.scheduleInfo.startDateTime).ToString("dd-MM-yyyy HH:mm")
                    $endDate = ($role.scheduleInfo.expiration.endDateTime).ToString("dd-MM-yyyy HH:mm")
                } else {
                    $endDate = "Permanent"
                }
            } else {
                $status = "Permanent"
                $StartDate = "Permanent"
                $endDate = "Permanent"
            }

            # Check Assigned Role
            if ($role.roleDefinition1.displayName) {
                $assignedRole = $role.roleDefinition1.displayName
            } elseif ($role.roleDefinition.displayName) {
                $assignedRole = $role.roleDefinition.displayName
            } else {
                $assignedRole = "Unknown"
            }

            $Reportline = [PSCustomObject]@{
                "Principal"             = $Principal
                "DisplayName"           = $role.principal.displayName
                "AccountStatus"         = $AccountStatus
                "PrincipalType"         = $principalType
                "Assigned Role"         = $assignedRole
                "AssignedRoleScopeName" = if ($role.directoryScopeId -eq "/" -or $null -eq $role.directoryScopeId) { "Tenant-Wide" } else { "AU/$($auLookup[$role.directoryScopeId])" }
                "AssignmentType"        = $status
                "AssignmentStartDate"   = $startDate
                "AssignmentEndDate"     = $endDate
                #"Members" = if ($principalType -eq "Group") { $securityGroupMembers[$role.principal.id].Members } else { $null }
                "IsBuiltIn"             = if ($role.roleDefinition.isBuiltIn) { $role.roleDefinition.isBuiltIn } elseif ($role.roleDefinition1.isBuiltIn) { $role.roleDefinition1.isBuiltIn } else { $null }

            }

            $report += $Reportline

        }

        $GroupAssignmentReport = $report | Where-Object { $_.PrincipalType -eq "group" }
        $ServicePrincipalReport = $report | Where-Object { $_.PrincipalType -eq "service Principal" } | Select-Object -ExcludeProperty Members
        $UserAssignmentReport = $report | Where-Object { $_.PrincipalType -eq "user" } | Select-Object -ExcludeProperty Members

        $GroupMembershipOverviewReport = @()
        foreach ($group in $GroupAssignmentReport) {
            # Look up group members from our previously collected data
            $members = ($securityGroupMembers.Values | Where-Object { $_.groupid -eq $group.Principal }).members.userprincipalname -join ", "

            if (-not $members) {
                $members = "None"
            } else {
                $members = $members -join ", "
            }

            $Reportline = [PSCustomObject]@{
                Principal   = $group.Principal
                DisplayName = $group.DisplayName
                Members     = $members
            }

            $GroupMembershipOverviewReport += $Reportline
        }


        $GroupMembershipOverviewReport = $GroupMembershipOverviewReport | Select-Object -Property Principal, DisplayName, Members -Unique

        New-AdminRoleHTMLReport -TenantName $tenantname -Report $Report -UserAssignmentReport $UserAssignmentReport -GroupAssignmentReport $GroupAssignmentReport -ServicePrincipalReport $ServicePrincipalReport -GroupMembershipOverviewReport $GroupMembershipOverviewReport

        
    } else {
        throw "Failed to connect to Microsoft Graph API."
    }
} catch { 
    Write-Error "Error: $_"
    throw $_
} finally {
    # Disconnect from Microsoft Graph if connected
    $contextInfo = Get-MgContext -ErrorAction SilentlyContinue
    if ($contextInfo) {
        Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null 
        Write-Host "Disconnected from Microsoft Graph." -ForegroundColor Green
    } else {
        Write-Host "No active connection to disconnect." -ForegroundColor Yellow
    }
}