src/report/Import-ScoutReportInventory.ps1
|
#Requires -Version 7.0 Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' function Import-ScoutReportInventory { <# .SYNOPSIS Attach every processed collector dataset to the report without changing rule inputs. .DESCRIPTION Collector rows can repeat resource identities (tags, disks, rules, and other children). Preserve all rows and report their unit separately from distinct resource identities. This is a local read of this run's cache, never an Azure collection or authentication. #> [CmdletBinding()] param( [Parameter(Mandatory)] [object] $Collect, [Parameter(Mandatory)] [string] $ReportCachePath ) $datasets = [ordered]@{} foreach ($file in @(Get-ChildItem -LiteralPath $ReportCachePath -Filter '*.json' -File -ErrorAction Stop | Sort-Object Name)) { $cache = Get-Content -LiteralPath $file.FullName -Raw -ErrorAction Stop | ConvertFrom-Json -Depth 100 -ErrorAction Stop if ($file.BaseName -eq 'Discovery') { $Collect | Add-Member -NotePropertyName discovery -NotePropertyValue $cache -Force continue } foreach ($property in $cache.PSObject.Properties) { # Category cache files contain named arrays, including empty arrays. Metadata # objects are not datasets and should not turn into phantom inventory rows. if ($null -eq $property.Value -or $property.Value -isnot [System.Collections.IList]) { continue } $rows = @($property.Value) $ids = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) $identified = 0 foreach ($row in $rows) { if ($null -eq $row) { continue } foreach ($name in @('id', 'ResourceId', 'Resource ID')) { $value = $row.PSObject.Properties[$name] if ($value -and -not [string]::IsNullOrWhiteSpace([string]$value.Value)) { $null = $ids.Add([string]$value.Value) $identified++ break } } } $key = 'collected.{0}.{1}' -f $file.BaseName.ToLowerInvariant(), $property.Name $datasets[$key] = [ordered]@{ label = '{0} → {1} (collected details)' -f $file.BaseName, ($property.Name -creplace '([a-z0-9])([A-Z])', '$1 $2') count = $rows.Count countUnit = 'evidence rows' resourceCount = if ($identified -eq $rows.Count) { $ids.Count } else { $null } rows = $rows truncated = $null source = '{0}/{1}' -f $file.Name, $property.Name } } } if ((Test-Path -LiteralPath (Join-Path $ReportCachePath 'Identity.json')) -and (Get-Command Get-ScoutEntraQueryCatalog -ErrorAction SilentlyContinue)) { $requirements = @(Get-ScoutEntraQueryCatalog | ForEach-Object { [pscustomobject]@{ Dataset = $_.Name Endpoint = $_.Uri Permission = $_.Permission Licensing = if ($_.ContainsKey('LicensedProduct')) { $_.LicensedProduct } else { 'Check endpoint requirements' } CollectionEnabled = -not $_.ContainsKey('Collect') -or [bool]$_.Collect AccessNote = 'OAuth permission, directory role, tenant consent, guest restrictions and licensing are separate requirements. This matrix is not proof of granted access.' } }) $datasets['collected.general.IdentityPermissionRequirements'] = [ordered]@{ label='Identity permission requirements'; count=$requirements.Count; rows=$requirements; truncated=$null; countUnit='dataset requirements'; resourceCount=$null; source='Entra query catalog' } } $Collect | Add-Member -NotePropertyName _reportInventory -NotePropertyValue $datasets -Force return $Collect } |