modules/Devolutions.CIEM.PSU/Pages/New-CIEMReportsPage.ps1
|
function New-CIEMReportsPage { <# .SYNOPSIS Creates the Reports page with registered CIEM report definitions. .DESCRIPTION Renders the report registry exposed by Get-CIEMReport so users can see available report workflows from the CIEM sidebar. .PARAMETER Navigation Array of UDListItem components for sidebar navigation. #> [CmdletBinding()] param( [Parameter(Mandatory)] [object[]]$Navigation ) $ErrorActionPreference = 'Stop' New-UDPage -Name 'Reports' -Url '/ciem/reports' -Content { New-UDTypography -Text 'Reports' -Variant 'h4' -Style @{ marginBottom = '10px'; marginTop = '10px' } New-UDTypography -Text 'Available CIEM reports and evidence views' -Variant 'subtitle1' -Style @{ marginBottom = '20px'; opacity = 0.7 } New-UDCard -Content { New-UDDynamic -Id 'ciemReportsPanel' -Content { try { $reports = @(Devolutions.CIEM\Get-CIEMReport | Sort-Object Provider, Category, Title) if ($reports.Count -eq 0) { New-UDTypography -Text 'No CIEM reports are registered.' -Variant 'body2' -Style @{ opacity = 0.5; fontStyle = 'italic'; padding = '16px' } } else { if (-not $Page:SelectedReportId) { $Page:SelectedReportId = [string]$reports[0].Id } $selectedReport = $reports | Where-Object { $_.Id -eq $Page:SelectedReportId } | Select-Object -First 1 if (-not $selectedReport) { throw "Selected CIEM report '$($Page:SelectedReportId)' is not registered." } $registryRows = @( foreach ($report in $reports) { [pscustomobject]@{ Title = $report.Title Provider = $report.Provider Category = $report.Category Description = $report.Description } } ) New-UDElement -Tag 'div' -Attributes @{ 'data-ciem-reports-table' = 'true'; style = @{ marginBottom = '18px' } } -Content { New-UDTable -Data $registryRows -Columns @( New-UDTableColumn -Property 'Title' -Title 'Report' New-UDTableColumn -Property 'Provider' -Title 'Provider' New-UDTableColumn -Property 'Category' -Title 'Category' New-UDTableColumn -Property 'Description' -Title 'Description' ) } $completedRuns = @(Devolutions.CIEM\Get-CIEMAzureDiscoveryRun -Status 'Completed' -Last 25) New-UDElement -Tag 'div' -Attributes @{ 'data-ciem-report-history' = 'true'; style = @{ marginBottom = '18px' } } -Content { New-UDTypography -Text 'Completed Discovery Runs' -Variant 'h5' -Style @{ marginBottom = '8px' } New-UDTypography -Text 'Generate the selected report against any completed discovery run to view current or past report evidence.' -Variant 'body2' -Style @{ opacity = 0.7; marginBottom = '14px' } if ($completedRuns.Count -eq 0) { New-UDTypography -Text 'No completed discovery runs are available for report generation.' -Variant 'body2' -Style @{ opacity = 0.6; fontStyle = 'italic'; padding = '12px' } } else { if (-not $Page:SelectedReportRunId) { $Page:SelectedReportRunId = [int]$completedRuns[0].Id } New-UDStack -Direction 'row' -Spacing 2 -AlignItems 'center' -Content { New-UDElement -Tag 'div' -Attributes @{ style = @{ minWidth = '300px' } } -Content { New-UDSelect -Id 'reportSelector' -Label 'Report' -Option { foreach ($report in $reports) { New-UDSelectOption -Name $report.Title -Value $report.Id } } -DefaultValue $Page:SelectedReportId -OnChange { $Page:SelectedReportId = [string]$EventData $Page:ReportRequest = $null Sync-UDElement -Id 'ciemReportsPanel' } -FullWidth } New-UDElement -Tag 'div' -Attributes @{ style = @{ minWidth = '420px' } } -Content { New-UDSelect -Id 'reportRunSelector' -Label 'Discovery Run' -Option { foreach ($run in $completedRuns) { $completedAt = ([datetime]$run.CompletedAt).ToString('yyyy-MM-dd HH:mm') New-UDSelectOption -Name "Run #$($run.Id) - $completedAt UTC - Scope $($run.Scope)" -Value $run.Id } } -DefaultValue $Page:SelectedReportRunId -OnChange { $Page:SelectedReportRunId = [int]$EventData $Page:ReportRequest = $null Sync-UDElement -Id 'ciemReportsPanel' } -FullWidth } New-UDButton -Id 'generateReportBtn' -Text 'Generate Report' -Variant 'contained' -Color 'primary' -OnClick { $selectedReportId = [string](Get-UDElement -Id 'reportSelector').value $selectedRunId = [int](Get-UDElement -Id 'reportRunSelector').value if ([string]::IsNullOrWhiteSpace($selectedReportId)) { throw 'Report selection is required.' } if ($selectedRunId -le 0) { throw 'Completed discovery run selection is required.' } $Page:SelectedReportId = $selectedReportId $Page:SelectedReportRunId = $selectedRunId $Page:ReportRequest = @{ ReportId = $selectedReportId RunId = $selectedRunId } Sync-UDElement -Id 'ciemReportsPanel' } } } } if (-not $Page:ReportRequest) { New-UDElement -Tag 'div' -Attributes @{ 'data-ciem-report-placeholder' = 'true'; style = @{ padding = '14px'; opacity = 0.7 } } -Content { New-UDTypography -Text 'Select a report and completed discovery run, then generate the report.' -Variant 'body2' } } else { $selectedRunId = [int]$Page:ReportRequest.RunId $selectedReport = $reports | Where-Object { $_.Id -eq $Page:ReportRequest.ReportId } | Select-Object -First 1 if (-not $selectedReport) { throw "Selected CIEM report '$($Page:ReportRequest.ReportId)' is not registered." } $result = Devolutions.CIEM\Invoke-CIEMReport -Id $selectedReport.Id -Parameter @{ RunId = $selectedRunId } $reportRows = @($result.Rows) $tableRows = @( foreach ($row in $reportRows) { $tableRow = [ordered]@{} foreach ($column in $result.Columns) { $property = $row.PSObject.Properties[$column] if (-not $property) { throw "CIEM report '$($result.ReportId)' row is missing configured column '$column'." } $tableRow[$column] = $property.Value } [pscustomobject]$tableRow } ) New-UDElement -Tag 'div' -Attributes @{ 'data-ciem-report-result' = 'true'; style = @{ display = 'grid'; gap = '14px' } } -Content { New-UDElement -Tag 'div' -Attributes @{ 'data-ciem-report-context' = 'true' } -Content { New-UDStack -Direction 'row' -Spacing 2 -AlignItems 'center' -Content { New-UDTypography -Text $result.Title -Variant 'h5' New-UDChip -Label "Run #$($result.Context.RunId)" -Size 'small' -Variant 'outlined' New-UDChip -Label "Scope $($result.Context.Scope)" -Size 'small' -Variant 'outlined' New-UDChip -Label "Status $($result.Context.Status)" -Size 'small' -Variant 'outlined' New-UDChip -Label "Generated $($result.GeneratedAt.ToString('yyyy-MM-dd HH:mm')) UTC" -Size 'small' -Variant 'outlined' } } New-UDElement -Tag 'div' -Attributes @{ 'data-ciem-report-summary' = 'true' } -Content { New-UDStack -Direction 'row' -Spacing 1 -AlignItems 'center' -Content { foreach ($summary in @($selectedReport.StatusSummary)) { $status = [string]$summary.status $count = @($reportRows | Where-Object { $_.Status -eq $status }).Count New-UDChip -Label "$status $count" -Size 'small' -Style @{ backgroundColor = [string]$summary.color; color = 'white' } } } } if ($tableRows.Count -eq 0) { New-UDElement -Tag 'div' -Attributes @{ 'data-ciem-report-result-table' = 'true' } -Content { New-UDTypography -Text $selectedReport.EmptyState -Variant 'body2' -Style @{ opacity = 0.6; fontStyle = 'italic'; padding = '12px' } } } else { New-UDElement -Tag 'div' -Attributes @{ 'data-ciem-report-result-table' = 'true' } -Content { New-UDTable -Data $tableRows -Columns @( foreach ($column in $result.Columns) { New-UDTableColumn -Property $column -Title $column } ) } } } } } } catch { Devolutions.CIEM\Write-CIEMLog -Message "Reports page failed: $($_.Exception.Message)" -Severity ERROR -Component 'PSU-ReportsPage' New-UDTypography -Text "Unable to load CIEM report: $($_.Exception.Message)" -Variant 'body2' -Style @{ opacity = 0.8; fontStyle = 'italic'; padding = '16px' } } } } } -Navigation $Navigation -NavigationLayout permanent } |