src/Private/Core/Invoke-FotonCollection.ps1
|
function Invoke-FotonCollection { <# Runs one collection task: write artifact, manifest row, timing, error capture, cache. A failure here is logged and does not stop the run - partial collection is evidence. -RequiresElevation: skip with Status SkippedNotElevated when the token is not admin. That is a visibility gap, not a collection error. #> param( [Parameter(Mandatory)][string]$Category, [Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][scriptblock]$Script, [ValidateSet('Json', 'Csv', 'Txt')][string]$Format = 'Json', [string]$Description = '', [switch]$NoCache, [switch]$RequiresElevation ) $dir = $script:FotonSession.Dirs[$Category] $file = Join-Path $dir ("{0}.{1}" -f $Name, $Format.ToLower()) $sw = [System.Diagnostics.Stopwatch]::StartNew() $status = 'Success' $errMsg = '' $data = $null $count = 0 $categories = $script:FotonSession.Categories if ($RequiresElevation -and -not $script:FotonSession.IsElevated) { $status = 'SkippedNotElevated' $errMsg = 'Requires an Administrator token; skipped in standard-user coverage.' $skipText = @( "Foton: task '$Name' was not run." "Status: SkippedNotElevated" $errMsg 'This is a visibility gap. Do not treat a missing or empty artifact as proof the underlying data was absent on the host.' ) -join [Environment]::NewLine try { $skipText | Out-File -FilePath $file -Encoding UTF8 } catch { } $sw.Stop() $null = $script:FotonSession.Manifest.Add([PSCustomObject]@{ Category = $Category Task = $Name Description = $Description File = (Join-Path $categories[$Category] (Split-Path $file -Leaf)) Records = 0 Status = $status DurationMs = [int]$sw.ElapsedMilliseconds Error = $errMsg }) if (-not $NoCache) { $script:FotonSession.Data[$Name] = $null } Write-FotonStatus "$Name - skipped (not elevated)" 'Info' return $null } try { $data = & $Script } catch { $status = 'Error' $errMsg = $_.Exception.Message $null = $script:FotonSession.Errors.Add([PSCustomObject]@{ Task = $Name; Error = $errMsg }) } try { if ($null -eq $data -or ($data -is [System.Array] -and $data.Count -eq 0)) { if ($status -eq 'Success') { $status = 'NoData' } "Foton: no data returned for '$Name'. Status: $status. $errMsg" | Out-File -FilePath $file -Encoding UTF8 } else { $count = @($data).Count switch ($Format) { 'Json' { $data | ConvertTo-Json -Depth 5 -Compress:$false | Out-File -FilePath $file -Encoding UTF8 } 'Csv' { $data | Export-Csv -Path $file -NoTypeInformation -Encoding UTF8 } 'Txt' { $data | Out-String -Width 4096 | Out-File -FilePath $file -Encoding UTF8 } } } } catch { $status = 'WriteError' $errMsg = $_.Exception.Message try { "Foton write error for '$Name': $errMsg" | Out-File -FilePath $file -Encoding UTF8 } catch { } } $sw.Stop() # Copy-style tasks return one row per file with their own Status. Rows prove the # task ran, not that it captured anything: on a real host every hive copy failed # with "in use" and the task was still logged Good. if ($status -eq 'Success') { $copyStatus = Get-FotonCopyTaskStatus -Rows $data if ($copyStatus.Status -ne 'Success') { $status = $copyStatus.Status $errMsg = $copyStatus.Message $null = $script:FotonSession.Errors.Add([PSCustomObject]@{ Task = $Name; Error = ("{0}: {1}" -f $status, $errMsg) }) } } $null = $script:FotonSession.Manifest.Add([PSCustomObject]@{ Category = $Category Task = $Name Description = $Description File = (Join-Path $categories[$Category] (Split-Path $file -Leaf)) Records = $count Status = $status DurationMs = [int]$sw.ElapsedMilliseconds Error = $errMsg }) if (-not $NoCache) { $script:FotonSession.Data[$Name] = $data } switch ($status) { 'Success' { Write-FotonStatus "$Name ($count records, $([int]$sw.ElapsedMilliseconds) ms)" 'Good' } 'NoData' { Write-FotonStatus "$Name - no data returned" 'Info' } 'Partial' { Write-FotonStatus "$Name ($count records) - Partial : $errMsg" 'Warn' } 'Failed' { Write-FotonStatus "$Name ($count records) - Failed : $errMsg" 'Warn' } default { Write-FotonStatus "$Name - $status : $errMsg" 'Warn' } } return $data } |