Fix: Export-M365SecurityAuditTable function
This commit is contained in:
@@ -31,6 +31,7 @@ The format is based on and uses the types of changes according to [Keep a Change
|
|||||||
- Phish policy test to return if highest priority policy conforms to the benchmark.
|
- Phish policy test to return if highest priority policy conforms to the benchmark.
|
||||||
- Module assertion to check for minimum version of required modules.
|
- Module assertion to check for minimum version of required modules.
|
||||||
- Module assertion to not import the module if it already exists.
|
- Module assertion to not import the module if it already exists.
|
||||||
|
- Fixed Export-M365SecurityAuditTable to ensure there are only 3 parameter sets: One for specific nested test output, one to export only nested tables, and one to export all tests along with options to export to CSV or Excel.
|
||||||
|
|
||||||
## [v0.1.28] - 2025-01-14
|
## [v0.1.28] - 2025-01-14
|
||||||
|
|
||||||
|
@@ -1,235 +1,201 @@
|
|||||||
<#
|
<#
|
||||||
.SYNOPSIS
|
.SYNOPSIS
|
||||||
Exports Microsoft 365 security audit results to CSV or Excel files and supports outputting specific test results as objects.
|
Export Microsoft 365 CIS audit results into CSV/Excel and package with hashes.
|
||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
The Export-M365SecurityAuditTable function exports Microsoft 365 security audit results from an array of CISAuditResult objects or a CSV file.
|
Export-M365SecurityAuditTable processes an array of CISAuditResult objects, exporting per-test nested tables
|
||||||
It can export all results to a specified path, output a specific test result as an object, and includes options for exporting results to Excel.
|
and/or a full audit summary (with oversized fields truncated) to CSV or Excel. All output files are
|
||||||
Additionally, it computes hashes for the exported files and includes them in the zip archive for verification purposes.
|
hashed (SHA256) and bundled into a ZIP archive whose filename includes a short hash for integrity.
|
||||||
.PARAMETER AuditResults
|
.PARAMETER AuditResults
|
||||||
An array of CISAuditResult objects containing the audit results. This parameter is mandatory when exporting from audit results.
|
An array of PSCustomObject (CISAuditResult) objects containing the audit results to export or query.
|
||||||
.PARAMETER CsvPath
|
|
||||||
The path to a CSV file containing the audit results. This parameter is mandatory when exporting from a CSV file.
|
|
||||||
.PARAMETER OutputTestNumber
|
|
||||||
The test number to output as an object. Valid values are "1.1.1", "1.3.1", "6.1.2", "6.1.3", "7.3.4". This parameter is used to output a specific test result.
|
|
||||||
.PARAMETER ExportNestedTables
|
|
||||||
Switch to export all test results. When specified, all test results are exported to the specified path.
|
|
||||||
.PARAMETER ExportPath
|
.PARAMETER ExportPath
|
||||||
The path where the CSV or Excel files will be exported. This parameter is mandatory when exporting all tests.
|
Path to the directory where CSV/Excel files and the final ZIP archive will be placed. Required for
|
||||||
.PARAMETER ExportOriginalTests
|
any file-based export (DefaultExport or OnlyExportNestedTables).
|
||||||
Switch to export the original audit results to a CSV file. When specified, the original test results are exported along with the processed results.
|
|
||||||
.PARAMETER ExportToExcel
|
.PARAMETER ExportToExcel
|
||||||
Switch to export the results to an Excel file. When specified, results are exported in Excel format.
|
Switch to export files in Excel (.xlsx) format instead of CSV. Requires the ImportExcel module.
|
||||||
|
.PARAMETER Prefix
|
||||||
|
A short prefix (0–5 characters, default 'Corp') appended to the summary audit filename and hashes.
|
||||||
|
.PARAMETER OnlyExportNestedTables
|
||||||
|
Switch to export only the per-test nested tables to files, skipping the full audit summary.
|
||||||
|
.PARAMETER OutputTestNumber
|
||||||
|
Specify one test number (valid values: '1.1.1','1.3.1','6.1.2','6.1.3','7.3.4') to return that test’s
|
||||||
|
details in-memory as objects without writing any files.
|
||||||
.INPUTS
|
.INPUTS
|
||||||
[CISAuditResult[]] - An array of CISAuditResult objects.
|
System.Object[] (array of CISAuditResult PSCustomObjects)
|
||||||
[string] - A path to a CSV file.
|
|
||||||
.OUTPUTS
|
.OUTPUTS
|
||||||
[PSCustomObject] - A custom object containing the path to the zip file and its hash.
|
PSCustomObject with property ZipFilePath indicating the final ZIP archive location, or raw test details
|
||||||
|
when using -OutputTestNumber.
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
Export-M365SecurityAuditTable -AuditResults $object -OutputTestNumber 6.1.2
|
# Return details for test 6.1.2
|
||||||
# Outputs the result of test number 6.1.2 from the provided audit results as an object.
|
Export-M365SecurityAuditTable -AuditResults $audits -OutputTestNumber 6.1.2
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
Export-M365SecurityAuditTable -ExportNestedTables -AuditResults $object -ExportPath "C:\temp"
|
# Full export (nested tables + summary) to CSV
|
||||||
# Exports all audit results to the specified path in CSV format.
|
Export-M365SecurityAuditTable -AuditResults $audits -ExportPath "C:\temp"
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
Export-M365SecurityAuditTable -CsvPath "C:\temp\auditresultstoday1.csv" -OutputTestNumber 6.1.2
|
# Only export nested tables to Excel
|
||||||
# Outputs the result of test number 6.1.2 from the CSV file as an object.
|
Export-M365SecurityAuditTable -AuditResults $audits -ExportPath "C:\temp" -OnlyExportNestedTables -ExportToExcel
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
Export-M365SecurityAuditTable -ExportNestedTables -CsvPath "C:\temp\auditresultstoday1.csv" -ExportPath "C:\temp"
|
# Custom prefix for filenames
|
||||||
# Exports all audit results from the CSV file to the specified path in CSV format.
|
Export-M365SecurityAuditTable -AuditResults $audits -ExportPath "C:\temp" -Prefix Dev
|
||||||
.EXAMPLE
|
|
||||||
Export-M365SecurityAuditTable -ExportNestedTables -AuditResults $object -ExportPath "C:\temp" -ExportOriginalTests
|
|
||||||
# Exports all audit results along with the original test results to the specified path in CSV format.
|
|
||||||
.EXAMPLE
|
|
||||||
Export-M365SecurityAuditTable -ExportNestedTables -CsvPath "C:\temp\auditresultstoday1.csv" -ExportPath "C:\temp" -ExportOriginalTests
|
|
||||||
# Exports all audit results from the CSV file along with the original test results to the specified path in CSV format.
|
|
||||||
.EXAMPLE
|
|
||||||
Export-M365SecurityAuditTable -ExportNestedTables -AuditResults $object -ExportPath "C:\temp" -ExportToExcel
|
|
||||||
# Exports all audit results to the specified path in Excel format.
|
|
||||||
.LINK
|
.LINK
|
||||||
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Export-M365SecurityAuditTable
|
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Export-M365SecurityAuditTable
|
||||||
#>
|
#>
|
||||||
function Export-M365SecurityAuditTable {
|
function Export-M365SecurityAuditTable {
|
||||||
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
|
[CmdletBinding(
|
||||||
|
DefaultParameterSetName = 'DefaultExport',
|
||||||
|
SupportsShouldProcess,
|
||||||
|
ConfirmImpact = 'High'
|
||||||
|
)]
|
||||||
[OutputType([PSCustomObject])]
|
[OutputType([PSCustomObject])]
|
||||||
param (
|
param(
|
||||||
[Parameter(Mandatory = $true, Position = 1, ParameterSetName = "ExportAllResultsFromAuditResults")]
|
#───────────────────────────────────────────────────────────────────────────
|
||||||
[Parameter(Mandatory = $true, Position = 2, ParameterSetName = "OutputObjectFromAuditResultsSingle")]
|
# 1) DefaultExport: full audit export (nested tables + summary) into ZIP
|
||||||
[psobject[]]$AuditResults,
|
# -AuditResults, -ExportPath, [-ExportToExcel], [-Prefix]
|
||||||
[Parameter(Mandatory = $true, Position = 1, ParameterSetName = "ExportAllResultsFromCsv")]
|
#───────────────────────────────────────────────────────────────────────────
|
||||||
[Parameter(Mandatory = $true, Position = 2, ParameterSetName = "OutputObjectFromCsvSingle")]
|
[Parameter(Mandatory, ParameterSetName = 'DefaultExport')]
|
||||||
[ValidateScript({ (Test-Path $_) -and ((Get-Item $_).PSIsContainer -eq $false) })]
|
[Parameter(Mandatory, ParameterSetName = 'OnlyExportNestedTables')]
|
||||||
[string]$CsvPath,
|
[Parameter(Mandatory, ParameterSetName = 'SingleObject')]
|
||||||
[Parameter(Mandatory = $true, Position = 1, ParameterSetName = "OutputObjectFromAuditResultsSingle")]
|
[psobject[]]
|
||||||
[Parameter(Mandatory = $true, Position = 1, ParameterSetName = "OutputObjectFromCsvSingle")]
|
$AuditResults,
|
||||||
[ValidateSet("1.1.1", "1.3.1", "6.1.2", "6.1.3", "7.3.4")]
|
[Parameter(Mandatory, ParameterSetName = 'DefaultExport')]
|
||||||
[string]$OutputTestNumber,
|
[Parameter(Mandatory, ParameterSetName = 'OnlyExportNestedTables')]
|
||||||
[Parameter(Mandatory = $false, Position = 0, ParameterSetName = "ExportAllResultsFromAuditResults")]
|
[string]
|
||||||
[Parameter(Mandatory = $false, Position = 0, ParameterSetName = "ExportAllResultsFromCsv")]
|
$ExportPath,
|
||||||
[switch]$ExportNestedTables,
|
[Parameter(ParameterSetName = 'DefaultExport')]
|
||||||
[Parameter(Mandatory = $true, ParameterSetName = "ExportAllResultsFromAuditResults")]
|
[Parameter(ParameterSetName = 'OnlyExportNestedTables')]
|
||||||
[Parameter(Mandatory = $true, ParameterSetName = "ExportAllResultsFromCsv")]
|
[switch]
|
||||||
[string]$ExportPath,
|
$ExportToExcel,
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = "ExportAllResultsFromAuditResults")]
|
[Parameter(ParameterSetName = 'DefaultExport')]
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = "ExportAllResultsFromCsv")]
|
[Parameter(ParameterSetName = 'OnlyExportNestedTables')]
|
||||||
[switch]$ExportOriginalTests,
|
[ValidateLength(0,5)]
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = "ExportAllResultsFromAuditResults")]
|
[string]
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = "ExportAllResultsFromCsv")]
|
$Prefix = 'Corp',
|
||||||
[switch]$ExportToExcel,
|
#───────────────────────────────────────────────────────────────────────────
|
||||||
# Add Prefix to filename after date when outputting to excel or csv.
|
# 2) OnlyExportNestedTables: nested tables only into ZIP
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = "ExportAllResultsFromAuditResults")]
|
# -AuditResults, -ExportPath, -OnlyExportNestedTables
|
||||||
[Parameter(Mandatory = $false, ParameterSetName = "ExportAllResultsFromCsv")]
|
#───────────────────────────────────────────────────────────────────────────
|
||||||
# Validate that the count of letters in the prefix is less than 5.
|
[Parameter(Mandatory, ParameterSetName = 'OnlyExportNestedTables')]
|
||||||
[ValidateLength(0, 5)]
|
[switch]
|
||||||
[string]$Prefix = "Corp"
|
$OnlyExportNestedTables,
|
||||||
|
#───────────────────────────────────────────────────────────────────────────
|
||||||
|
# 3) SingleObject: in-memory output of one test’s details
|
||||||
|
# -AuditResults, -OutputTestNumber
|
||||||
|
#───────────────────────────────────────────────────────────────────────────
|
||||||
|
[Parameter(Mandatory, ParameterSetName = 'SingleObject')]
|
||||||
|
[ValidateSet('1.1.1','1.3.1','6.1.2','6.1.3','7.3.4')]
|
||||||
|
[string]
|
||||||
|
$OutputTestNumber
|
||||||
)
|
)
|
||||||
Begin {
|
Begin {
|
||||||
$createdFiles = @() # Initialize an array to keep track of created files
|
# Load v4.0 definitions
|
||||||
|
$Version = '4.0.0'
|
||||||
|
$script:TestDefinitionsObject = Get-TestDefinition -Version $Version
|
||||||
|
# Ensure Excel support if requested
|
||||||
if ($ExportToExcel) {
|
if ($ExportToExcel) {
|
||||||
if ($PSCmdlet.ShouldProcess("ImportExcel v7.8.9", "Assert-ModuleAvailability")) {
|
Assert-ModuleAvailability -ModuleName ImportExcel -RequiredVersion '7.8.9'
|
||||||
Assert-ModuleAvailability -ModuleName ImportExcel -RequiredVersion "7.8.9"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($PSCmdlet.ParameterSetName -like "ExportAllResultsFromCsv" -or $PSCmdlet.ParameterSetName -eq "OutputObjectFromCsvSingle") {
|
|
||||||
$AuditResults = Import-Csv -Path $CsvPath | ForEach-Object {
|
|
||||||
$params = @{
|
|
||||||
Rec = $_.Rec
|
|
||||||
Result = [bool]$_.Result
|
|
||||||
Status = $_.Status
|
|
||||||
Details = $_.Details
|
|
||||||
FailureReason = $_.FailureReason
|
|
||||||
}
|
|
||||||
Initialize-CISAuditResult @params
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($ExportNestedTables) {
|
|
||||||
$TestNumbers = "1.1.1", "1.3.1", "6.1.2", "6.1.3", "7.3.4"
|
|
||||||
}
|
}
|
||||||
|
# Tests producing nested tables
|
||||||
|
$nestedTests = '1.1.1','1.3.1','6.1.2','6.1.3','7.3.4'
|
||||||
|
# Initialize collections
|
||||||
$results = @()
|
$results = @()
|
||||||
$testsToProcess = if ($OutputTestNumber) { @($OutputTestNumber) } else { $TestNumbers }
|
$createdFiles = [System.Collections.Generic.List[string]]::new()
|
||||||
|
# Determine which tests to process
|
||||||
|
if ($PSCmdlet.ParameterSetName -eq 'SingleObject') {
|
||||||
|
$testsToProcess = @($OutputTestNumber)
|
||||||
|
} else {
|
||||||
|
$testsToProcess = $nestedTests
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Process {
|
Process {
|
||||||
foreach ($test in $testsToProcess) {
|
foreach ($test in $testsToProcess) {
|
||||||
$auditResult = $AuditResults | Where-Object { $_.Rec -eq $test }
|
$item = $AuditResults | Where-Object Rec -EQ $test
|
||||||
if (-not $auditResult) {
|
if (-not $item) { continue }
|
||||||
Write-Information "No audit results found for the test number $test."
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
switch ($test) {
|
switch ($test) {
|
||||||
"6.1.2" {
|
'6.1.2' { $parsed = Get-AuditMailboxDetail -Details $item.Details -Version '6.1.2' }
|
||||||
$details = $auditResult.Details
|
'6.1.3' { $parsed = Get-AuditMailboxDetail -Details $item.Details -Version '6.1.3' }
|
||||||
$newObjectDetails = Get-AuditMailboxDetail -Details $details -Version '6.1.2'
|
Default { $parsed = $item.Details | ConvertFrom-Csv -Delimiter '|' }
|
||||||
$results += [PSCustomObject]@{ TestNumber = $test; Details = $newObjectDetails }
|
|
||||||
}
|
|
||||||
"6.1.3" {
|
|
||||||
$details = $auditResult.Details
|
|
||||||
$newObjectDetails = Get-AuditMailboxDetail -Details $details -Version '6.1.3'
|
|
||||||
$results += [PSCustomObject]@{ TestNumber = $test; Details = $newObjectDetails }
|
|
||||||
}
|
|
||||||
Default {
|
|
||||||
$details = $auditResult.Details
|
|
||||||
$csv = $details | ConvertFrom-Csv -Delimiter '|'
|
|
||||||
$results += [PSCustomObject]@{ TestNumber = $test; Details = $csv }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
$results += [PSCustomObject]@{ TestNumber = $test; Details = $parsed }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
End {
|
End {
|
||||||
if ($ExportPath) {
|
#--- SingleObject: return in-memory details ---
|
||||||
if ($PSCmdlet.ShouldProcess("Export-M365SecurityAuditTable", "Exporting results to $ExportPath")) {
|
if ($PSCmdlet.ParameterSetName -eq 'SingleObject') {
|
||||||
$timestamp = (Get-Date).ToString("yyyy.MM.dd_HH.mm.ss")
|
if ($results.Count -and $results[0].Details) {
|
||||||
$exportedTests = @()
|
|
||||||
foreach ($result in $results) {
|
|
||||||
$testDef = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $result.TestNumber }
|
|
||||||
if ($testDef) {
|
|
||||||
$fileName = "$ExportPath\$($timestamp)_$($result.TestNumber).$($testDef.TestFileName -replace '\.ps1$').csv"
|
|
||||||
if ($result.Details.Count -eq 0) {
|
|
||||||
Write-Information "No results found for test number $($result.TestNumber)."
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (($result.Details -ne "No M365 E3 licenses found.") -and ($result.Details -ne "No M365 E5 licenses found.")) {
|
|
||||||
if ($ExportToExcel) {
|
|
||||||
$xlsxPath = [System.IO.Path]::ChangeExtension($fileName, '.xlsx')
|
|
||||||
$result.Details | Export-Excel -Path $xlsxPath -WorksheetName Table -TableName Table -AutoSize -TableStyle Medium2
|
|
||||||
$createdFiles += $xlsxPath # Add the created file to the array
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$result.Details | Export-Csv -Path $fileName -NoTypeInformation
|
|
||||||
$createdFiles += $fileName # Add the created file to the array
|
|
||||||
}
|
|
||||||
$exportedTests += $result.TestNumber
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($exportedTests.Count -gt 0) {
|
|
||||||
Write-Information "The following tests were exported: $($exportedTests -join ', ')"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if ($ExportOriginalTests) {
|
|
||||||
Write-Information "Full audit results exported however, none of the following tests had exports: `n1.1.1, 1.3.1, 6.1.2, 6.1.3, 7.3.4"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Information "No specified tests were included in the export."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($ExportOriginalTests) {
|
|
||||||
# Define the test numbers to check
|
|
||||||
$TestNumbersToCheck = "1.1.1", "1.3.1", "6.1.2", "6.1.3", "7.3.4"
|
|
||||||
# Check for large details and update the AuditResults array
|
|
||||||
$updatedAuditResults = Get-ExceededLengthResultDetail -AuditResults $AuditResults -TestNumbersToCheck $TestNumbersToCheck -ExportedTests $exportedTests -DetailsLengthLimit 30000 -PreviewLineCount 25
|
|
||||||
$originalFileName = "$ExportPath\$timestamp`_$Prefix-M365FoundationsAudit.csv"
|
|
||||||
if ($ExportToExcel) {
|
|
||||||
$xlsxPath = [System.IO.Path]::ChangeExtension($originalFileName, '.xlsx')
|
|
||||||
$updatedAuditResults | Export-Excel -Path $xlsxPath -WorksheetName Table -TableName Table -AutoSize -TableStyle Medium2
|
|
||||||
$createdFiles += $xlsxPath # Add the created file to the array
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$updatedAuditResults | Export-Csv -Path $originalFileName -NoTypeInformation
|
|
||||||
$createdFiles += $originalFileName # Add the created file to the array
|
|
||||||
}
|
|
||||||
}
|
|
||||||
# Hash each file and add it to a dictionary
|
|
||||||
# Hash each file and save the hashes to a text file
|
|
||||||
$hashFilePath = "$ExportPath\$timestamp`_Hashes.txt"
|
|
||||||
$fileHashes = @()
|
|
||||||
foreach ($file in $createdFiles) {
|
|
||||||
$hash = Get-FileHash -Path $file -Algorithm SHA256
|
|
||||||
$fileHashes += "$($file): $($hash.Hash)"
|
|
||||||
}
|
|
||||||
$fileHashes | Set-Content -Path $hashFilePath
|
|
||||||
$createdFiles += $hashFilePath # Add the hash file to the array
|
|
||||||
# Create a zip file and add all the created files
|
|
||||||
$zipFilePath = "$ExportPath\$timestamp`_$Prefix-M365FoundationsAudit.zip"
|
|
||||||
Compress-Archive -Path $createdFiles -DestinationPath $zipFilePath
|
|
||||||
# Remove the original files after they have been added to the zip
|
|
||||||
foreach ($file in $createdFiles) {
|
|
||||||
Remove-Item -Path $file -Force
|
|
||||||
}
|
|
||||||
# Compute the hash for the zip file and rename it
|
|
||||||
$zipHash = Get-FileHash -Path $zipFilePath -Algorithm SHA256
|
|
||||||
$newZipFilePath = "$ExportPath\$timestamp`_$Prefix-M365FoundationsAudit_$($zipHash.Hash.Substring(0, 8)).zip"
|
|
||||||
Rename-Item -Path $zipFilePath -NewName $newZipFilePath
|
|
||||||
# Output the zip file path with hash
|
|
||||||
return [PSCustomObject]@{
|
|
||||||
ZipFilePath = $newZipFilePath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} # End of ExportPath
|
|
||||||
elseif ($OutputTestNumber) {
|
|
||||||
if ($results[0].Details) {
|
|
||||||
return $results[0].Details
|
return $results[0].Details
|
||||||
}
|
}
|
||||||
else {
|
throw "No results found for test $OutputTestNumber."
|
||||||
Write-Information "No results found for test number $($OutputTestNumber)."
|
|
||||||
}
|
}
|
||||||
|
#--- File export: DefaultExport or OnlyExportNestedTables ---
|
||||||
|
if (-not $ExportPath) {
|
||||||
|
throw 'ExportPath is required for file export.'
|
||||||
}
|
}
|
||||||
else {
|
if ($PSCmdlet.ShouldProcess($ExportPath, 'Export and archive audit results')) {
|
||||||
Write-Error "No valid operation specified. Please provide valid parameters."
|
# Ensure directory
|
||||||
|
if (-not (Test-Path $ExportPath)) { New-Item -Path $ExportPath -ItemType Directory -Force | Out-Null }
|
||||||
|
$timestamp = (Get-Date).ToString('yyyy.MM.dd_HH.mm.ss')
|
||||||
|
$exportedTests = @()
|
||||||
|
# Always truncate large details before writing files
|
||||||
|
Write-Verbose 'Truncating oversized details...'
|
||||||
|
$truncatedAudit = Get-ExceededLengthResultDetail `
|
||||||
|
-AuditResults $AuditResults `
|
||||||
|
-TestNumbersToCheck $nestedTests `
|
||||||
|
-ExportedTests $exportedTests `
|
||||||
|
-DetailsLengthLimit 30000 `
|
||||||
|
-PreviewLineCount 25
|
||||||
|
#--- Export nested tables ---
|
||||||
|
Write-Verbose "[$($PSCmdlet.ParameterSetName)] exporting nested table CSV/XLSX..."
|
||||||
|
foreach ($entry in $results) {
|
||||||
|
if (-not $entry.Details) { continue }
|
||||||
|
$name = "$timestamp`_$($entry.TestNumber)"
|
||||||
|
$csv = Join-Path $ExportPath "$name.csv"
|
||||||
|
if ($ExportToExcel) {
|
||||||
|
$xlsx = [IO.Path]::ChangeExtension($csv, '.xlsx')
|
||||||
|
$entry.Details | Export-Excel -Path $xlsx -WorksheetName Table -TableName Table -AutoSize -TableStyle Medium2
|
||||||
|
$createdFiles.Add($xlsx)
|
||||||
|
} else {
|
||||||
|
$entry.Details | Export-Csv -Path $csv -NoTypeInformation
|
||||||
|
$createdFiles.Add($csv)
|
||||||
|
}
|
||||||
|
$exportedTests += $entry.TestNumber
|
||||||
|
}
|
||||||
|
if ($exportedTests.Count) {
|
||||||
|
Write-Information "Exported nested tables: $($exportedTests -join ', ')"
|
||||||
|
} elseif ($OnlyExportNestedTables) {
|
||||||
|
Write-Warning 'No nested data to export.'
|
||||||
|
}
|
||||||
|
#--- Summary export (DefaultExport only) ---
|
||||||
|
if ($PSCmdlet.ParameterSetName -eq 'DefaultExport') {
|
||||||
|
Write-Verbose 'Exporting full summary with truncated details...'
|
||||||
|
$base = "${timestamp}_${Prefix}-M365FoundationsAudit"
|
||||||
|
$out = Join-Path $ExportPath "$base.csv"
|
||||||
|
if ($ExportToExcel) {
|
||||||
|
$xlsx = [IO.Path]::ChangeExtension($out, '.xlsx')
|
||||||
|
$truncatedAudit | select-object * | Export-Excel -Path $xlsx -WorksheetName Table -TableName Table -AutoSize -TableStyle Medium2
|
||||||
|
$createdFiles.Add($xlsx)
|
||||||
|
} else {
|
||||||
|
Write-Verbose "Exporting to Path: $out"
|
||||||
|
$truncatedAudit | select-object * | Export-Csv -Path $out -NoTypeInformation
|
||||||
|
$createdFiles.Add($out)
|
||||||
|
}
|
||||||
|
Write-Information 'Exported summary of all audit results.'
|
||||||
|
}
|
||||||
|
#--- Hash & ZIP ---
|
||||||
|
Write-Verbose 'Computing file hashes...'
|
||||||
|
$hashFile = Join-Path $ExportPath "$timestamp`_${Prefix}-Hashes.txt"
|
||||||
|
$createdFiles | ForEach-Object {
|
||||||
|
$h = Get-FileHash -Path $_ -Algorithm SHA256
|
||||||
|
"$([IO.Path]::GetFileName($_)): $($h.Hash)"
|
||||||
|
} | Set-Content -Path $hashFile
|
||||||
|
$createdFiles.Add($hashFile)
|
||||||
|
Write-Verbose 'Creating ZIP archive...'
|
||||||
|
$zip = Join-Path $ExportPath "$timestamp`_${Prefix}-M365FoundationsAudit.zip"
|
||||||
|
Compress-Archive -Path $createdFiles -DestinationPath $zip -Force
|
||||||
|
$createdFiles | Remove-Item -Force
|
||||||
|
# Rename to include short hash
|
||||||
|
$zHash = Get-FileHash -Path $zip -Algorithm SHA256
|
||||||
|
$final = Join-Path $ExportPath ("$timestamp`_${Prefix}-M365FoundationsAudit_$($zHash.Hash.Substring(0,8)).zip")
|
||||||
|
Rename-Item -Path $zip -NewName (Split-Path $final -Leaf)
|
||||||
|
return [PSCustomObject]@{ ZipFilePath = $final }
|
||||||
}
|
}
|
||||||
# Output the created files at the end
|
|
||||||
#if ($createdFiles.Count -gt 0) {
|
|
||||||
########### $createdFiles
|
|
||||||
#}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user