Public/Export-EFEndpointReport.ps1
|
function Export-EFEndpointReport { <# .SYNOPSIS Exports EndpointForge objects to HTML, JSON, CSV, or CLIXML. .DESCRIPTION Accepts pipeline input and writes a report. HTML creates a self-contained, plain-language report for people; it does not load scripts, fonts, or other content from the internet. Nested properties are preserved in JSON and CLIXML; CSV serializes nested values as compact JSON strings. Existing files require Force. The command supports WhatIf and Confirm. .PARAMETER InputObject One or more EndpointForge or PowerShell objects to export. .PARAMETER Path The destination path. A matching extension is appended when none is provided. .PARAMETER Format Html, Json, Csv, or Clixml. When omitted, the format is inferred from the destination extension and defaults to Json when the path has no extension. .PARAMETER Depth The maximum object depth used for JSON and CLIXML serialization. .PARAMETER AsArray Always writes a JSON array, including when the pipeline contains one object. .PARAMETER Force Overwrites an existing report. .PARAMETER PassThru Returns the created FileInfo. The command is silent by default. .EXAMPLE Get-EFEndpointHealth | Export-EFEndpointReport -Path C:\ProgramData\EndpointForge\health.json .EXAMPLE Get-EFInstalledSoftware | Export-EFEndpointReport -Path .\software.csv -Format Csv -Force .EXAMPLE Get-EFEndpointSummary -NoProgress | Export-EFEndpointReport -Path .\computer-check.html .OUTPUTS System.IO.FileInfo when PassThru is specified. .LINK Get-EFEndpointSummary #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')] param( [Parameter(Mandatory, ValueFromPipeline)] [AllowNull()] [object]$InputObject, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Path, [ValidateSet('Html', 'Json', 'Csv', 'Clixml')] [string]$Format, [ValidateRange(2, 100)] [int]$Depth = 12, [switch]$AsArray, [switch]$Force, [switch]$PassThru ) begin { $items = [Collections.Generic.List[object]]::new() } process { $items.Add($InputObject) } end { $resolvedPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path) $pathExtension = [IO.Path]::GetExtension($resolvedPath).ToLowerInvariant() $extensionFormat = switch ($pathExtension) { '.html' { 'Html' } '.htm' { 'Html' } '.json' { 'Json' } '.csv' { 'Csv' } '.clixml' { 'Clixml' } default { $null } } $effectiveFormat = if ($PSBoundParameters.ContainsKey('Format')) { $Format } elseif ($null -ne $extensionFormat) { $extensionFormat } else { 'Json' } if (-not [string]::IsNullOrWhiteSpace($pathExtension) -and $null -eq $extensionFormat) { throw [System.ArgumentException]::new( "Report extension '$pathExtension' is not supported. Use .html, .json, .csv, or .clixml." ) } if ($null -ne $extensionFormat -and $PSBoundParameters.ContainsKey('Format') -and $extensionFormat -ne $Format) { throw [System.ArgumentException]::new( "Path extension '$pathExtension' does not match requested format '$Format'. Use a matching extension or omit Format." ) } if ([string]::IsNullOrWhiteSpace($pathExtension)) { $extension = switch ($effectiveFormat) { 'Html' { '.html' } 'Json' { '.json' } 'Csv' { '.csv' } 'Clixml' { '.clixml' } } $resolvedPath += $extension } if ($AsArray -and $effectiveFormat -ne 'Json') { throw [System.ArgumentException]::new('AsArray is supported only for JSON reports.') } $containsNull = $false foreach ($reportItem in $items) { if ($null -eq $reportItem) { $containsNull = $true; break } } if ($containsNull -and $effectiveFormat -ne 'Json') { throw [System.ArgumentException]::new('Null report input is supported only for JSON output.') } if ((Test-Path -LiteralPath $resolvedPath) -and -not $Force) { throw [System.IO.IOException]::new("Report '$resolvedPath' already exists. Use -Force to overwrite it.") } if (-not $PSCmdlet.ShouldProcess($resolvedPath, "Export $($items.Count) report object(s) as $effectiveFormat")) { return } $parent = Split-Path -Parent $resolvedPath if (-not [string]::IsNullOrWhiteSpace($parent) -and -not (Test-Path -LiteralPath $parent)) { $null = New-Item -ItemType Directory -Path $parent -Force -ErrorAction Stop } $output = if ($items.Count -eq 1 -and -not $AsArray) { $items[0] } else { @($items) } $utf8WithoutBom = [Text.UTF8Encoding]::new($false) switch ($effectiveFormat) { 'Html' { $html = ConvertTo-EFHtmlReport -InputObject $items.ToArray() -Title 'EndpointForge computer report' [IO.File]::WriteAllText($resolvedPath, $html, $utf8WithoutBom) } 'Json' { if ($AsArray) { $serializableItems = [Collections.Generic.List[object]]::new() foreach ($item in $items) { if ($null -eq $item) { $serializableItems.Add($null) } else { $serializableItems.Add((ConvertTo-EFSerializableValue -InputObject $item)) } } $json = ConvertTo-Json -InputObject $serializableItems.ToArray() -Depth $Depth } else { $serializableOutput = ConvertTo-EFSerializableValue -InputObject $output $json = if ($null -eq $output) { 'null' } else { ConvertTo-Json -InputObject $serializableOutput -Depth $Depth } } [IO.File]::WriteAllText($resolvedPath, $json, $utf8WithoutBom) } 'Csv' { $csv = @($items | ForEach-Object { ConvertTo-EFFlatRecord -InputObject $_ }) | ConvertTo-Csv -NoTypeInformation [IO.File]::WriteAllLines($resolvedPath, [string[]]$csv, $utf8WithoutBom) } 'Clixml' { $output | Export-Clixml -LiteralPath $resolvedPath -Depth $Depth -Force:$Force -ErrorAction Stop } } Write-EFLog -Message "Report exported to '$resolvedPath'." -Data @{ format = $effectiveFormat; itemCount = $items.Count } if ($PassThru) { Get-Item -LiteralPath $resolvedPath } } } |