fix: Refactor sync function to one simple function
This commit is contained in:
@@ -1,64 +0,0 @@
|
||||
function Merge-CISExcelAndCsvData {
|
||||
[CmdletBinding(DefaultParameterSetName = 'CsvInput')]
|
||||
[OutputType([PSCustomObject[]])]
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ExcelPath,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$WorksheetName,
|
||||
|
||||
[Parameter(Mandatory = $true, ParameterSetName = 'CsvInput')]
|
||||
[string]$CsvPath,
|
||||
|
||||
[Parameter(Mandatory = $true, ParameterSetName = 'ObjectInput')]
|
||||
[CISAuditResult[]]$AuditResults
|
||||
)
|
||||
|
||||
process {
|
||||
# Import data from Excel
|
||||
$import = Import-Excel -Path $ExcelPath -WorksheetName $WorksheetName
|
||||
|
||||
# Import data from CSV or use provided object
|
||||
$csvData = if ($PSCmdlet.ParameterSetName -eq 'CsvInput') {
|
||||
Import-Csv -Path $CsvPath
|
||||
} else {
|
||||
$AuditResults
|
||||
}
|
||||
|
||||
# Extract recommendation numbers from the CSV
|
||||
$csvRecs = $csvData | Select-Object -ExpandProperty Rec
|
||||
|
||||
# Ensure headers are included in the merged data
|
||||
$headers = @()
|
||||
$firstItem = $import[0]
|
||||
foreach ($property in $firstItem.PSObject.Properties) {
|
||||
$headers += $property.Name
|
||||
}
|
||||
$headers += 'CSV_Connection', 'CSV_Status', 'CSV_Date', 'CSV_Details', 'CSV_FailureReason'
|
||||
|
||||
$mergedData = @()
|
||||
foreach ($item in $import) {
|
||||
# Check if the recommendation number exists in the CSV
|
||||
$recNum = $item.'recommendation #'
|
||||
if ($csvRecs -contains $recNum) {
|
||||
$csvRow = $csvData | Where-Object { $_.Rec -eq $recNum }
|
||||
$mergedData += New-MergedObject -ExcelItem $item -CsvRow $csvRow
|
||||
} else {
|
||||
$mergedData += $item
|
||||
}
|
||||
}
|
||||
|
||||
# Create a new PSObject array with headers included
|
||||
$result = @()
|
||||
foreach ($item in $mergedData) {
|
||||
$newItem = New-Object PSObject
|
||||
foreach ($header in $headers) {
|
||||
$newItem | Add-Member -MemberType NoteProperty -Name $header -Value $item.$header -Force
|
||||
}
|
||||
$result += $newItem
|
||||
}
|
||||
|
||||
return $result
|
||||
}
|
||||
}
|
@@ -1,30 +0,0 @@
|
||||
function New-MergedObject {
|
||||
[CmdletBinding()]
|
||||
[OutputType([PSCustomObject])]
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[psobject]$ExcelItem,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[psobject]$CsvRow
|
||||
)
|
||||
|
||||
$newObject = New-Object PSObject
|
||||
$currentDate = Get-Date -Format "yyyy-MM-ddTHH:mm:ss"
|
||||
|
||||
foreach ($property in $ExcelItem.PSObject.Properties) {
|
||||
$newObject | Add-Member -MemberType NoteProperty -Name $property.Name -Value $property.Value -Force
|
||||
}
|
||||
|
||||
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Connection' -Value $CsvRow.Connection -Force
|
||||
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Status' -Value $CsvRow.Status -Force
|
||||
if ($CsvRow.Status -ne $null) {
|
||||
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Date' -Value $currentDate -Force
|
||||
} else {
|
||||
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Date' -Value $null -Force
|
||||
}
|
||||
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Details' -Value $CsvRow.Details -Force
|
||||
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_FailureReason' -Value $CsvRow.FailureReason -Force
|
||||
|
||||
return $newObject
|
||||
}
|
@@ -1,44 +0,0 @@
|
||||
function Update-CISExcelWorksheet {
|
||||
[OutputType([void])]
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ExcelPath,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$WorksheetName,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[psobject[]]$Data,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[int]$StartingRowIndex = 2 # Default starting row index, assuming row 1 has headers
|
||||
)
|
||||
|
||||
process {
|
||||
# Load the existing Excel sheet
|
||||
$excelPackage = Open-ExcelPackage -Path $ExcelPath
|
||||
$worksheet = $excelPackage.Workbook.Worksheets[$WorksheetName]
|
||||
|
||||
if (-not $worksheet) {
|
||||
throw "Worksheet '$WorksheetName' not found in '$ExcelPath'"
|
||||
}
|
||||
|
||||
# Ensure headers are set
|
||||
$firstItem = $Data[0]
|
||||
$colIndex = 1
|
||||
foreach ($property in $firstItem.PSObject.Properties) {
|
||||
if ($worksheet.Cells[1, $colIndex].Value -eq $null -or $worksheet.Cells[1, $colIndex].Value -ne $property.Name) {
|
||||
$worksheet.Cells[1, $colIndex].Value = $property.Name
|
||||
}
|
||||
$colIndex++
|
||||
}
|
||||
|
||||
# Update the worksheet with the provided data
|
||||
$validRows = $Data | Where-Object { $_.'recommendation #' -ne $null }
|
||||
Update-WorksheetCell -Worksheet $worksheet -Data $validRows -StartingRowIndex $StartingRowIndex
|
||||
|
||||
# Save and close the Excel package
|
||||
Close-ExcelPackage $excelPackage
|
||||
}
|
||||
}
|
@@ -1,30 +0,0 @@
|
||||
function Update-WorksheetCell {
|
||||
[OutputType([void])]
|
||||
param (
|
||||
$Worksheet,
|
||||
$Data,
|
||||
$StartingRowIndex
|
||||
)
|
||||
|
||||
# Check and set headers
|
||||
$firstItem = $Data[0]
|
||||
$colIndex = 1
|
||||
foreach ($property in $firstItem.PSObject.Properties) {
|
||||
if ($StartingRowIndex -eq 2 -and $Worksheet.Cells[1, $colIndex].Value -eq $null) {
|
||||
# Add header if it's not present
|
||||
$Worksheet.Cells[1, $colIndex].Value = $property.Name
|
||||
}
|
||||
$colIndex++
|
||||
}
|
||||
|
||||
# Iterate over each row in the data and update cells
|
||||
$rowIndex = $StartingRowIndex
|
||||
foreach ($item in $Data) {
|
||||
$colIndex = 1
|
||||
foreach ($property in $item.PSObject.Properties) {
|
||||
$Worksheet.Cells[$rowIndex, $colIndex].Value = $property.Value
|
||||
$colIndex++
|
||||
}
|
||||
$rowIndex++
|
||||
}
|
||||
}
|
@@ -1,86 +1,72 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Synchronizes data between an Excel file and either a CSV file or an output object from Invoke-M365SecurityAudit, and optionally updates the Excel worksheet.
|
||||
.DESCRIPTION
|
||||
The Sync-CISExcelAndCsvData function merges data from a specified Excel file with data from either a CSV file or an output object from Invoke-M365SecurityAudit based on a common key. It can also update the Excel worksheet with the merged data. This function is particularly useful for updating Excel records with additional data from a CSV file or audit results while preserving the original formatting and structure of the Excel worksheet.
|
||||
.PARAMETER ExcelPath
|
||||
The path to the Excel file that contains the original data. This parameter is mandatory.
|
||||
.PARAMETER WorksheetName
|
||||
The name of the worksheet within the Excel file that contains the data to be synchronized. This parameter is mandatory.
|
||||
.PARAMETER CsvPath
|
||||
The path to the CSV file containing data to be merged with the Excel data. This parameter is mandatory when using the CsvInput parameter set.
|
||||
.PARAMETER AuditResults
|
||||
An array of CISAuditResult objects from Invoke-M365SecurityAudit to be merged with the Excel data. This parameter is mandatory when using the ObjectInput parameter set. It can also accept pipeline input.
|
||||
.PARAMETER SkipUpdate
|
||||
If specified, the function will return the merged data object without updating the Excel worksheet. This is useful for previewing the merged data.
|
||||
.EXAMPLE
|
||||
PS> Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -CsvPath "path\to\data.csv"
|
||||
Merges data from 'data.csv' into 'excel.xlsx' on the 'DataSheet' worksheet and updates the worksheet with the merged data.
|
||||
.EXAMPLE
|
||||
PS> $mergedData = Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -CsvPath "path\to\data.csv" -SkipUpdate
|
||||
Retrieves the merged data object for preview without updating the Excel worksheet.
|
||||
.EXAMPLE
|
||||
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://tenant-admin.url" -DomainName "example.com"
|
||||
PS> Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -AuditResults $auditResults
|
||||
Merges data from the audit results into 'excel.xlsx' on the 'DataSheet' worksheet and updates the worksheet with the merged data.
|
||||
.EXAMPLE
|
||||
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://tenant-admin.url" -DomainName "example.com"
|
||||
PS> $mergedData = Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -AuditResults $auditResults -SkipUpdate
|
||||
Retrieves the merged data object for preview without updating the Excel worksheet.
|
||||
.EXAMPLE
|
||||
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://tenant-admin.url" -DomainName "example.com" | Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet"
|
||||
Pipes the audit results into Sync-CISExcelAndCsvData to merge data into 'excel.xlsx' on the 'DataSheet' worksheet and updates the worksheet with the merged data.
|
||||
.INPUTS
|
||||
System.String, CISAuditResult[]
|
||||
You can pipe CISAuditResult objects to Sync-CISExcelAndCsvData.
|
||||
.OUTPUTS
|
||||
Object[]
|
||||
If the SkipUpdate switch is used, the function returns an array of custom objects representing the merged data.
|
||||
.NOTES
|
||||
- Ensure that the 'ImportExcel' module is installed and up to date.
|
||||
- It is recommended to backup the Excel file before running this script to prevent accidental data loss.
|
||||
- This function is part of the CIS Excel and CSV Data Management Toolkit.
|
||||
.LINK
|
||||
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Sync-CISExcelAndCsvData
|
||||
#>
|
||||
function Sync-CISExcelAndCsvData {
|
||||
[OutputType([void], [PSCustomObject[]])]
|
||||
[CmdletBinding(DefaultParameterSetName = 'CsvInput')]
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateScript({ Test-Path $_ })]
|
||||
param(
|
||||
[string]$ExcelPath,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$WorksheetName,
|
||||
|
||||
[Parameter(Mandatory = $true, ParameterSetName = 'CsvInput')]
|
||||
[ValidateScript({ Test-Path $_ })]
|
||||
[string]$CsvPath,
|
||||
|
||||
[Parameter(Mandatory = $true, ParameterSetName = 'ObjectInput', ValueFromPipeline = $true)]
|
||||
[CISAuditResult[]]$AuditResults,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[switch]$SkipUpdate
|
||||
[string]$SheetName
|
||||
)
|
||||
|
||||
process {
|
||||
$requiredModules = Get-RequiredModule -SyncFunction
|
||||
foreach ($module in $requiredModules) {
|
||||
Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModuleName $module.SubModuleName
|
||||
}
|
||||
# Import the CSV file
|
||||
$csvData = Import-Csv -Path $CsvPath
|
||||
|
||||
if ($PSCmdlet.ParameterSetName -eq 'CsvInput') {
|
||||
$mergedData = Merge-CISExcelAndCsvData -ExcelPath $ExcelPath -WorksheetName $WorksheetName -CsvPath $CsvPath
|
||||
} else {
|
||||
$mergedData = Merge-CISExcelAndCsvData -ExcelPath $ExcelPath -WorksheetName $WorksheetName -AuditResults $AuditResults
|
||||
}
|
||||
# Get the current date in the specified format
|
||||
$currentDate = Get-Date -Format "yyyy-MM-ddTHH:mm:ss"
|
||||
|
||||
if ($SkipUpdate) {
|
||||
return $mergedData
|
||||
} else {
|
||||
Update-CISExcelWorksheet -ExcelPath $ExcelPath -WorksheetName $WorksheetName -Data $mergedData
|
||||
# Load the Excel workbook
|
||||
$excelPackage = Open-ExcelPackage -Path $ExcelPath
|
||||
$worksheet = $excelPackage.Workbook.Worksheets[$SheetName]
|
||||
|
||||
# Define and check new headers, including the date header
|
||||
$lastCol = $worksheet.Dimension.End.Column
|
||||
$newHeaders = @("CSV_Connection", "CSV_Status", "CSV_Date", "CSV_Details", "CSV_FailureReason")
|
||||
$existingHeaders = $worksheet.Cells[1, 1, 1, $lastCol].Value
|
||||
|
||||
# Add new headers if they do not exist
|
||||
foreach ($header in $newHeaders) {
|
||||
if ($header -notin $existingHeaders) {
|
||||
$lastCol++
|
||||
$worksheet.Cells[1, $lastCol].Value = $header
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Save changes made to add headers
|
||||
$excelPackage.Save()
|
||||
|
||||
# Update the worksheet variable to include possible new columns
|
||||
$worksheet = $excelPackage.Workbook.Worksheets[$SheetName]
|
||||
|
||||
# Mapping the headers to their corresponding column numbers
|
||||
$headerMap = @{}
|
||||
for ($col = 1; $col -le $worksheet.Dimension.End.Column; $col++) {
|
||||
$headerMap[$worksheet.Cells[1, $col].Text] = $col
|
||||
}
|
||||
|
||||
# For each record in CSV, find the matching row and update/add data
|
||||
foreach ($row in $csvData) {
|
||||
# Find the matching recommendation # row
|
||||
$matchRow = $null
|
||||
for ($i = 2; $i -le $worksheet.Dimension.End.Row; $i++) {
|
||||
if ($worksheet.Cells[$i, $headerMap['Recommendation #']].Text -eq $row.rec) {
|
||||
$matchRow = $i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
# Update values if a matching row is found
|
||||
if ($matchRow) {
|
||||
foreach ($header in $newHeaders) {
|
||||
if ($header -eq 'CSV_Date') {
|
||||
$columnIndex = $headerMap[$header]
|
||||
$worksheet.Cells[$matchRow, $columnIndex].Value = $currentDate
|
||||
} else {
|
||||
$csvKey = $header -replace 'CSV_', ''
|
||||
$columnIndex = $headerMap[$header]
|
||||
$worksheet.Cells[$matchRow, $columnIndex].Value = $row.$csvKey
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Save the updated Excel file
|
||||
$excelPackage.Save()
|
||||
$excelPackage.Dispose()
|
||||
}
|
Reference in New Issue
Block a user