Public/Export-LMLogAnomalyReport.ps1

<#
.SYNOPSIS
    Generates a detailed HTML report of LogicMonitor log anomaly analysis results.

.DESCRIPTION
    This cmdlet creates an interactive HTML report from log anomaly analysis results. The report includes:
    - Summary statistics and visualizations
    - Severity distribution charts
    - Resource and datasource analysis
    - Detailed alert information with associated logs
    - Interactive data tables with search, filter, and export capabilities

    The report is generated using the PSWriteHTML module and includes responsive design elements
    for optimal viewing on different devices.

.PARAMETER InputObject
    Array of log anomaly results, typically from Find-LMLogAnomalyAlerts.
    Accepts pipeline input.

.OUTPUTS
    Generates an HTML file named "LogicMonitor-Anomaly-Report.html" containing:
    - Summary statistics
    - Interactive charts:
        * Alert severity distribution
        * Top 10 resources by alert count
        * Top 10 datasources by alert count
    - Detailed data table with:
        * Resource details
        * Alert information
        * Associated logs
        * Sentiment analysis
    - Export options (CSV, Excel, PDF)
    - Search and filtering capabilities

.EXAMPLE
    Find-LMLogAnomalyAlerts | Export-LMLogAnomalyReport
    
    Generates a report from the last 14 days of anomaly data.

.EXAMPLE
    $results = Find-LMLogAnomalyAlerts -StartDate (Get-Date).AddDays(-7)
    Export-LMLogAnomalyReport -InputObject $results
    
    Generates a report from the last 7 days of anomaly data using variable input.

.NOTES
    Requirements:
    - PSWriteHTML module must be installed
    - Modern web browser for viewing the report
    
    Features:
    - Responsive design
    - Interactive data visualization
    - Color-coded severity indicators
    - Sortable and searchable tables
    - Multiple export formats
    - Fixed headers for better navigation

.LINK
    Module repo: https://github.com/stevevillardi/Logic.Monitor.SE

.LINK
    PSGallery: https://www.powershellgallery.com/packages/Logic.Monitor.SE

.LINK
    PSWriteHTML: https://www.powershellgallery.com/packages/PSWriteHTML
#>


Function Export-LMLogAnomalyReport {
    [CmdletBinding()]
    Param(
        [Parameter(ValueFromPipeline = $true)]
        [Array]$InputObject
    )

    Begin {
        if (-not (Get-Module -Name PSWriteHTML -ListAvailable)) {
            Write-Error "PSWriteHTML module is required. Please install it using: Install-Module PSWriteHTML -Force"
            return
        }
        Import-Module PSWriteHTML
        $allAlerts = @()
    }

    Process {
        $allAlerts += $InputObject
    }

    End {
        $DateGenerated = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        $ReportPath = "LogicMonitor-Anomaly-Report.html"
        $Title = "LogicMonitor Anomaly Analysis Report"

        # Prepare the main table data
        $tableData = $allAlerts | ForEach-Object {
            [PSCustomObject]@{
                'Resource'          = $_.ResourceName
                'Instance'          = $_.InstanceName
                'Datapoint'         = $_.DatapointName
                'Datasource'        = $_.DatasourceName
                'AlertId'           = $_.AlertId
                'Alert Url'         = $_.AlertUrl
                'Alert Value'       = $_.AlertValue
                'Alert Type'        = $_.AlertType
                'Severity'          = $_.Severity
                'Timestamp'         = $_.AlertTimestamp.ToString('yyyy-MM-dd HH:mm:ss')
                'Log Count'         = $_.LogCount
                'Average Sentiment' = $_.AverageSentimentScore
                'Logs'              = if ($_.AssociatedLogs) {
                    $_.AssociatedLogs | ForEach-Object {
                        "`n[$($_.LogTimestamp.ToString('yyyy-MM-dd HH:mm:ss'))] [$($_.TimeDifferenceMinutes) minutes] [Score: $($_.SentimentScore)] $($_.LogMessage)"
                    } | Out-String
                } else { "No associated logs" }
            }
        }

        New-HTML -TitleText $Title -FilePath $ReportPath {
            # Add custom CSS for better layout
            New-HTMLHeader {
                New-HTMLText -Text "Report Generated: $DateGenerated" -Color Gray
            }


            # Summary Statistics Row
            New-HTMLSection -Invisible {
                New-HTMLPanel {
                    New-HTMLToast -Text 'Total Alerts with Log Anomalies' -TextHeader $allAlerts.Count -IconSolid exclamation-circle -BarColorLeft Blue -TextHeaderColor Blue
                }
                New-HTMLPanel {
                    New-HTMLToast -Text 'Total Logs Associated with Alerts' -TextHeader ($allAlerts | Measure-Object -Property LogCount -Sum | Select-Object -ExpandProperty Sum) -IconSolid exclamation-circle -BarColorLeft Green -TextHeaderColor Green
                }
                New-HTMLPanel {
                    New-HTMLToast -Text 'Warning Alerts' -TextHeader ($allAlerts | Where-Object Severity -EQ 2 | Measure-Object | Select-Object -ExpandProperty Count) -IconSolid exclamation-circle -BarColorLeft Red -TextHeaderColor Red
                }
                New-HTMLPanel {
                    New-HTMLToast -Text 'Error Alerts' -TextHeader ($allAlerts | Where-Object Severity -EQ 3 | Measure-Object | Select-Object -ExpandProperty Count) -IconSolid exclamation-circle -BarColorLeft Orange -TextHeaderColor Orange
                }
                New-HTMLPanel {
                    New-HTMLToast -Text 'Critical Alerts' -TextHeader ($allAlerts | Where-Object Severity -EQ 4 | Measure-Object | Select-Object -ExpandProperty Count) -IconSolid exclamation-circle -BarColorLeft Red -TextHeaderColor Red
                }
            }

            # Charts Row
            New-HTMLSection -Invisible {
                # Severity Distribution
                New-HTMLPanel -Width 100 {
                    New-HTMLChart {
                        New-ChartToolbar -Download
                        New-ChartLegend -Name 'Severity Distribution'
                        $severityCounts = $allAlerts | Group-Object Severity | Sort-Object Name
                        foreach ($severity in $severityCounts) {
                            $Name = $severity.Name
                            Switch ($severity.Name) {
                                2 { $Name = 'Warning'; $color = '#FFC100' } #yellow
                                3 { $Name = 'Error'; $color = '#FF7400' } #orange
                                4 { $Name = 'Critical'; $color = '#FF0000' } #red
                            }
                            New-ChartPie -Name $Name -Value $severity.Count -Color $color
                        }
                    } -Title 'Alert Severity Distribution'
                }

                # Resource Distribution
                New-HTMLPanel -Width 100 {
                    New-HTMLChart {
                        New-ChartToolbar -Download
                        New-ChartLegend -Name 'Top Resources'
                        $resourceCounts = $allAlerts | Group-Object ResourceName | Sort-Object Count -Descending | Select-Object -First 10
                        foreach ($resource in $resourceCounts) {
                            New-ChartBar -Name $resource.Name -Value $resource.Count
                        }
                    } -Title 'Top 10 Resources'
                }

                # Datasource Distribution
                New-HTMLPanel -Width 100 {
                    New-HTMLChart {
                        New-ChartToolbar -Download
                        New-ChartLegend -Name 'Top Datasources'
                        $datasourceCounts = $allAlerts | Group-Object DatasourceName | Sort-Object Count -Descending | Select-Object -First 10
                        foreach ($ds in $datasourceCounts) {
                            New-ChartDonut -Name $ds.Name -Value $ds.Count
                        }
                    } -Title 'Top 10 Datasources'
                }
            }
            
            # Alert Details Table
            New-HTMLSection -HeaderText 'Alert Details' {
                New-HTMLTable -DataTable $tableData -HideFooter:$false {
                    # Color coding for severity
                    New-TableCondition -Name 'Severity' -Value 2 -BackgroundColor '#FFC100' 
                    New-TableCondition -Name 'Severity' -Value 3 -BackgroundColor '#FF7400' 
                    New-TableCondition -Name 'Severity' -Value 4 -BackgroundColor '#FF0000' 

                } -Buttons @('searchPanes', 'pageLength', 'copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5') `
                    -FixedHeader -FixedFooter -DisablePaging -AutoSize 
            }

            New-HTMLFooter {
                New-HTMLText -Text @(
                    "Total Alerts: $($allAlerts.Count) | ",
                    "Average Sentiment Score: $([math]::Round(($allAlerts | Measure-Object -Property AverageSentimentScore -Average).Average, 2))"
                ) -Color Navy -FontSize 12
            }
        } -Online

        # Add informative messages about report generation
        Write-Host "`n===========================================" -ForegroundColor Cyan
        Write-Host "Report Generation Complete!" -ForegroundColor Green
        Write-Host "----------------------------------------"
        Write-Host "Report Details:" -ForegroundColor Yellow
        Write-Host "- Location: $((Get-Item $ReportPath).FullName)"
        Write-Host "- Generated: $DateGenerated"
        Write-Host "- Total Alerts: $($allAlerts.Count)"
        Write-Host "- Total Associated Logs: $($allAlerts | Measure-Object -Property LogCount -Sum | Select-Object -ExpandProperty Sum)"
        Write-Host "`nOpen the report in your web browser to view the interactive dashboard." -ForegroundColor Cyan
        Write-Host "===========================================" -ForegroundColor Cyan
    }
}