output/20260723_081209/govviz/repo/pwsh/dev/functions/processDefinitionInsights.ps1

function processDefinitionInsights() {
    $startDefinitionInsights = Get-Date
    Write-Host ' Building DefinitionInsights'

    $SHA256 = New-Object -TypeName System.Security.Cryptography.SHA256CryptoServiceProvider
    $utf8 = New-Object -TypeName System.Text.UTF8Encoding

    #region definitionInsightsAzurePolicy
    $htmlDefinitionInsights = [System.Text.StringBuilder]::new()
    [void]$htmlDefinitionInsights.AppendLine( @'
    <button type="button" class="collapsible" id="definitionInsights_AzurePolicy"><hr class="hr-text-definitionInsightsPolicy" data-content="Policy" /></button>
    <div class="content contentDefinitionInsights">
'@
)

    #policy/policySet preQuery
    #region preQuery
    $htPolicyWithAssignments = @{
        policy    = @{}
        policySet = @{}
    }

    foreach ($policyOrPolicySet in $arrayPolicyAssignmentsEnriched | Sort-Object -Property PolicyAssignmentId -Unique | Group-Object -Property PolicyId, PolicyVariant) {
        $policyOrPolicySetNameSplit = $policyOrPolicySet.name.split(', ')
        if ($policyOrPolicySetNameSplit[1] -eq 'Policy') {
            #policy
            if (-not ($htPolicyWithAssignments).policy.($policyOrPolicySetNameSplit[0])) {
                $pscustomObj = [System.Collections.ArrayList]@()
                foreach ($entry in $policyOrPolicySet.group) {
                    $null = $pscustomObj.Add([PSCustomObject]@{
                            PolicyAssignmentId          = $entry.PolicyAssignmentId
                            PolicyAssignmentDisplayName = $entry.PolicyAssignmentDisplayName
                        })
                }
                ($htPolicyWithAssignments).policy.($policyOrPolicySetNameSplit[0]) = @{
                    Assignments = [array]($pscustomObj)
                }
            }
        }
        else {
            #policySet
            if (-not ($htPolicyWithAssignments).policySet.($policyOrPolicySetNameSplit[0])) {
                $pscustomObj = [System.Collections.ArrayList]@()
                foreach ($entry in $policyOrPolicySet.group) {
                    $null = $pscustomObj.Add([PSCustomObject]@{
                            PolicyAssignmentId          = $entry.PolicyAssignmentId
                            PolicyAssignmentDisplayName = $entry.PolicyAssignmentDisplayName
                        })
                }
                ($htPolicyWithAssignments).policySet.($policyOrPolicySetNameSplit[0]) = @{
                    Assignments = [array]($pscustomObj)
                }
            }
        }
    }

    foreach ($customPolicy in $tenantCustomPolicies) {
        if ($htPoliciesWithAssignmentOnRgRes.($customPolicy.PolicyDefinitionId)) {
            if (-not ($htPolicyWithAssignments).policy.($customPolicy.PolicyDefinitionId)) {
                ($htPolicyWithAssignments).policy.($customPolicy.PolicyDefinitionId) = @{
                    Assignments = [array]($htPoliciesWithAssignmentOnRgRes.($customPolicy.PolicyDefinitionId).Assignments)
                }
            }
            else {
                $array = [System.Collections.ArrayList]@()
                $null = $array.Add($htPoliciesWithAssignmentOnRgRes.($customPolicy.PolicyDefinitionId).Assignments)
                ($htPolicyWithAssignments).policy.($customPolicy.PolicyDefinitionId).Assignments = $array
            }
        }
    }

    foreach ($customPolicySet in $tenantCustomPolicySets) {
        if ($htPoliciesWithAssignmentOnRgRes.($customPolicySet.PolicyDefinitionId)) {
            if (-not ($htPolicyWithAssignments).policySet.($customPolicySet.PolicyDefinitionId)) {
                ($htPolicyWithAssignments).policySet.($customPolicySet.PolicyDefinitionId) = @{
                    Assignments = [array]($htPoliciesWithAssignmentOnRgRes.($customPolicySet.PolicyDefinitionId).Assignments)
                }
            }
            else {
                $array = [System.Collections.ArrayList]@()
                $null = $array.Add($htPoliciesWithAssignmentOnRgRes.($customPolicySet.PolicyDefinitionId).Assignments)
                ($htPolicyWithAssignments).policySet.($customPolicySet.PolicyDefinitionId).Assignments = $array
            }
        }
    }
    #endregion preQuery

    #region definitionInsightsPolicyDefinitions
    $startDefinitionInsightsPolicyDefinitions = Get-Date
    Write-Host ' processing DefinitionInsights Policy definitions'
    ShowMemoryUsage
    $tfCount = $tenantAllPoliciesCount
    $htmlTableId = 'definitionInsights_Policy'
    [void]$htmlDefinitionInsights.AppendLine( @"
<button onclick="loadtf$("func_$htmlTableId")()" type="button" class="collapsible" id="button_definitionInsights_Policy"><i class="fa fa-check-circle blue" aria-hidden="true"></i> <span class="valignMiddle">$tenantAllPoliciesCount Policy definitions</span></button>
<div class="content contentDefinitionInsights">
 
<div id="extGridPolicy" class="panel panel-info pull-left" >
    <div class="panel-body bg-info">
        <div class="me">
            <label>Search JSON</label>
            <span id="polJson"></span>
        </div>
 
        <div class="me">
            <label>Builtin/Custom/Static</label>
            <span id="polType"></span>
        </div>
 
        <div class="me">
            <label>ALZ</label>
            <span id="polIsALZ"></span>
        </div>
 
        <div class="me">
            <label>Category</label>
            <span id="polCategory"></span>
        </div>
 
        <div class="me">
            <label>Deprecated</label>
            <span id="polDeprecated"></span>
        </div>
 
        <div class="me">
            <label>Preview</label>
            <span id="polPreview"></span>
        </div>
 
        <div class="me">
            <label>Scope Mg/Sub</label>
            <span id="polScope"></span>
        </div>
 
        <div class="me">
            <label>Scope Name/Id</label>
            <span id="polScopeNameId"></span>
        </div>
 
        <div class="me">
            <label>Effect default</label>
            <span id="polEffectDefaultValue"></span>
        </div>
 
        <div class="me">
            <label>hasAssignment</label>
            <span id="polHasAssignment"></span>
        </div>
 
        <div class="me" style="display: none;">
            <label>polPolAssignments</label>
            <span id="polPolAssignments"></span>
        </div>
 
        <div class="me" style="display: none;">
            <label>polhid1</label>
            <span id="polhid1"></span>
        </div>
 
        <div class="me">
            <label>usedInPolicySet</label>
            <span id="polUsedInPolicySet"></span>
        </div>
 
        <div class="me" style="display: none;">
            <label>polUsedInPolicySetCount</label>
            <span id="polUsedInPolicySetCount"></span>
        </div>
 
        <div class="me" style="display: none;">
            <label>polUsedInPolicySets</label>
            <span id="polUsedInPolicySets"></span>
        </div>
 
        <div class="me">
            <label>Roles</label>
            <span id="polRoledefs"></span>
        </div>
 
    </div>
</div>
 
<div class="pull-left" style="margin-left: 0.5em;">
 
<table id="$htmlTableId" class="summaryTable">
<thead>
<tr>
<th>JSON</th>
<th>PolicyType</th>
<th>ALZ</th>
<th>Category</th>
<th>Deprecated</th>
<th>Preview</th>
<th>Scope Mg/Sub</th>
<th>Scope Name/Id</th>
<th>effectDefaultValue</th>
<th>hasAssignments</th>
<th>Assignments Count</th>
<th>Assignments</th>
<th>UsedInPolicySet</th>
<th>PolicySetsCount</th>
<th>PolicySets</th>
<th>Roles</th>
</tr>
</thead>
<tbody>
"@
)

    $cnter = 0
    $htmlDefinitionInsightshlp = $null
    $htmlDefinitionInsightshlp = foreach ($policy in (($htCacheDefinitionsPolicy).Values | Sort-Object @{Expression = { $_.DisplayName } }, @{Expression = { $_.PolicyDefinitionId } })) {

        $cnter++
        if ($cnter % 1000 -eq 0) {
            Write-Host " $cnter Policy definitions processed"
            ShowMemoryUsage
        }

        $hasAssignments = 'false'
        $assignmentsCount = 0
        $assignmentsDetailed = 'n/a'

        if (($htPolicyWithAssignments).policy.($policy.PolicyDefinitionId)) {
            $hasAssignments = 'true'
            $assignments = ($htPolicyWithAssignments).policy.($policy.PolicyDefinitionId).Assignments
            $assignmentsCount = $assignments.Count

            if ($assignmentsCount -gt 0) {
                $arrayAssignmentDetails = @()
                $arrayAssignmentDetails = foreach ($assignment in $assignments) {
                    if ($assignment.PolicyAssignmentDisplayName -eq '') {
                        $polAssDisplayName = '<i>#no AssignmentName given</i>'
                    }
                    else {
                        $polAssDisplayName = $assignment.PolicyAssignmentDisplayName
                    }
                    "$($assignment.PolicyAssignmentId) (<b>$($polAssDisplayName)</b>)"
                }
                $assignmentsDetailed = $arrayAssignmentDetails -join "$CsvDelimiterOpposite "
            }

        }

        $roleDefinitionIds = 'n/a'
        if ($policy.RoleDefinitionIds -ne 'n/a') {
            $arrayRoleDefDetails = @()
            $arrayRoleDefDetails = foreach ($roleDef in $policy.RoleDefinitionIds) {
                $roleDefIdOnly = $roleDef -replace '.*/'
                if (($roleDefIdOnly).Length -ne 36) {
                    "'INVALID RoleDefId!' ($($roleDefIdOnly))"
                }
                else {
                    $roleDefHlp = ($htCacheDefinitionsRole).($roleDefIdOnly)
                    "'$($roleDefHlp.Name)' ($($roleDefHlp.Id))"
                }
            }
            $roleDefinitionIds = $arrayRoleDefDetails -join "$CsvDelimiterOpposite "
        }

        $scopeDetails = 'n/a'
        if ($policy.ScopeId -ne 'n/a') {
            if ([string]::IsNullOrEmpty($policy.ScopeId)) {
                Write-Host "unexpected IsNullOrEmpty - processing: $($policy | ConvertTo-Json -Depth 99)"
            }
            $scopeDetails = "$($policy.ScopeId) ($($htEntities.($policy.ScopeId).DisplayName))"
        }

        $usedInPolicySet = 'false'
        $usedInPolicySetCount = 0
        $usedInPolicySets = 'n/a'

        if ($htPoliciesUsedInPolicySets.($policy.PolicyDefinitionId)) {
            $usedInPolicySet = 'true'
            $usedInPolicySetCount = ($htPoliciesUsedInPolicySets.($policy.PolicyDefinitionId).policySet).Count
            $usedInPolicySets = ($htPoliciesUsedInPolicySets.($policy.PolicyDefinitionId).policySet | Sort-Object) -join "$CsvDelimiterOpposite "
        }

        $json = $($policy.Json | ConvertTo-Json -Depth 99)
        $guid = ([System.BitConverter]::ToString($SHA256.ComputeHash($utf8.GetBytes($policy.PolicyDefinitionId)))) -replace '-'
        @"
<tr>
<td class="definitionInsightsjsontd">
 
    <div class="defCopy">
        <button class= "defCopyButton" onclick="copyDef('json$($guid)')">Copy definition</button>
            <script>
            function copyDef(elementId) {
                var copyOfTheDefinition = document.getElementById(elementId).innerText;
                navigator.clipboard.writeText(copyOfTheDefinition);
            }
        </script>
    </div>
 
    <div class="definitioninsightsjsondiv" id="json$($guid)"></div>
 
    <script>
        var jsonObj$($guid) = {};
        var jsonViewer$($guid) = new JSONViewer();
        document.querySelector("#json$($guid)").appendChild(jsonViewer$($guid).getContainer());
        var setJSON$($guid) = function() {
            try {
                jsonObj$($guid) = JSON.parse(JSON.stringify($($json)));
            }
            catch (err) {
                alert(err);
            }
        };
        setJSON$($guid)();
        jsonViewer$($guid).showJSON(jsonObj$($guid))
    </script>
 
</td>
<td>$($policy.Type)</td>
<td>$($policy.ALZ)</td>
<td>$($policy.Category -replace '<', '&lt;' -replace '>', '&gt;')</td>
<td>$($policy.Deprecated)</td>
<td>$($policy.Preview)</td>
<td>$($policy.ScopeMgSub)</td>
<td>$($scopeDetails -replace '<', '&lt;' -replace '>', '&gt;')</td>
<td>$($policy.effectDefaultValue)</td>
<td>$hasAssignments</td>
<td>$assignmentsCount</td>
<td class="breakwordall">$assignmentsDetailed</td>
<td class="breakwordall">$usedInPolicySet</td>
<td class="breakwordall">$usedInPolicySetCount</td>
<td class="breakwordall">$usedInPolicySets</td>
<td class="breakwordall">$($roleDefinitionIds -replace '<', '&lt;' -replace '>', '&gt;')</td>
</tr>
"@

    }
    [void]$htmlDefinitionInsights.AppendLine($htmlDefinitionInsightshlp)
    if ($NoDefinitionInsightsDedicatedHTML) {
        $htmlDefinitionInsights | Add-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).html" -Encoding utf8 -Force
        $htmlDefinitionInsights = [System.Text.StringBuilder]::new()
    }
    [void]$htmlDefinitionInsights.AppendLine( @"
    </tbody>
</table>
</div>
<script>
function loadtf$("func_$htmlTableId")() { if (window.helpertfConfig4$htmlTableId !== 1) {
    window.helpertfConfig4$htmlTableId =1;
    var tfConfig4$htmlTableId = {
    base_path: 'https://www.azadvertizer.net/azgovvizv4/tablefilter/', rows_counter: true,
"@
)
    if ($tfCount -gt 10) {
        $spectrum = "10, $tfCount"
        if ($tfCount -gt 50) {
            $spectrum = "10, 25, 50, $tfCount"
        }
        if ($tfCount -gt 100) {
            $spectrum = "10, 30, 50, 100, $tfCount"
        }
        if ($tfCount -gt 500) {
            $spectrum = "10, 30, 50, 100, 250, $tfCount"
        }
        if ($tfCount -gt 1000) {
            $spectrum = "10, 30, 50, 100, 250, 500, 750, $tfCount"
        }
        if ($tfCount -gt 2000) {
            $spectrum = "10, 30, 50, 100, 250, 500, 750, 1000, 1500, $tfCount"
        }
        if ($tfCount -gt 3000) {
            $spectrum = "10, 30, 50, 100, 250, 500, 750, 1000, 1500, 3000, $tfCount"
        }
        [void]$htmlDefinitionInsights.AppendLine( @"
        paging: {
            results_per_page: [
                'Records: ',
                [$spectrum]
            ]
        },
        /*state: {types: ['local_storage'], filters: true, page_number: true, page_length: true, sort: true},*/
"@
)
    }
    [void]$htmlDefinitionInsights.AppendLine( @"
    btn_reset: true,
    highlight_keywords: true,
    alternate_rows: true,
    auto_filter: {
        delay: 1100
    },
    linked_filters: true,
    no_results_message: true,
    rows_counter: {
        text: 'results: '
    },
    col_1: 'select',
    col_2: 'select',
    col_3: 'select',
    col_4: 'select',
    col_5: 'select',
    col_6: 'select',
    col_8: 'select',
    col_9: 'select',
    col_12: 'select',
    col_types: [
        'caseinsensitivestring',
        'caseinsensitivestring',
        'caseinsensitivestring',
        'caseinsensitivestring',
        'caseinsensitivestring',
        'caseinsensitivestring',
        'caseinsensitivestring',
        'caseinsensitivestring',
        'caseinsensitivestring',
        'caseinsensitivestring',
        'number',
        'caseinsensitivestring',
        'number',
        'caseinsensitivestring',
        'caseinsensitivestring',
        'caseinsensitivestring'
    ],
    external_flt_ids: [
        'polJson',
        'polType',
        'polIsALZ',
        'polCategory',
        'polDeprecated',
        'polPreview',
        'polScope',
        'polScopeNameId',
        'polEffectDefaultValue',
        'polHasAssignment',
        'polPolAssignments',
        'polhid1',
        'polUsedInPolicySet',
        'polUsedInPolicySetCount',
        'polUsedInPolicySets',
        'polRoledefs'
    ],
    watermark: ['', '', '','', '', '', '', '', '', '', '','','','','', 'try: \'Contributor\''],
    extensions: [
        {
            name: 'sort'
        },
        {
            name: 'colsVisibility',
            at_start: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],
            text: 'Columns: ',
            enable_tick_all: true
        }
    ]
};
var tf = new TableFilter('$htmlTableId', tfConfig4$htmlTableId);
tf.init();}}
</script>
</div>
"@
)
    $endDefinitionInsightsPolicyDefinitions = Get-Date
    Write-Host " DefinitionInsights Policy definitions duration: $((New-TimeSpan -Start $startDefinitionInsightsPolicyDefinitions -End $endDefinitionInsightsPolicyDefinitions).TotalMinutes) minutes ($((New-TimeSpan -Start $startDefinitionInsightsPolicyDefinitions -End $endDefinitionInsightsPolicyDefinitions).TotalSeconds) seconds)"
    showMemoryUsage
    #endregion definitionInsightsPolicyDefinitions

    #region definitionInsightsPolicySetDefinitions
    $startDefinitionInsightsPolicySetDefinitions = Get-Date
    Write-Host ' processing DefinitionInsights PolicySet definitions'
    ShowMemoryUsage
    $tfCount = $tenantAllPolicySetsCount
    $htmlTableId = 'definitionInsights_PolicySet'
    [void]$htmlDefinitionInsights.AppendLine( @"
<button onclick="loadtf$("func_$htmlTableId")()" type="button" class="collapsible" id="button_definitionInsights_PolicySet"><i class="fa fa-check-circle blue" aria-hidden="true"></i> <span class="valignMiddle">$tenantAllPolicySetsCount PolicySet definitions</span></button>
<div class="content contentDefinitionInsights">
 
<div id="extGridPolicySet" class="panel panel-info pull-left" >
    <div class="panel-body bg-info">
        <div class="me">
            <label>Search JSON</label>
            <span id="polsetJson"></span>
        </div>
 
        <div class="me">
            <label>Builtin/Custom</label>
            <span id="polsetType"></span>
        </div>
 
        <div class="me">
            <label>ALZ</label>
            <span id="polsetIsALZ"></span>
        </div>
 
        <div class="me">
            <label>Category</label>
            <span id="polsetCategory"></span>
        </div>
 
        <div class="me">
            <label>Deprecated</label>
            <span id="polsetDeprecated"></span>
        </div>
 
        <div class="me">
            <label>Preview</label>
            <span id="polsetPreview"></span>
        </div>
 
        <div class="me">
            <label>Scope Mg/Sub</label>
            <span id="polSetScope"></span>
        </div>
 
        <div class="me">
            <label>Scope Name/Id</label>
            <span id="polSetScopeNameId"></span>
        </div>
 
        <div class="me">
            <label>hasAssignment</label>
            <span id="polSetHasAssignment"></span>
        </div>
 
    </div>
</div>
 
 
<div class="pull-left" style="margin-left: 0.5em;">
 
<table id="$htmlTableId" class="summaryTable">
<thead>
<tr>
<th>JSON</th>
<th>PolicySet Type</th>
<th>ALZ</th>
<th>Category</th>
<th>Deprecated</th>
<th>Preview</th>
<th>Scope Mg/Sub</th>
<th>Scope Name/Id</th>
<th>hasAssignments</th>
<th>Assignments Count</th>
<th>Assignments</th>
</tr>
</thead>
<tbody>
"@
)
    $htmlDefinitionInsightshlp = $null
    $htmlDefinitionInsightshlp = foreach ($policySet in ($tenantAllPolicySets | Sort-Object @{Expression = { $_.DisplayName } }, @{Expression = { $_.PolicyDefinitionId } })) {
        $hasAssignments = 'false'
        $assignmentsCount = 0
        $assignmentsDetailed = 'n/a'

        if (($htPolicyWithAssignments).policySet.($policySet.PolicyDefinitionId)) {
            $hasAssignments = 'true'
            $assignments = ($htPolicyWithAssignments).policySet.($policySet.PolicyDefinitionId).Assignments
            $assignmentsCount = ($assignments | Measure-Object).Count

            if ($assignmentsCount -gt 0) {
                $arrayAssignmentDetails = @()
                $arrayAssignmentDetails = foreach ($assignment in $assignments) {
                    if ($assignment.PolicyAssignmentDisplayName -eq '') {
                        $polAssDisplayName = '<i>#no AssignmentName given</i>'
                    }
                    else {
                        $polAssDisplayName = $assignment.PolicyAssignmentDisplayName
                    }
                    "$($assignment.PolicyAssignmentId) (<b>$($polAssDisplayName)</b>)"
                }
                $assignmentsDetailed = $arrayAssignmentDetails -join "$CsvDelimiterOpposite "
            }
        }

        $scopeDetails = 'n/a'
        if ($policySet.ScopeId -ne 'n/a') {
            $scopeDetails = "$($policySet.ScopeId) ($($htEntities.($policySet.ScopeId).DisplayName))"
        }
        $json = $($policySet.Json | ConvertTo-Json -Depth 99)
        $guid = ([System.BitConverter]::ToString($SHA256.ComputeHash($utf8.GetBytes($policySet.PolicyDefinitionId)))) -replace '-'
        @"
<tr>
<td class="definitionInsightsjsontd">
 
    <div class="defCopy">
        <button class= "defCopyButton" onclick="copyDef('json$($guid)')">Copy definition</button>
            <script>
            function copyDef(elementId) {
                var copyOfTheDefinition = document.getElementById(elementId).innerText;
                navigator.clipboard.writeText(copyOfTheDefinition);
            }
        </script>
    </div>
 
    <div class="definitioninsightsjsondiv" id="json$($guid)"></div>
 
    <script>
        var jsonObj$($guid) = {};
        var jsonViewer$($guid) = new JSONViewer();
        document.querySelector("#json$($guid)").appendChild(jsonViewer$($guid).getContainer());
        var setJSON$($guid) = function() {
            try {
                jsonObj$($guid) = JSON.parse(JSON.stringify($($json)));
            }
            catch (err) {
                alert(err);
            }
        };
        setJSON$($guid)();
        jsonViewer$($guid).showJSON(jsonObj$($guid))
    </script>
 
</td>
<td>$($policySet.Type)</td>
<td>$($policySet.ALZ)</td>
<td>$($policySet.Category -replace '<', '&lt;' -replace '>', '&gt;')</td>
<td>$($policySet.Deprecated)</td>
<td>$($policySet.Preview)</td>
<td>$($policySet.ScopeMgSub)</td>
<td>$($scopeDetails -replace '<', '&lt;' -replace '>', '&gt;')</td>
<td>$hasAssignments</td>
<td>$assignmentsCount</td>
<td class="breakwordall">$assignmentsDetailed</td>
</tr>
"@

    }
    [void]$htmlDefinitionInsights.AppendLine($htmlDefinitionInsightshlp)
    if ($NoDefinitionInsightsDedicatedHTML) {
        $htmlDefinitionInsights | Add-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).html" -Encoding utf8 -Force
        $htmlDefinitionInsights = [System.Text.StringBuilder]::new()
    }
    [void]$htmlDefinitionInsights.AppendLine( @"
    </tbody>
</table>
</div>
<script>
function loadtf$("func_$htmlTableId")() { if (window.helpertfConfig4$htmlTableId !== 1) {
    window.helpertfConfig4$htmlTableId =1;
    var tfConfig4$htmlTableId = {
    base_path: 'https://www.azadvertizer.net/azgovvizv4/tablefilter/', rows_counter: true,
"@
)
    if ($tfCount -gt 10) {
        $spectrum = "10, $tfCount"
        if ($tfCount -gt 50) {
            $spectrum = "10, 25, 50, $tfCount"
        }
        if ($tfCount -gt 100) {
            $spectrum = "10, 30, 50, 100, $tfCount"
        }
        if ($tfCount -gt 500) {
            $spectrum = "10, 30, 50, 100, 250, $tfCount"
        }
        if ($tfCount -gt 1000) {
            $spectrum = "10, 30, 50, 100, 250, 500, 750, $tfCount"
        }
        if ($tfCount -gt 2000) {
            $spectrum = "10, 30, 50, 100, 250, 500, 750, 1000, 1500, $tfCount"
        }
        if ($tfCount -gt 3000) {
            $spectrum = "10, 30, 50, 100, 250, 500, 750, 1000, 1500, 3000, $tfCount"
        }
        [void]$htmlDefinitionInsights.AppendLine( @"
        paging: {
            results_per_page: [
                'Records: ',
                [$spectrum]
            ]
        },
        /*state: {types: ['local_storage'], filters: true, page_number: true, page_length: true, sort: true},*/
"@
)
    }
    [void]$htmlDefinitionInsights.AppendLine( @"
    btn_reset: true,
    highlight_keywords: true,
    alternate_rows: true,
    auto_filter: {
        delay: 1100
    },
    linked_filters: true,
    no_results_message: true,
    rows_counter: {
        text: 'results: '
    },
    col_1: 'select',
    col_2: 'select',
    col_3: 'select',
    col_4: 'select',
    col_5: 'select',
    col_6: 'select',
    col_8: 'select',
    col_types: [
        'caseinsensitivestring',
        'caseinsensitivestring',
        'caseinsensitivestring',
        'caseinsensitivestring',
        'caseinsensitivestring',
        'caseinsensitivestring',
        'caseinsensitivestring',
        'caseinsensitivestring',
        'caseinsensitivestring',
        'number',
        'caseinsensitivestring'
 
    ],
    external_flt_ids: [
        'polsetJson',
        'polsetType',
        'polsetIsALZ',
        'polsetCategory',
        'polsetDeprecated',
        'polsetPreview',
        'polSetScope',
        'polSetScopeNameId',
        'polSetHasAssignment'
    ],
    extensions: [
        {
            name: 'sort'
        },
        {
            name: 'colsVisibility',
            at_start: [1,2,3,4,5,6,7,8,9,10],
            text: 'Columns: ',
            enable_tick_all: true
        }
    ]
};
var tf = new TableFilter('$htmlTableId', tfConfig4$htmlTableId);
tf.init();}}
</script>
</div>
"@
)
    $endDefinitionInsightsPolicySetDefinitions = Get-Date
    Write-Host " DefinitionInsights PolicySet definitions duration: $((New-TimeSpan -Start $startDefinitionInsightsPolicySetDefinitions -End $endDefinitionInsightsPolicySetDefinitions).TotalMinutes) minutes ($((New-TimeSpan -Start $startDefinitionInsightsPolicySetDefinitions -End $endDefinitionInsightsPolicySetDefinitions).TotalSeconds) seconds)"
    showMemoryUsage
    #endregion definitionInsightsPolicySetDefinitions

    [void]$htmlDefinitionInsights.AppendLine( @'
    </div>
'@
)
    #endregion definitionInsightsAzurePolicy

    #region definitionInsightsAzureRBAC
    [void]$htmlDefinitionInsights.AppendLine( @'
    <button type="button" class="collapsible" id="definitionInsights_AzureRBAC"><hr class="hr-text-definitionInsightsRBAC" data-content="RBAC" /></button>
    <div class="content contentDefinitionInsights">
'@
)

    #RBAC preQuery
    $htRoleWithAssignments = @{}
    foreach ($roleDef in $rbacAll | Sort-Object -Property RoleAssignmentId -Unique | Group-Object -Property RoleId) {
        if (-not ($htRoleWithAssignments).($roleDef.Name)) {
            ($htRoleWithAssignments).($roleDef.Name) = @{
                Assignments = [array]($roleDef.group)
            }
        }
    }

    #region definitionInsightsRoleDefinitions
    $startDefinitionInsightsRoleDefinitions = Get-Date
    Write-Host ' processing DefinitionInsights Role definitions'
    ShowMemoryUsage
    $tfCount = $tenantAllRolesCount
    $htmlTableId = 'definitionInsights_Roles'
    [void]$htmlDefinitionInsights.AppendLine( @"
<button onclick="loadtf$("func_$htmlTableId")()" type="button" class="collapsible" id="button_definitionInsights_Roles"><i class="fa fa-check-circle blue" aria-hidden="true"></i> <span class="valignMiddle">$tenantAllRolesCount Role definitions</span></button>
<div class="content contentDefinitionInsights">
 
<div id="extGridRole" class="panel panel-info pull-left" >
    <div class="panel-body bg-info">
        <div class="me">
            <label>Search JSON</label>
            <span id="roleJson"></span>
        </div>
 
        <div class="me">
            <label>Builtin/Custom</label>
            <span id="roleType"></span>
        </div>
 
        <div class="me">
            <label>Data</label>
            <span id="roleDataRelated"></span>
        </div>
 
        <div class="me">
            <label>canDoRoleAssignments</label>
            <span id="roleCanDoRoleAssignments"></span>
        </div>
 
        <div class="me">
            <label>hasAssignment</label>
            <span id="roleHasAssignment"></span>
        </div>
 
    </div>
</div>
 
 
<div class="pull-left" style="margin-left: 0.5em;">
 
<table id="$htmlTableId" class="summaryTable">
<thead>
<tr>
<th>JSON</th>
<th>Role Type</th>
<th>Data</th>
<th>canDoRoleAssignments</th>
<th>hasAssignments</th>
<th>Assignments Count</th>
<th>Assignments</th>
</tr>
</thead>
<tbody>
"@
)
    $arrayRoleDefinitionsForCSVExport = [System.Collections.ArrayList]@()
    $htmlDefinitionInsightshlp = $null
    $htmlDefinitionInsightshlp = foreach ($role in ($tenantAllRoles | Sort-Object @{Expression = { $_.Name } })) {
        if ($role.IsCustom -eq $true) {
            $roleType = 'Custom'
            $AssignableScopesCount = $role.AssignableScopes.Count
            if ($role.AssignableScopes -like '*/providers/microsoft.management/managementgroups/*') {
                $AssignableScopesMG = $true
            }
            else {
                $AssignableScopesMG = $false
            }

        }
        else {
            $roleType = 'Builtin'
            $AssignableScopesCount = ''
            $AssignableScopesMG = ''
        }
        if (-not [string]::IsNullOrEmpty($role.DataActions) -or -not [string]::IsNullOrEmpty($role.NotDataActions)) {
            $roleManageData = 'true'
        }
        else {
            $roleManageData = 'false'
        }

        $hasAssignments = 'false'
        $assignmentsCount = 0
        $assignmentsDetailed = 'n/a'
        if (($htRoleWithAssignments).($role.Id)) {
            $hasAssignments = 'true'
            $assignments = ($htRoleWithAssignments).($role.Id).Assignments
            $assignmentsCount = ($assignments).Count
            if ($assignmentsCount -gt 0) {
                $arrayAssignmentDetails = @()
                $arrayAssignmentDetails = foreach ($assignment in $assignments) {
                    "$($assignment.RoleAssignmentId)"
                }
                $assignmentsDetailed = $arrayAssignmentDetails -join "$CsvDelimiterOpposite "
            }
        }

        #array for exportCSV
        if (-not $NoCsvExport) {
            $null = $arrayRoleDefinitionsForCSVExport.Add([PSCustomObject]@{
                    Name                  = $role.Name
                    Id                    = $role.Id
                    Description           = $role.Json.properties.description
                    Type                  = $roleType
                    AssignmentsCount      = $assignmentsCount
                    AssignableScopesCount = $AssignableScopesCount
                    AssignableScopesMG    = $AssignableScopesMG
                    AssignableScopes      = ($role.AssignableScopes | Sort-Object) -join "$CsvDelimiterOpposite "
                    DataRelated           = $roleManageData
                    RoleAssWriteCapable   = $role.RoleCanDoRoleAssignments
                    Actions               = $role.Actions -join "$CsvDelimiterOpposite "
                    NotActions            = $role.NotActions -join "$CsvDelimiterOpposite "
                    DataActions           = $role.DataActions -join "$CsvDelimiterOpposite "
                    NotDataActions        = $role.NotDataActions -join "$CsvDelimiterOpposite "
                })
        }

        $json = $role.Json | ConvertTo-Json -Depth 99
        $guid = $role.Id -replace '-'
        @"
<tr>
<td class="definitionInsightsjsontd">
 
    <div class="defCopy">
        <button class= "defCopyButton" onclick="copyDef('json$($guid)')">Copy definition</button>
            <script>
            function copyDef(elementId) {
                var copyOfTheDefinition = document.getElementById(elementId).innerText;
                navigator.clipboard.writeText(copyOfTheDefinition);
            }
        </script>
    </div>
 
    <div class="definitioninsightsjsonrbacdiv" id="json$($guid)"></div>
 
    <script>
        var jsonObj$($guid) = {};
        var jsonViewer$($guid) = new JSONViewer();
        document.querySelector("#json$($guid)").appendChild(jsonViewer$($guid).getContainer());
        var setJSON$($guid) = function() {
            try {
                jsonObj$($guid) = JSON.parse(JSON.stringify($($json)));
            }
            catch (err) {
                alert(err);
            }
        };
        setJSON$($guid)();
        jsonViewer$($guid).showJSON(jsonObj$($guid))
    </script>
 
</td>
<td>$($roleType)</td>
<td>$($roleManageData)</td>
<td>$($role.RoleCanDoRoleAssignments)</td>
<td>$hasAssignments</td>
<td>$assignmentsCount</td>
<td class="breakwordall">$assignmentsDetailed</td>
</tr>
"@

    }

    #region exportCSV
    if (-not $NoCsvExport) {
        $csvFilename = "$($filename)_RoleDefinitions"
        Write-Host " Exporting RoleDefinitions CSV '$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv'"
        $arrayRoleDefinitionsForCSVExport | Sort-Object -Property Type, Name, Id | Export-Csv -Encoding utf8 -Path "$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv" -Delimiter $csvDelimiter -NoTypeInformation
        $arrayRoleDefinitionsForCSVExport = $null
    }
    #endregion exportCSV

    [void]$htmlDefinitionInsights.AppendLine($htmlDefinitionInsightshlp)
    if ($NoDefinitionInsightsDedicatedHTML) {
        $htmlDefinitionInsights | Add-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).html" -Encoding utf8 -Force
        $htmlDefinitionInsights = [System.Text.StringBuilder]::new()
    }
    [void]$htmlDefinitionInsights.AppendLine( @"
    </tbody>
</table>
</div>
<script>
function loadtf$("func_$htmlTableId")() { if (window.helpertfConfig4$htmlTableId !== 1) {
    window.helpertfConfig4$htmlTableId =1;
    var tfConfig4$htmlTableId = {
    base_path: 'https://www.azadvertizer.net/azgovvizv4/tablefilter/', rows_counter: true,
"@
)
    if ($tfCount -gt 10) {
        $spectrum = "10, $tfCount"
        if ($tfCount -gt 50) {
            $spectrum = "10, 25, 50, $tfCount"
        }
        if ($tfCount -gt 100) {
            $spectrum = "10, 30, 50, 100, $tfCount"
        }
        if ($tfCount -gt 500) {
            $spectrum = "10, 30, 50, 100, 250, $tfCount"
        }
        if ($tfCount -gt 1000) {
            $spectrum = "10, 30, 50, 100, 250, 500, 750, $tfCount"
        }
        if ($tfCount -gt 2000) {
            $spectrum = "10, 30, 50, 100, 250, 500, 750, 1000, 1500, $tfCount"
        }
        if ($tfCount -gt 3000) {
            $spectrum = "10, 30, 50, 100, 250, 500, 750, 1000, 1500, 3000, $tfCount"
        }
        [void]$htmlDefinitionInsights.AppendLine( @"
        paging: {
            results_per_page: [
                'Records: ',
                [$spectrum]
            ]
        },
        /*state: {types: ['local_storage'], filters: true, page_number: true, page_length: true, sort: true},*/
"@
)
    }
    [void]$htmlDefinitionInsights.AppendLine( @"
    btn_reset: true,
    highlight_keywords: true,
    alternate_rows: true,
    auto_filter: {
        delay: 1100
    },
    linked_filters: true,
    no_results_message: true,
    rows_counter: {
        text: 'results: '
    },
    col_1: 'select',
    col_2: 'select',
    col_3: 'select',
    col_4: 'select',
    col_types: [
        'caseinsensitivestring',
        'caseinsensitivestring',
        'caseinsensitivestring',
        'caseinsensitivestring',
        'caseinsensitivestring',
        'number',
        'caseinsensitivestring'
    ],
    external_flt_ids: [
        'roleJson',
        'roleType',
        'roleDataRelated',
        'roleCanDoRoleAssignments',
        'roleHasAssignment'
    ],
    extensions: [
        {
            name: 'sort'
        },
        {
            name: 'colsVisibility',
            at_start: [1,2,3,4,5,6],
            text: 'Columns: ',
            enable_tick_all: true
        }
    ]
};
var tf = new TableFilter('$htmlTableId', tfConfig4$htmlTableId);
tf.init();}}
</script>
</div>
"@
)
    $endDefinitionInsightsRoleDefinitions = Get-Date
    Write-Host " DefinitionInsights Role definitions duration: $((New-TimeSpan -Start $startDefinitionInsightsRoleDefinitions -End $endDefinitionInsightsRoleDefinitions).TotalMinutes) minutes ($((New-TimeSpan -Start $startDefinitionInsightsRoleDefinitions -End $endDefinitionInsightsRoleDefinitions).TotalSeconds) seconds)"
    showMemoryUsage
    #endregion definitionInsightsRoleDefinitions

    [void]$htmlDefinitionInsights.AppendLine( @'
    </div>
'@
)
    #endregion definitionInsightsAzureRBAC

    Write-Host " NoDefinitionInsightsDedicatedHTML: $NoDefinitionInsightsDedicatedHTML"
    if ($NoDefinitionInsightsDedicatedHTML) {
        Write-Host ' Appending DefinitionInsights to HTML'
        $script:html += $htmlDefinitionInsights
        $htmlDefinitionInsights = $null
        $script:html | Add-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).html" -Encoding utf8 -Force
        $script:html = $null
    }
    else {
        Write-Host " Creating dedicated DefinitionInsights HTML ($($outputPath)$($DirectorySeparatorChar)$($fileName)_DefinitionInsights.html)"
        $htmlDefinitionInsightsDedicated = $null
        $htmlDefinitionInsightsDedicated += $htmlDefinitionInsightsDedicatedStart
        $htmlDefinitionInsightsDedicated += $htmlDefinitionInsights
        $htmlDefinitionInsightsDedicated += $htmlDefinitionInsightsDedicatedEnd
        #$htmlDefinitionInsights = $null
        $htmlDefinitionInsightsDedicated | Set-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName)_DefinitionInsights.html" -Encoding utf8 -Force
        #$script:htmlDefinitionInsightsDedicated = $null

        $htmlDefinitionInsightsNo = @"
        <span>DefinitionInsights has been saved to dedicated HTML file '<i>$($outputPathGiven)$($DirectorySeparatorChar)$($fileName)_DefinitionInsights.html</i>' (parameter -NoDefinitionInsightsDedicatedHTML = $($NoDefinitionInsightsDedicatedHTML))</span><br>
        Open <a class="externallink" href="$($fileName)_DefinitionInsights.html" target="blank">DefinitionInsights <i class="fa fa-external-link" aria-hidden="true"></i></a>
"@

        $script:html += $htmlDefinitionInsightsNo
        #$htmlDefinitionInsightsNo = $null
        $script:html | Add-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).html" -Encoding utf8 -Force
        $script:html = $null
    }


    $endDefinitionInsights = Get-Date
    Write-Host " DefinitionInsights processing duration: $((New-TimeSpan -Start $startDefinitionInsights -End $endDefinitionInsights).TotalMinutes) minutes ($((New-TimeSpan -Start $startDefinitionInsights -End $endDefinitionInsights).TotalSeconds) seconds)"
    ShowMemoryUsage
}