From 59bb71faffcc258ea0e963ef55d0f70636fdd16d Mon Sep 17 00:00:00 2001 From: salbeck-sit Date: Wed, 4 Dec 2024 08:24:29 +0100 Subject: [PATCH 1/7] Added CSV-option to New-M365DSCReportFromConfiguration --- .../Modules/M365DSCReport.psm1 | 95 ++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 index 7258cda1bd..4dca1767d0 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 @@ -554,6 +554,95 @@ function New-M365DSCConfigurationToExcel $excel.Quit() } +<# +.Description +This function creates a new CSV file from the specified exported configuration + +.Functionality +Internal, Hidden +#> +function New-M365DSCConfigurationToCSV +{ + [CmdletBinding()] + param + ( + [Parameter()] + [Array] + $ParsedContent, + + [Parameter(Mandatory = $true)] + [System.String] + $OutputPath + ) + + $modelRow = @{'Component Name'=$null; Property=$null; Value = $null} + $row = 0 + $csvOutput = @() + + foreach ($resource in $parsedContent) + { + $beginRow = $row + foreach ($property in $resource.Keys) + { + $newRow = $modelRow.Clone() + if ($property -ne 'ResourceName' -and $property -ne 'Credential') + { + $newRow.'Component Name' = $resource.ResourceName + $newRow.Property = $property + try + { + if ([System.String]::IsNullOrEmpty($resource.$property)) + { + $newRow.Value = "`$Null" + } + else + { + if ($resource.$property.GetType().Name -eq 'Object[]') + { + $value = $resource.$property | Out-String + $newRow.Value = $value + } + else + { + $value = ($resource.$property).ToString().Replace('$', '') + $value = $value.Replace('@', '') + $value = $value.Replace('(', '') + $value = $value.Replace(')', '') + $newRow.Value = $value + } + } + + } + catch + { + New-M365DSCLogEntry -Message 'Error during conversion to CSV:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + } + + if ($property -in @('Identity', 'Name', 'IsSingleInstance', 'DisplayName')) + { + $OriginPropertyName = $csvOutput[$beginRow].Property + $OriginPropertyValue = $csvOutput[$beginRow].Value + $CurrentPropertyName = $newRow.Property + $CurrentPropertyValue = $newRow.Value + + $csvOutput[$beginRow].Property = $CurrentPropertyName + $csvOutput[$beginRow].Value = $CurrentPropertyValue + $newRow.Property = $OriginPropertyName + $newRow.Value = $OriginPropertyValue + + } + $csvOutput += [pscustomobject]$newRow + $row++ + } + } + } + $csvOutput | Out-File -FilePath $OutputPath -Encoding utf8 -Force +} + <# .Description This function creates a report from the specified exported configuration, @@ -586,7 +675,7 @@ function New-M365DSCReportFromConfiguration param ( [Parameter(Mandatory = $true)] - [ValidateSet('Excel', 'HTML', 'JSON', 'Markdown')] + [ValidateSet('Excel', 'HTML', 'JSON', 'Markdown', 'CSV')] [System.String] $Type, @@ -638,6 +727,10 @@ function New-M365DSCReportFromConfiguration $templateName = $Template.Name.Split('.')[0] New-M365DSCConfigurationToMarkdown -ParsedContent $parsedContent -OutputPath $OutputPath -TemplateName $templateName } + 'CSV' + { + New-M365DSCConfigurationToCSV -ParsedContent $parsedContent -OutputPath $OutputPath + } } } else From 90843fe3b7bb2467c0959abfd5d9cd4c97d6a893 Mon Sep 17 00:00:00 2001 From: salbeck-sit Date: Wed, 4 Dec 2024 09:22:21 +0100 Subject: [PATCH 2/7] fix CSV output-format --- Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 index 4dca1767d0..1d194367d9 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 @@ -640,7 +640,7 @@ function New-M365DSCConfigurationToCSV } } } - $csvOutput | Out-File -FilePath $OutputPath -Encoding utf8 -Force + $csvOutput | Export-Csv -Path $OutputPath -Encoding UTF8 -Delimiter ',' -NoTypeInformation } <# From 2da4f66afc2557497e576c66cdbcf486fe78dfc3 Mon Sep 17 00:00:00 2001 From: salbeck-sit Date: Wed, 4 Dec 2024 12:37:14 +0100 Subject: [PATCH 3/7] added dynamic parameter that is only used with Type CSV --- .../Modules/M365DSCReport.psm1 | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 index 1d194367d9..9ad01fa90e 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 @@ -572,7 +572,11 @@ function New-M365DSCConfigurationToCSV [Parameter(Mandatory = $true)] [System.String] - $OutputPath + $OutputPath, + + [Parameter()] + [System.String] + $Delimiter = ',' ) $modelRow = @{'Component Name'=$null; Property=$null; Value = $null} @@ -640,7 +644,7 @@ function New-M365DSCConfigurationToCSV } } } - $csvOutput | Export-Csv -Path $OutputPath -Encoding UTF8 -Delimiter ',' -NoTypeInformation + $csvOutput | Export-Csv -Path $OutputPath -Encoding UTF8 -Delimiter $Delimiter -NoTypeInformation } <# @@ -687,6 +691,31 @@ function New-M365DSCReportFromConfiguration [System.String] $OutputPath ) + DynamicParam # parameter 'Delimiter' is only available when Type = 'CSV' + { + $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() + if ($Type -eq 'CSV') + { + $delimiterAttr = [System.Management.Automation.ParameterAttribute]::New() + $delimiterAttr.Mandatory = $false + $attributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::New() + $attributeCollection.Add($delimiterAttr) + $delimiterParam = [System.Management.Automation.RuntimeDefinedParameter]::New("Delimiter", [System.String], $attributeCollection) + $delimiterParam.Value = ';' # default value, comma makes a mess when importing a CSV-file in Excel + $paramDictionary.Add("Delimiter", $delimiterParam) + } + return $paramDictionary + } + +begin +{ + if ($PSBoundParameters.ContainsKey('Delimiter')) + { + $Delimiter = $PSBoundParameters.Delimiter + } +} +process # required with DynamicParam +{ # Validate that the latest version of the module is installed. Test-M365DSCModuleValidity @@ -729,7 +758,7 @@ function New-M365DSCReportFromConfiguration } 'CSV' { - New-M365DSCConfigurationToCSV -ParsedContent $parsedContent -OutputPath $OutputPath + New-M365DSCConfigurationToCSV -ParsedContent $parsedContent -OutputPath $OutputPath -Delimiter $Delimiter } } } @@ -738,6 +767,7 @@ function New-M365DSCReportFromConfiguration Write-Warning -Message "Parsed content was null. No report was generated." } } +} <# .Description From a486c917544987a4bb0ced456097d239535d1dcb Mon Sep 17 00:00:00 2001 From: salbeck-sit Date: Wed, 4 Dec 2024 12:50:03 +0100 Subject: [PATCH 4/7] updated changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 059fe494b6..2641adeced 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,8 @@ * MISC * M365DSCDRGUtil * Add separate check for strings with ordinal comparison and standardized line breaks. + * M365DSCReport + * Add support for creating report in CSV-format # 1.24.1127.1 From 0c2f8ee3dead0cbccca6268e6aaa42112c030e95 Mon Sep 17 00:00:00 2001 From: salbeck-sit Date: Wed, 4 Dec 2024 14:59:10 +0100 Subject: [PATCH 5/7] add separator-line in CSV-file between resources --- Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 index 9ad01fa90e..aa2e7505db 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 @@ -585,6 +585,14 @@ function New-M365DSCConfigurationToCSV foreach ($resource in $parsedContent) { + $newRow = $modelRow.Clone() + if ($row -gt 0) + { + write-verbose "add separator-line in CSV-file between resources" + $newRow.'Component Name' = '======================' + $csvOutput += [pscustomobject]$newRow + $row++ + } $beginRow = $row foreach ($property in $resource.Keys) { From b576c3f36c7d4de277648be57fbe1401eeb78330 Mon Sep 17 00:00:00 2001 From: salbeck-sit Date: Thu, 5 Dec 2024 08:14:11 +0100 Subject: [PATCH 6/7] added -message to write-verbose and updated formatting --- Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 index aa2e7505db..1d2b633558 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 @@ -588,7 +588,7 @@ function New-M365DSCConfigurationToCSV $newRow = $modelRow.Clone() if ($row -gt 0) { - write-verbose "add separator-line in CSV-file between resources" + Write-Verbose -Message "add separator-line in CSV-file between resources" $newRow.'Component Name' = '======================' $csvOutput += [pscustomobject]$newRow $row++ @@ -623,7 +623,6 @@ function New-M365DSCConfigurationToCSV $newRow.Value = $value } } - } catch { @@ -645,7 +644,6 @@ function New-M365DSCConfigurationToCSV $csvOutput[$beginRow].Value = $CurrentPropertyValue $newRow.Property = $OriginPropertyName $newRow.Value = $OriginPropertyValue - } $csvOutput += [pscustomobject]$newRow $row++ From e68ba0649717b331cd4150242153ca4356f9ed94 Mon Sep 17 00:00:00 2001 From: salbeck-sit Date: Thu, 5 Dec 2024 08:19:22 +0100 Subject: [PATCH 7/7] fixed indentation --- .../Modules/M365DSCReport.psm1 | 104 +++++++++--------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 index 1d2b633558..1ecbbefb85 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 @@ -713,66 +713,66 @@ function New-M365DSCReportFromConfiguration return $paramDictionary } -begin -{ - if ($PSBoundParameters.ContainsKey('Delimiter')) + begin { - $Delimiter = $PSBoundParameters.Delimiter + if ($PSBoundParameters.ContainsKey('Delimiter')) + { + $Delimiter = $PSBoundParameters.Delimiter + } } -} -process # required with DynamicParam -{ - - # Validate that the latest version of the module is installed. - Test-M365DSCModuleValidity - - #Ensure the proper dependencies are installed in the current environment. - Confirm-M365DSCDependencies - - #region Telemetry - $data = [System.Collections.Generic.Dictionary[[String], [String]]]::new() - $data.Add('Event', 'Report') - $data.Add('Type', $Type) - Add-M365DSCTelemetryEvent -Data $data -Type 'NewReport' - #endregion - - [Array] $parsedContent = Initialize-M365DSCReporting -ConfigurationPath $ConfigurationPath - - if ($null -ne $parsedContent) + process # required with DynamicParam { - switch ($Type) + + # Validate that the latest version of the module is installed. + Test-M365DSCModuleValidity + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $data = [System.Collections.Generic.Dictionary[[String], [String]]]::new() + $data.Add('Event', 'Report') + $data.Add('Type', $Type) + Add-M365DSCTelemetryEvent -Data $data -Type 'NewReport' + #endregion + + [Array] $parsedContent = Initialize-M365DSCReporting -ConfigurationPath $ConfigurationPath + + if ($null -ne $parsedContent) { - 'Excel' - { - New-M365DSCConfigurationToExcel -ParsedContent $parsedContent -OutputPath $OutputPath - } - 'HTML' + switch ($Type) { - $template = Get-Item $ConfigurationPath - $templateName = $Template.Name.Split('.')[0] - New-M365DSCConfigurationToHTML -ParsedContent $parsedContent -OutputPath $OutputPath -TemplateName $templateName - } - 'JSON' - { - New-M365DSCConfigurationToJSON -ParsedContent $parsedContent -OutputPath $OutputPath - } - 'Markdown' - { - $template = Get-Item $ConfigurationPath - $templateName = $Template.Name.Split('.')[0] - New-M365DSCConfigurationToMarkdown -ParsedContent $parsedContent -OutputPath $OutputPath -TemplateName $templateName - } - 'CSV' - { - New-M365DSCConfigurationToCSV -ParsedContent $parsedContent -OutputPath $OutputPath -Delimiter $Delimiter + 'Excel' + { + New-M365DSCConfigurationToExcel -ParsedContent $parsedContent -OutputPath $OutputPath + } + 'HTML' + { + $template = Get-Item $ConfigurationPath + $templateName = $Template.Name.Split('.')[0] + New-M365DSCConfigurationToHTML -ParsedContent $parsedContent -OutputPath $OutputPath -TemplateName $templateName + } + 'JSON' + { + New-M365DSCConfigurationToJSON -ParsedContent $parsedContent -OutputPath $OutputPath + } + 'Markdown' + { + $template = Get-Item $ConfigurationPath + $templateName = $Template.Name.Split('.')[0] + New-M365DSCConfigurationToMarkdown -ParsedContent $parsedContent -OutputPath $OutputPath -TemplateName $templateName + } + 'CSV' + { + New-M365DSCConfigurationToCSV -ParsedContent $parsedContent -OutputPath $OutputPath -Delimiter $Delimiter + } } } + else + { + Write-Warning -Message "Parsed content was null. No report was generated." + } } - else - { - Write-Warning -Message "Parsed content was null. No report was generated." - } -} } <#