Private/Convert-IntunePoliciesToHtml.ps1

<#
.SYNOPSIS
    Function to generate an HTML report of Intune policies.
  
.DESCRIPTION
    #************************************************************************************************************
    # Disclaimer
    #
    # This sample script is not supported under any Microsoft standard support program or service. This sample
    # script is provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties
    # including, without limitation, any implied warranties of merchantability or of fitness for a particular
    # purpose. The entire risk arising out of the use or performance of this sample script and documentation
    # remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation,
    # production, or delivery of this script be liable for any damages whatsoever (including, without limitation,
    # damages for loss of business profits, business interruption, loss of business information, or other
    # pecuniary loss) arising out of the use of or inability to use this sample script or documentation, even
    # if Microsoft has been advised of the possibility of such damages.
    #
    #************************************************************************************************************
 
#>

#region Convert-IntunePoliciesToHtml
function Convert-IntunePoliciesToHtml 
{
    param 
    (
        [Parameter(Mandatory=$false)]
        [string]$OutputPath,

        [Parameter(Mandatory=$false)]
        [array]$Policies,

        [Parameter(Mandatory=$false)]
        [string]$Title = "Policy Report"
    )

    if ([string]::IsNullOrEmpty($script:MDMDiagReportPathVariable)) 
    {
        $headerSubText = "Generated locally running on $($env:COMPUTERNAME) on: 📅 $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
    }
    else 
    {
        $headerSubText = "Generated from captured MDM Diagnostics Report on: 📅 $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
    }

$htmlHeader = @"
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>$Title</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 14px; }
        h1 { font-size: 24px; color: #2E6DA4; }
        h2 { font-size: 18px; color: #444; margin-top: 10px; }
 
        .toggle-button {
            background-color: #007BFF;
            color: white;
            border: none;
            padding: 5px 10px;
            margin-bottom: 10px;
            cursor: pointer;
            border-radius: 4px;
            width: 90px; /* Fixed width for consistent size */
            text-align: center; /* Center the text */
            box-sizing: border-box; /* Ensure padding is included in width */
        }
 
        .toggle-button-inner {
            background-color: #a5a5a5ff;
            color: white;
            border: none;
            padding: 5px 10px;
            margin-bottom: 5px;
            cursor: pointer;
            border-radius: 4px;
            font-size: 11px; /* Smaller font size for inner buttons */
            width: 70px; /* Fixed width for consistent size */
            text-align: center; /* Center the text */
            box-sizing: border-box; /* Ensure padding is included in width */
        }
 
        .toggle-button-inner-script {
            background-color: #ffffffff;
            color: #333333; /* Darker text color for visibility */
            border: 2px solid #ccc;
            padding: 3px 6px;
            margin-bottom: 5px;
            cursor: pointer;
            border-radius: 4px;
            font-size: 13px; /* Increased font size for visibility */
            /* font-weight: bold; Make the symbol stand out */
            /* font-family: Arial, sans-serif; Clean, readable font */
            width: 50px; /* Fixed width for consistent size */
            height: 30px; /* Fixed hight for consistent size */
            text-align: center; /* Center the text */
            box-sizing: border-box; /* Ensure padding is included in width */
        }
 
        .collapsible-content {
            display: block;
            margin-top: 10px;
        }
 
        .group-container {
            border: 1px solid #ccc;
            background-color: #f9f9f9;
            padding: 15px;
            margin-bottom: 30px;
            border-radius: 6px;
            box-shadow: 2px 2px 5px rgba(0,0,0,0.05);
        }
 
        .script-container {
            border: 1px solid #ccc;
            background-color: #f9f9f9;
            padding: 5px;
            margin-bottom: 5px;
            border-radius: 6px;
            box-shadow: 2px 2px 5px rgba(0,0,0,0.05);
        }
 
        .policy-area-title {
            color: #2E6DA4;
        }
         
 
        /* === MAIN TABLE STYLING === */
        .main-table {
            border-collapse: collapse;
            width: 100%;
            margin-bottom: 20px;
            table-layout: fixed;
            border: 3px solid #ddd;
            font-size: 13px;
        }
 
        .main-table th,
        .main-table td {
            border: 1px solid #ddd;
            padding: 8px;
            word-wrap: break-word;
            text-align: left;
            vertical-align: top;
            font-size: 13px;
        }
 
        .main-table th {
            background-color: #f2f2f2;
        }
 
        .main-table tr:nth-child(even) {
            background-color: #f9f9f9;
        }
 
        .main-table th.resource-col,
        .main-table td.resource-col {
            width: 100px;
        }
 
        /* === NESTED TABLE STYLING === */
        .nested-table {
            border: none !important;
            outline: none !important;
            background-color: transparent !important;
            border-collapse: collapse;
            width: 100%;
            table-layout: fixed;
            font-size: 13px;
        }
 
        .nested-table th,
        .nested-table td {
            background-color: transparent !important;
            outline: none !important;
            border: none !important;
            padding: 8px;
            word-wrap: break-word;
            text-align: left;
            vertical-align: top;
        }
 
        .nested-table td:first-child {
            width: 200px;
        }
 
     
 
    </style>
    <script>
        
        function toggleContent(button) {
            // Find the main content section to toggle
            let content = button.parentElement.nextElementSibling;
 
            if (!content || !content.classList.contains('collapsible-content')) {
                const groupContainer = button.closest('.group-container');
                content = groupContainer ? groupContainer.querySelector('.collapsible-content') : null;
            }
 
            if (!content) return;
 
            const isVisible = window.getComputedStyle(content).display !== "none";
            const newDisplay = isVisible ? "none" : "block";
            const newLabel = isVisible ? "Show" : "Hide";
 
            // Toggle the main content
            content.style.display = newDisplay;
            button.textContent = newLabel;
 
            // Also toggle all nested collapsible contents and update their buttons
            const nestedContents = content.querySelectorAll('.collapsible-content');
            const nestedButtons = content.querySelectorAll('.toggle-button');
 
            nestedContents.forEach(nested => {
                nested.style.display = newDisplay;
            });
 
            nestedButtons.forEach(nestedBtn => {
                nestedBtn.textContent = newLabel;
            });
        }
 
  
        function toggleAll() {
            const contents = document.querySelectorAll('.collapsible-content');
            const buttons = document.querySelectorAll('.toggle-button:not(#toggleAllBtn)');
            const toggleAllBtn = document.getElementById('toggleAllBtn');
            const shouldCollapse = toggleAllBtn.textContent === 'Collapse All';
 
            contents.forEach((content, index) => {
                    content.style.display = shouldCollapse ? 'none' : 'block';
                if (buttons[index]) {
                buttons[index].textContent = shouldCollapse ? 'Show' : 'Hide';
            }
        });
 
 
        toggleAllBtn.textContent = shouldCollapse ? 'Expand All' : 'Collapse All';
        }
 
 
        function toggleScript(button) {
            const pre = button.parentElement.querySelector('.script-body');
            const isVisible = pre.style.display !== 'none';
            pre.style.display = isVisible ? 'none' : 'block';
            button.textContent = isVisible ? 'Show' : 'Hide';
        }
 
 
        function copyScript(button) {
            const pre = button.parentElement.querySelector('.script-body');
            const text = pre.textContent;
            navigator.clipboard.writeText(text).then(() => {
                button.textContent = '✅';
                setTimeout(() => button.textContent = 'Copy', 1500);
            });
        }
 
    </script>
</head>
<body>
    <h1>$Title ⚙️</h1>
    <p>$headerSubText</p>
    <p>This report contains detailed information about Intune policies applied to devices and users.</p>
    <button class='toggle-button' onclick='toggleAll()' id='toggleAllBtn'>Collapse All</button>
"@



    $htmlFooter = "</body></html>"
    $htmlBody = ""
 
    $grouped = $Policies | Group-Object -Property PolicyScope

    $htmlBody += Get-DeviceInfoHTMLTables -GroupedPolicies $grouped

    $htmlBody += Get-DeviceAndUserHTMLTables -GroupedPolicies $grouped

    $htmlBody += Get-EnterpriseApplicationHTMLTables -GroupedPolicies $grouped

    $htmlBody += Get-ResourceHTMLTables -GroupedPolicies $grouped

    $htmlBody += Get-LAPSHTMLTables -GroupedPolicies $grouped 

    $htmlBody += Get-IntuneWin32AppTables

    $htmlBody += Get-IntuneScriptPolicyTables

    $fullHtml = $htmlHeader + $htmlBody + $htmlFooter

    Set-Content -Path $OutputPath -Value $fullHtml -Encoding UTF8
}
#endregion