Advertisement
nottyheadedboss

Get-RDLMetadata2XLS.ps1

Jun 3rd, 2025
333
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PowerShell 24.42 KB | Source Code | 0 0
  1. <#
  2. .SYNOPSIS
  3.     Extracts metadata from RDL (Report Definition Language) files (2016/01 schema)
  4.     and exports it into multiple CSV files, which are then consolidated into a
  5.     single Excel workbook with separate sheets for each data category.
  6.  
  7. .DESCRIPTION
  8.     This script parses RDL XML files to extract various metadata, including:
  9.     - Report-level properties (Name, Description, Author, Page Size, etc.)
  10.     - Data Source definitions (Name, Connection String, Type, Security)
  11.     - Dataset definitions (Name, Data Source, Command Text, Fields and their types)
  12.     - Report Parameter definitions (Name, Data Type, Prompt, Multi-Value, Allow Blank,
  13.       Hidden, Nullable, Default Values, and Valid Values source)
  14.     - Report Section details (Body dimensions, Page Margins)
  15.  
  16.     The extracted data is first saved to individual CSV files for each category
  17.     and then combined into a single, timestamped Excel (.xlsx) file, with each
  18.     category residing on its own sheet.
  19.  
  20.     The script includes:
  21.     - Robust XML parsing using XmlNamespaceManager for RDL 2016/01 schema.
  22.     - Automatic installation of the 'ImportExcel' PowerShell module if not found.
  23.     - Comprehensive metadata extraction for the specified elements.
  24.     - Export to individual CSV files for structured output.
  25.     - Conversion of CSVs to a single Excel workbook with named sheets.
  26.     - Memory cleanup routines to dispose of large objects.
  27.  
  28. .PARAMETER Path
  29.     Specifies the root directory containing the RDL files to process.
  30.     The script will recursively search for .rdl files within this path.
  31.  
  32. .EXAMPLE
  33.     .\Get-RDLMetadata2XLS.ps1
  34.  
  35.     Prompts for RDL folder path and output folder path, then extracts and
  36.     exports metadata to Excel.
  37.  
  38. .EXAMPLE
  39.     # Directly specify paths (uncomment and modify in script)
  40.     # $rdlFolderPath = "C:\MyReports"
  41.     # $outputFolderPath = "C:\RDL_Output"
  42.     # Get-RDLMetadata2XLS -Path $rdlFolderPath -OutputDirectory $outputFolderPath
  43.  
  44. .NOTES
  45.     Author: Gemini (AI)
  46.     Date: June 3, 2025
  47.     Version: 1.0
  48.  
  49.     This script requires internet access for automatic 'ImportExcel' module installation.
  50.     Ensure you have appropriate permissions to install modules and write to specified directories.
  51. #>
  52.  
  53. # Function to extract RDL metadata
  54. function Get-RDLMetadata {
  55.     param(
  56.         [Parameter(Mandatory=$true)]
  57.         [string]$Path
  58.     )
  59.  
  60.     $rdlFiles = Get-ChildItem -Path $Path -Filter "*.rdl" -Recurse
  61.  
  62.     if (-not $rdlFiles) {
  63.         Write-Warning "No RDL files found in '$Path'."
  64.         return @{
  65.             ReportProperties = @()
  66.             DataSources      = @()
  67.             Datasets         = @()
  68.             Parameters       = @()
  69.             ReportSections   = @()
  70.         }
  71.     }
  72.  
  73.     # Initialize collections for each output type
  74.     $allReportProperties = @()
  75.     $allDataSources = @()
  76.     $allDatasets = @()
  77.     $allParameters = @()
  78.     $allReportSections = @()
  79.  
  80.     foreach ($rdlFile in $rdlFiles) {
  81.         Write-Host "Processing $($rdlFile.FullName)..."
  82.  
  83.         # Use a nested try-finally to ensure XML object is disposed
  84.         $rdlContent = $null # Initialize to null
  85.         try {
  86.             [xml]$rdlContent = Get-Content $rdlFile.FullName -Encoding UTF8
  87.  
  88.             # --- Create an XmlNamespaceManager ---
  89.             $nsManager = New-Object System.Xml.XmlNamespaceManager($rdlContent.NameTable)
  90.             $nsManager.AddNamespace("r", "http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition")
  91.             $nsManager.AddNamespace("rd", "http://schemas.microsoft.com/SQLServer/reporting/reportdesigner")
  92.             # ------------------------------------------------
  93.  
  94.             $fileName = $rdlFile.Name
  95.             $fullPath = $rdlFile.FullName
  96.  
  97.             # --- Report Properties ---
  98.             $reportNameNode = $rdlContent.SelectSingleNode("/r:Report/r:Name", $nsManager)
  99.             $reportName = if ($reportNameNode) { $reportNameNode.InnerXml } else { $fileName.Replace(".rdl", "") } # Fallback to file name without extension
  100.  
  101.             $descriptionNode = $rdlContent.SelectSingleNode("/r:Report/r:Description", $nsManager)
  102.             $description = if ($descriptionNode) { $descriptionNode.InnerXml } else { $null }
  103.  
  104.             $authorNode = $rdlContent.SelectSingleNode("/r:Report/r:Author", $nsManager)
  105.             $author = if ($authorNode) { $authorNode.InnerXml } else { $null }
  106.  
  107.             $widthNode = $rdlContent.SelectSingleNode("/r:Report/r:Width", $nsManager)
  108.             $width = if ($widthNode) { $widthNode.InnerXml } else { $null }
  109.  
  110.             $pageHeightNode = $rdlContent.SelectSingleNode("/r:Report/r:Page/r:PageHeight", $nsManager)
  111.             $height = if ($pageHeightNode) { $pageHeightNode.InnerXml } else { $null }
  112.  
  113.             $languageNode = $rdlContent.SelectSingleNode("/r:Report/r:Language", $nsManager)
  114.             $language = if ($languageNode) { $languageNode.InnerXml } else { $null }
  115.  
  116.             $consumeWhitespaceNode = $rdlContent.SelectSingleNode("/r:Report/r:ConsumeContainerWhitespace", $nsManager)
  117.             $consumeWhitespace = if ($consumeWhitespaceNode) { [bool]$consumeWhitespaceNode.InnerXml } else { $null }
  118.  
  119.             $rdlIDNode = $rdlContent.SelectSingleNode("/r:Report/rd:ReportID", $nsManager) # Designer specific ID
  120.             $rdlID = if ($rdlIDNode) { $rdlIDNode.InnerXml } else { $null }
  121.  
  122.             $reportUnitTypeNode = $rdlContent.SelectSingleNode("/r:Report/rd:ReportUnitType", $nsManager) # Designer specific Unit Type
  123.             $reportUnitType = if ($reportUnitTypeNode) { $reportUnitTypeNode.InnerXml } else { $null }
  124.  
  125.             $allReportProperties += [PSCustomObject]@{
  126.                 FileName = $fileName
  127.                 FullPath = $fullPath
  128.                 ReportName = $reportName
  129.                 Description = $description
  130.                 Author = $author
  131.                 Width = $width
  132.                 PageHeight = $height
  133.                 Language = $language
  134.                 ConsumeContainerWhitespace = $consumeWhitespace
  135.                 ReportID = $rdlID
  136.                 ReportUnitType = $reportUnitType
  137.             }
  138.  
  139.             # --- Data Sources ---
  140.             $rdlContent.SelectNodes("//r:DataSource", $nsManager) | ForEach-Object {
  141.                 $dsName = $_.Name
  142.                 $dataSourceReferenceNode = $_.SelectSingleNode("r:DataSourceReference", $nsManager)
  143.                 $connectionString = if ($dataSourceReferenceNode) { $dataSourceReferenceNode.InnerXml } else { $null }
  144.  
  145.                 if (-not $connectionString) { # If DataSourceReference is not present, check ConnectionProperties
  146.                     $connectionStringNode = $_.ConnectionProperties.SelectSingleNode("r:ConnectString", $nsManager)
  147.                     $connectionString = if ($connectionStringNode) { $connectionStringNode.InnerXml } else { $null }
  148.                 }
  149.                 $dsTypeNode = $_.ConnectionProperties.SelectSingleNode("r:DataProvider", $nsManager)
  150.                 $dsType = if ($dsTypeNode) { $dsTypeNode.InnerXml } else { $null }
  151.  
  152.                 $securityTypeNode = $_.SelectSingleNode("rd:SecurityType", $nsManager)
  153.                 $securityType = if ($securityTypeNode) { $securityTypeNode.InnerXml } else { $null }
  154.  
  155.                 $promptNode = $_.SelectSingleNode("r:Prompt", $nsManager)
  156.                 $prompt = if ($promptNode) { $promptNode.InnerXml } else { $null }
  157.  
  158.                 $allDataSources += [PSCustomObject]@{
  159.                     FileName = $fileName
  160.                     ReportName = $reportName
  161.                     DataSourceName = $dsName
  162.                     ConnectionString = $connectionString
  163.                     Type = $dsType
  164.                     SecurityType = $securityType
  165.                     Prompt = $prompt
  166.                 }
  167.             }
  168.  
  169.             # --- Datasets and their Fields ---
  170.             $rdlContent.SelectNodes("//r:DataSet", $nsManager) | ForEach-Object {
  171.                 $datasetName = $_.Name
  172.                 $dataSourceNameNode = $_.Query.SelectSingleNode("r:DataSourceName", $nsManager)
  173.                 $dataSourceName = if ($dataSourceNameNode) { $dataSourceNameNode.InnerXml } else { $null }
  174.  
  175.                 $commandTextNode = $_.Query.SelectSingleNode("r:CommandText", $nsManager)
  176.                 $commandText = if ($commandTextNode) { $commandTextNode.InnerXml } else { $null }
  177.  
  178.                 $commandTypeNode = $_.Query.SelectSingleNode("r:CommandType", $nsManager)
  179.                 $commandType = if ($commandTypeNode) { $commandTypeNode.InnerXml } else { "Text" } # Default is Text
  180.  
  181.                 $timeoutNode = $_.Query.SelectSingleNode("r:Timeout", $nsManager)
  182.                 $timeout = if ($timeoutNode) { [int]$timeoutNode.InnerXml } else { $null }
  183.  
  184.                 $caseSensitivityNode = $_.SelectSingleNode("r:CaseSensitivity", $nsManager)
  185.                 $caseSensitivity = if ($caseSensitivityNode) { [bool]$caseSensitivityNode.InnerXml } else { $null }
  186.  
  187.                 # Fields within this dataset
  188.                 $fieldsFound = $false
  189.                 $_.SelectNodes("r:Fields/r:Field", $nsManager) | ForEach-Object {
  190.                     $fieldsFound = $true
  191.                     $fieldName = $_.Name
  192.                     $dataFieldNode = $_.SelectSingleNode("r:DataField", $nsManager)
  193.                     $dataField = if ($dataFieldNode) { $dataFieldNode.InnerXml } else { $null }
  194.                     $typeNameNode = $_.SelectSingleNode("rd:TypeName", $nsManager) # rd:TypeName is often for designer info
  195.                     $typeName = if ($typeNameNode) { $typeNameNode.InnerXml } else { $null }
  196.                     $dataTypeNode = $_.SelectSingleNode("r:rd:DataType", $nsManager) # Some fields might have this
  197.                     $dataType = if ($dataTypeNode) { $dataTypeNode.InnerXml } else { $null }
  198.  
  199.                     $allDatasets += [PSCustomObject]@{
  200.                         FileName = $fileName
  201.                         ReportName = $reportName
  202.                         DataSetName = $datasetName
  203.                         DataSourceName = $dataSourceName
  204.                         CommandText = $commandText
  205.                         CommandType = $commandType
  206.                         Timeout = $timeout
  207.                         CaseSensitivity = $caseSensitivity
  208.                         FieldName = $fieldName
  209.                         DataField = $dataField
  210.                         FieldTypeName = $typeName
  211.                         FieldDataType = $dataType
  212.                     }
  213.                 }
  214.                 # If a dataset has no fields but we still want to log it (e.g., empty datasets)
  215.                 if (-not $fieldsFound) {
  216.                      $allDatasets += [PSCustomObject]@{
  217.                         FileName = $fileName
  218.                         ReportName = $reportName
  219.                         DataSetName = $datasetName
  220.                         DataSourceName = $dataSourceName
  221.                         CommandText = $commandText
  222.                         CommandType = $commandType
  223.                         Timeout = $timeout
  224.                         CaseSensitivity = $caseSensitivity
  225.                         FieldName = $null
  226.                         DataField = $null
  227.                         FieldTypeName = $null
  228.                         FieldDataType = $null
  229.                     }
  230.                 }
  231.             }
  232.  
  233.  
  234.             # --- Report Parameters ---
  235.             $rdlContent.SelectNodes("//r:ReportParameter", $nsManager) | ForEach-Object {
  236.                 $paramName = $_.Name
  237.                 $paramDataTypeNode = $_.SelectSingleNode("r:DataType", $nsManager)
  238.                 $paramDataType = if ($paramDataTypeNode) { $paramDataTypeNode.InnerXml } else { $null }
  239.  
  240.                 $paramPromptNode = $_.SelectSingleNode("r:Prompt", $nsManager)
  241.                 $paramPrompt = if ($paramPromptNode) { $paramPromptNode.InnerXml } else { $null }
  242.  
  243.                 $paramMultiValueNode = $_.SelectSingleNode("r:MultiValue", $nsManager)
  244.                 $paramMultiValue = if ($paramMultiValueNode) { [bool]$paramMultiValueNode.InnerXml } else { $false }
  245.  
  246.                 $paramAllowBlankNode = $_.SelectSingleNode("r:AllowBlank", $nsManager)
  247.                 $paramAllowBlank = if ($paramAllowBlankNode) { [bool]$paramAllowBlankNode.InnerXml } else { $false }
  248.  
  249.                 $paramHiddenNode = $_.SelectSingleNode("r:Hidden", $nsManager)
  250.                 $paramHidden = if ($paramHiddenNode) { [bool]$paramHiddenNode.InnerXml } else { $false }
  251.  
  252.                 $paramNullableNode = $_.SelectSingleNode("r:Nullable", $nsManager)
  253.                 $paramNullable = if ($paramNullableNode) { [bool]$paramNullableNode.InnerXml } else { $false }
  254.  
  255.                 # Default values (can be multiple)
  256.                 $defaultValues = @()
  257.                 $_.SelectNodes("r:DefaultValues/r:Value", $nsManager) | ForEach-Object {
  258.                     $defaultValues += $_.InnerXml
  259.                 }
  260.                 $defaultValuesText = if ($defaultValues.Count -gt 0) { $defaultValues -join ';' } else { $null }
  261.  
  262.                 # Valid values from DatasetReference or Static Values
  263.                 $paramValuesSource = $null
  264.                 $validValuesDataSetRef = $_.ValidValues.SelectSingleNode("r:DataSetReference", $nsManager)
  265.                 if ($validValuesDataSetRef) {
  266.                     $refDataSetNameNode = $validValuesDataSetRef.SelectSingleNode("r:DataSetName", $nsManager)
  267.                     $refValueFieldNode = $validValuesDataSetRef.SelectSingleNode("r:ValueField", $nsManager)
  268.                     $refLabelFieldNode = $validValuesDataSetRef.SelectSingleNode("r:LabelField", $nsManager)
  269.  
  270.                     $paramValuesSource = "DataSet: $($refDataSetNameNode.InnerXml), ValueField: $($refValueFieldNode.InnerXml), LabelField: $($refLabelFieldNode.InnerXml)"
  271.                 }
  272.  
  273.                 $staticValidValues = @()
  274.                 $_.SelectNodes("r:ValidValues/r:ParameterValues/r:ParameterValue/r:Value", $nsManager) | ForEach-Object {
  275.                     $staticValidValues += $_.InnerXml
  276.                 }
  277.                 if ($staticValidValues.Count -gt 0) {
  278.                     if ($paramValuesSource) {
  279.                         $paramValuesSource += "; Static Values: " + ($staticValidValues -join ';')
  280.                     } else {
  281.                         $paramValuesSource = "Static Values: " + ($staticValidValues -join ';')
  282.                     }
  283.                 }
  284.  
  285.                 $allParameters += [PSCustomObject]@{
  286.                     FileName = $fileName
  287.                     ReportName = $reportName
  288.                     ParameterName = $paramName
  289.                     DataType = $paramDataType
  290.                     Prompt = $paramPrompt
  291.                     MultiValue = $paramMultiValue
  292.                     AllowBlank = $paramAllowBlank
  293.                     Hidden = $paramHidden
  294.                     Nullable = $paramNullable
  295.                     DefaultValues = $defaultValuesText
  296.                     ValuesSource = $paramValuesSource
  297.                 }
  298.             }
  299.  
  300.             # --- Report Sections (simplified, focusing on containers) ---
  301.             $rdlContent.SelectNodes("/r:Report/r:ReportSections/r:ReportSection", $nsManager) | ForEach-Object {
  302.                 $sectionIndex = ($allReportSections | Where-Object { $_.FileName -eq $fileName -and $_.ReportName -eq $reportName }).Count + 1
  303.                 $sectionName = $_.Name
  304.                 if (-not $sectionName) { $sectionName = "Section_$sectionIndex" }
  305.  
  306.                 $bodyHeightNode = $_.Body.SelectSingleNode("r:Height", $nsManager)
  307.                 $bodyHeight = if ($bodyHeightNode) { $bodyHeightNode.InnerXml } else { $null }
  308.  
  309.                 $bodyWidthNode = $_.Body.SelectSingleNode("r:Width", $nsManager)
  310.                 $bodyWidth = if ($bodyWidthNode) { $bodyWidthNode.InnerXml } else { $null }
  311.  
  312.                 $pageMargins = $_.Page
  313.                 $leftMargin = if ($pageMargins) { $pageMargins.SelectSingleNode("r:LeftMargin", $nsManager).InnerXml } else { $null }
  314.                 $rightMargin = if ($pageMargins) { $pageMargins.SelectSingleNode("r:RightMargin", $nsManager).InnerXml } else { $null }
  315.                 $topMargin = if ($pageMargins) { $pageMargins.SelectSingleNode("r:TopMargin", $nsManager).InnerXml } else { $null }
  316.                 $bottomMargin = if ($pageMargins) { $pageMargins.SelectSingleNode("r:BottomMargin", $nsManager).InnerXml } else { $null }
  317.  
  318.                 $allReportSections += [PSCustomObject]@{
  319.                     FileName = $fileName
  320.                     ReportName = $reportName
  321.                     SectionName = $sectionName
  322.                     BodyHeight = $bodyHeight
  323.                     BodyWidth = $bodyWidth
  324.                     PageLeftMargin = $leftMargin
  325.                     PageRightMargin = $rightMargin
  326.                     PageTopMargin = $topMargin
  327.                     PageBottomMargin = $bottomMargin
  328.                 }
  329.             }
  330.  
  331.         }
  332.         catch {
  333.             Write-Error "Error processing $($rdlFile.FullName): $($_.Exception.Message)"
  334.         }
  335.         finally {
  336.             # Explicitly dispose of the XML document to free up memory
  337.             if ($rdlContent -is [System.IDisposable]) {
  338.                 $rdlContent.Dispose()
  339.                 $rdlContent = $null # Clear the variable
  340.             }
  341.             # Other objects are typically managed by PowerShell's garbage collection,
  342.             # but setting variables to null can help for large collections.
  343.             $nsManager = $null
  344.         }
  345.     }
  346.  
  347.     # Return a hashtable of all collected data
  348.     return @{
  349.         ReportProperties = $allReportProperties
  350.         DataSources      = $allDataSources
  351.         Datasets         = $allDatasets
  352.         Parameters       = $allParameters
  353.         ReportSections   = $allReportSections
  354.     }
  355. }
  356.  
  357. # --- Main Script Execution ---
  358.  
  359. #region Prerequisites and Setup
  360.  
  361. #region Install ImportExcel Module
  362. # Check if ImportExcel module is installed
  363. if (-not (Get-Module -ListAvailable -Name ImportExcel)) {
  364.     Write-Host "The 'ImportExcel' module is not found. Attempting to install..."
  365.     try {
  366.         Install-Module -Name ImportExcel -Scope CurrentUser -Force -AllowClobber
  367.         Write-Host "The 'ImportExcel' module was installed successfully."
  368.     }
  369.     catch {
  370.         Write-Error "Failed to install the 'ImportExcel' module. Please install it manually by running:"
  371.         Write-Error "  Install-Module -Name ImportExcel -Scope CurrentUser -Force -AllowClobber"
  372.         Write-Error "Error details: $($_.Exception.Message)"
  373.         exit 1 # Exit script if module cannot be installed
  374.     }
  375. }
  376. # Import the module (even if already installed, this ensures it's loaded for the script's scope)
  377. Import-Module ImportExcel -Force -Global:$true
  378. #endregion
  379.  
  380. # Prompt for RDL folder path
  381. $rdlFolderPath = Read-Host "Enter the path to your RDL files (e.g., C:\MyReports)"
  382. while (-not (Test-Path $rdlFolderPath -PathType Container)) {
  383.     Write-Warning "The specified RDL folder path does not exist or is not a directory. Please try again."
  384.     $rdlFolderPath = Read-Host "Enter the path to your RDL files (e.g., C:\MyReports)"
  385. }
  386.  
  387. # Prompt for output folder path
  388. $outputFolderPath = Read-Host "Enter the desired output folder path for CSVs and Excel (e.g., C:\RDL_Output)"
  389. if (-not (Test-Path $outputFolderPath -PathType Container)) {
  390.     try {
  391.         New-Item -ItemType Directory -Path $outputFolderPath -Force | Out-Null
  392.         Write-Host "Created output directory: $outputFolderPath"
  393.     }
  394.     catch {
  395.         Write-Error "Failed to create output directory: $($_.Exception.Message). Please ensure you have write permissions."
  396.         exit 1
  397.     }
  398. }
  399.  
  400. Write-Host "Starting RDL metadata extraction from: $rdlFolderPath"
  401.  
  402. #endregion
  403.  
  404. #region Extraction and CSV Export
  405. $allExtractedData = Get-RDLMetadata -Path $rdlFolderPath
  406.  
  407. $csvFilePaths = @() # To store paths of generated CSVs for later Excel conversion
  408.  
  409. # Export Report Properties
  410. if ($allExtractedData.ReportProperties.Count -gt 0) {
  411.     $reportPropertiesCsvPath = Join-Path $outputFolderPath "RDL_ReportProperties.csv"
  412.     $allExtractedData.ReportProperties | Export-Csv -Path $reportPropertiesCsvPath -NoTypeInformation -Encoding UTF8
  413.     $csvFilePaths += $reportPropertiesCsvPath
  414.     Write-Host "Exported Report Properties to: $reportPropertiesCsvPath"
  415. } else {
  416.     Write-Warning "No Report Properties extracted."
  417. }
  418.  
  419. # Export Data Sources
  420. if ($allExtractedData.DataSources.Count -gt 0) {
  421.     $dataSourcesCsvPath = Join-Path $outputFolderPath "RDL_DataSources.csv"
  422.     $allExtractedData.DataSources | Export-Csv -Path $dataSourcesCsvPath -NoTypeInformation -Encoding UTF8
  423.     $csvFilePaths += $dataSourcesCsvPath
  424.     Write-Host "Exported Data Sources to: $dataSourcesCsvPath"
  425. } else {
  426.     Write-Warning "No Data Sources extracted."
  427. }
  428.  
  429. # Export Datasets and Fields
  430. if ($allExtractedData.Datasets.Count -gt 0) {
  431.     $datasetsCsvPath = Join-Path $outputFolderPath "RDL_Datasets.csv"
  432.     $allExtractedData.Datasets | Export-Csv -Path $datasetsCsvPath -NoTypeInformation -Encoding UTF8
  433.     $csvFilePaths += $datasetsCsvPath
  434.     Write-Host "Exported Datasets and Fields to: $datasetsCsvPath"
  435. } else {
  436.     Write-Warning "No Datasets or Fields extracted."
  437. }
  438.  
  439. # Export Parameters
  440. if ($allExtractedData.Parameters.Count -gt 0) {
  441.     $parametersCsvPath = Join-Path $outputFolderPath "RDL_Parameters.csv"
  442.     $allExtractedData.Parameters | Export-Csv -Path $parametersCsvPath -NoTypeInformation -Encoding UTF8
  443.     $csvFilePaths += $parametersCsvPath
  444.     Write-Host "Exported Parameters to: $parametersCsvPath"
  445. } else {
  446.     Write-Warning "No Parameters extracted."
  447. }
  448.  
  449. # Export Report Sections
  450. if ($allExtractedData.ReportSections.Count -gt 0) {
  451.     $reportSectionsCsvPath = Join-Path $outputFolderPath "RDL_ReportSections.csv"
  452.     $allExtractedData.ReportSections | Export-Csv -Path $reportSectionsCsvPath -NoTypeInformation -Encoding UTF8
  453.     $csvFilePaths += $reportSectionsCsvPath
  454.     Write-Host "Exported Report Sections to: $reportSectionsCsvPath"
  455. } else {
  456.     Write-Warning "No Report Sections extracted."
  457. }
  458. #endregion
  459.  
  460. #region CSV to Excel Conversion
  461. if ($csvFilePaths.Count -gt 0) {
  462.     Write-Host "`nConverting CSV files to Excel..."
  463.     $excelFileName = "RDL_Metadata_$(Get-Date -Format 'yyyyMMdd_HHmmss').xlsx"
  464.     $excelFilePath = Join-Path $outputFolderPath $excelFileName
  465.  
  466.     $excelPackage = $null # Initialize for disposal
  467.  
  468.     try {
  469.         # Create a new Excel file
  470.         $excelPackage = New-ExcelPackage -Path $excelFilePath
  471.  
  472.         foreach ($csvPath in $csvFilePaths) {
  473.             $sheetName = [System.IO.Path]::GetFileNameWithoutExtension($csvPath).Replace("RDL_", "") # Get sheet name from CSV filename
  474.             Write-Host "  Adding '$sheetName' data to Excel sheet..."
  475.             $data = Import-Csv -Path $csvPath -Encoding UTF8
  476.             $data | Export-Excel -ExcelPackage $excelPackage -WorksheetName $sheetName -AutoFilter -AutoSize -ClearSheet -PassThru | Out-Null
  477.             $data = $null # Clear data after export
  478.         }
  479.  
  480.         Close-ExcelPackage -ExcelPackage $excelPackage -Show:$false # Save and close the Excel file without opening it
  481.         Write-Host "Successfully converted CSVs to Excel: $excelFilePath"
  482.  
  483.         # Optional: Remove CSV files after conversion
  484.         # Write-Host "Removing temporary CSV files..."
  485.         # $csvFilePaths | Remove-Item -ErrorAction SilentlyContinue -Force
  486.         # Write-Host "Temporary CSV files removed."
  487.  
  488.     }
  489.     catch {
  490.         Write-Error "An error occurred during CSV to Excel conversion: $($_.Exception.Message)"
  491.     }
  492.     finally {
  493.         # Dispose of the Excel package object
  494.         if ($excelPackage -is [System.IDisposable]) {
  495.             $excelPackage.Dispose()
  496.             $excelPackage = $null
  497.         }
  498.     }
  499. } else {
  500.     Write-Warning "No CSV files were generated, so Excel conversion is skipped."
  501. }
  502. #endregion
  503.  
  504. #region Memory Cleanup
  505. Write-Host "`nInitiating memory cleanup..."
  506.  
  507. # Clear variables holding large data collections and file lists
  508. $allExtractedData = $null
  509. $allReportProperties = $null
  510. $allDataSources = $null
  511. $allDatasets = $null
  512. $allParameters = $null
  513. $allReportSections = $null
  514. $rdlFiles = $null
  515. $csvFilePaths = $null
  516. $rdlFolderPath = $null
  517. $outputFolderPath = $null
  518.  
  519. # Force garbage collection (use with caution, as it can be resource-intensive, but useful for large scripts)
  520. # [System.GC]::Collect()
  521. # [System.GC]::WaitForPendingFinalizers()
  522.  
  523. Write-Host "Memory cleanup routines completed."
  524. #endregion
  525.  
  526. Write-Host "`nRDL metadata extraction and export process finished."
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement