Private/_New-M365AuditHtml.ps1
|
function _New-M365AuditHtml { <# .SYNOPSIS Generates the M365 security audit HTML dashboard. #> param( [hashtable]$AuditResults, [string]$TenantName ) $css = @" <style> body { font-family: Segoe UI, Arial, sans-serif; margin: 20px; background: #f5f5f5; } h1 { color: #2c3e50; border-bottom: 3px solid #0078d4; padding-bottom: 10px; } h2 { color: #34495e; margin-top: 30px; border-bottom: 1px solid #bdc3c7; padding-bottom: 5px; } .meta { color: #7f8c8d; margin-bottom: 20px; } .dashboard { display: flex; gap: 15px; flex-wrap: wrap; margin-bottom: 20px; } .card { background: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); min-width: 180px; text-align: center; } .card .number { font-size: 36px; font-weight: bold; } .card .label { color: #7f8c8d; font-size: 12px; margin-top: 5px; } .card.danger .number { color: #e74c3c; } .card.warning .number { color: #e67e22; } .card.ok .number { color: #27ae60; } table { border-collapse: collapse; width: 100%; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,0.1); margin-bottom: 20px; } th { background: #0078d4; color: #fff; padding: 10px 8px; text-align: left; font-size: 11px; } td { padding: 8px; border-bottom: 1px solid #ecf0f1; font-size: 11px; } tr:nth-child(even) { background: #f9f9f9; } tr:hover { background: #eaf2f8; } .risk { color: #e74c3c; font-weight: bold; } .ok { color: #27ae60; } .rec { background: #fff3cd; padding: 15px; border-radius: 5px; border-left: 4px solid #e67e22; margin: 10px 0; } </style> "@ $mfaTotal = $AuditResults['MFA'].Count $mfaEnabled = @($AuditResults['MFA'] | Where-Object MFAEnabled -eq $true).Count $mfaDisabled = $mfaTotal - $mfaEnabled $mfaPercent = if ($mfaTotal -gt 0) { [math]::Round(($mfaEnabled / $mfaTotal) * 100) } else { 0 } $forwardingCount = $AuditResults['Forwarding'].Count $externalForwarding = @($AuditResults['Forwarding'] | Where-Object IsExternal -eq $true).Count $guestCount = $AuditResults['Guests'].Count $staleGuests = @($AuditResults['Guests'] | Where-Object StaleGuest -eq $true).Count $caCount = $AuditResults['ConditionalAccess'].Count $mfaClass = if ($mfaPercent -ge 95) { 'ok' } elseif ($mfaPercent -ge 80) { 'warning' } else { 'danger' } # MFA table rows $mfaNoRows = ($AuditResults['MFA'] | Where-Object MFAEnabled -eq $false | ForEach-Object { "<tr><td>$($_.DisplayName)</td><td>$($_.UserPrincipalName)</td><td class='risk'>No MFA</td><td>$($_.LastSignIn)</td></tr>" }) -join "`n" # Forwarding rows $fwdRows = ($AuditResults['Forwarding'] | ForEach-Object { $extClass = if ($_.IsExternal) { " class='risk'" } else { '' } "<tr><td>$($_.Mailbox)</td><td>$($_.RuleType)</td><td$extClass>$($_.ForwardTo)</td><td>$($_.IsExternal)</td><td>$($_.RuleName)</td></tr>" }) -join "`n" # CA rows $caRows = ($AuditResults['ConditionalAccess'] | ForEach-Object { "<tr><td>$($_.PolicyName)</td><td>$($_.State)</td><td>$($_.TargetUsers)</td><td>$($_.TargetApps)</td><td>$($_.RequiresMFA)</td><td>$($_.BlocksLegacy)</td></tr>" }) -join "`n" # Guest rows $guestRows = ($AuditResults['Guests'] | Sort-Object StaleGuest -Descending | ForEach-Object { $staleClass = if ($_.StaleGuest) { " class='risk'" } else { '' } "<tr><td>$($_.DisplayName)</td><td>$($_.Email)</td><td>$($_.InviteState)</td><td>$($_.LastSignIn)</td><td$staleClass>$($_.StaleGuest)</td><td>$($_.Groups)</td></tr>" }) -join "`n" @" <!DOCTYPE html> <html> <head><title>M365 Security Audit - $TenantName</title>$css</head> <body> <h1>Microsoft 365 Security Baseline Audit</h1> <div class="meta">Tenant: $TenantName | Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm')</div> <div class="dashboard"> <div class="card $mfaClass"><div class="number">$mfaPercent%</div><div class="label">MFA Adoption</div></div> <div class="card $(if($mfaDisabled -gt 0){'danger'}else{'ok'})"><div class="number">$mfaDisabled</div><div class="label">Users Without MFA</div></div> <div class="card $(if($externalForwarding -gt 0){'danger'}else{'ok'})"><div class="number">$externalForwarding</div><div class="label">External Forwarding</div></div> <div class="card"><div class="number">$caCount</div><div class="label">CA Policies</div></div> <div class="card $(if($staleGuests -gt 5){'warning'}else{'ok'})"><div class="number">$staleGuests</div><div class="label">Stale Guests</div></div> </div> <h2>Users Without MFA ($mfaDisabled)</h2> $(if($mfaDisabled -gt 0){"<div class='rec'>Recommendation: Enable MFA for all users via Conditional Access policy.</div>"}) <table> <tr><th>Name</th><th>UPN</th><th>MFA Status</th><th>Last Sign-In</th></tr> $mfaNoRows </table> <h2>Mailbox Forwarding Rules ($forwardingCount)</h2> $(if($externalForwarding -gt 0){"<div class='rec'>Recommendation: Review external forwarding rules. Consider blocking auto-forwarding to external domains via transport rule.</div>"}) <table> <tr><th>Mailbox</th><th>Rule Type</th><th>Forward To</th><th>External</th><th>Rule Name</th></tr> $fwdRows </table> <h2>Conditional Access Policies ($caCount)</h2> <table> <tr><th>Policy</th><th>State</th><th>Target Users</th><th>Target Apps</th><th>Requires MFA</th><th>Blocks Legacy</th></tr> $caRows </table> <h2>Guest Accounts ($guestCount)</h2> <table> <tr><th>Name</th><th>Email</th><th>Invite State</th><th>Last Sign-In</th><th>Stale</th><th>Groups</th></tr> $guestRows </table> </body> </html> "@ } |