functions/Invoke-ELExchangeLogConvert.ps1
function Invoke-ELExchangeLogConvert { <# .SYNOPSIS Convert frunction to recursively process a folder structure and convert exchange logs to CSV files .DESCRIPTION This one is a utility function inside the ExchangeLogs module. It's intendet to parse through a folder structure (staging directory, filled by Invoke-ELCentralizeLogging), find all exchange logfiles (supported by the module) and convert the files into flatten and better read-/processable CSV files. The intented workflow provided by this function: - Parse through "Source" folder structure and find logfiles - convert logfiles to csv files and put them in the "Destination" folder. The folder structrue from source directory will be preserved and rebuild in destination folder - Processed files will be move to "Archive" folder. The folder structrue from source directory will be preserved and rebuild in archive folder. Requirements: - The users needs read/write permission on the central network share The workflow will be: - First, find all logs and centralize them into a staging-folder with 'Invoke-ELCentralizeLogging' - Second, process staging-folder with 'Invoke-ELExchangeLogConvert' and build read- and processable CSV files in a reporting-folder - Use other BI-/Analytical tools to process the CSV Files in the reporting folder .PARAMETER Source Path to folder where logfiles are stored for processing This one can also called 'staging directory' .PARAMETER Destination Path to store converted CSV files from exchange service logs This one can also called 'reporting directory' Default value: A folder "Reporting" next to the folder specified in source parameter .PARAMETER Archive Path fo archiving the processed exchange service log files out of the source directory This is only for archival reason. If no folder is specified, the logs in the source folder will be deleted! Default value: A folder "Archive" next to the folder specified in source parameter .PARAMETER Filter Name filter for files to be processed in the source directory Default value: *.log .PARAMETER MaxFileCount Amount of files processed in a processing cycle per directory. The higher the number, the more memory and time is consumed for processing. Assume usally at least 24 files per service (SMTP, IMAP, POP, ...) per exchange server Default value: 200 .PARAMETER LogFile The logfile to archive processing information. Default value: -not specified- Recommended: C:\Administration\Logs\Exchange\ExchangeLogConvert.log .PARAMETER WhatIf If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. .PARAMETER Confirm If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. .EXAMPLE PS C:\> Invoke-ELExchangeLogConvert -Source "\\SRV01\Logs\Exchange\Staging" This will convert *.log files in all folders in path "\\SRV01\Logs\Exchange\Staging". The parameter "Source" can also be used with the alias "Path". Converted data will be put in CSV files in a folder "Reporting" in path "\\SRV01\Logs\Exchange". Processed files will be archived in a folder "Archive" in path "\\SRV01\Logs\Exchange". .EXAMPLE PS C:\> Invoke-ELExchangeLogConvert -Path "\\SRV01\Logs\Exchange\Staging" -Destination "\\SRV01\Logs\Exchange\Reporting" -Archive "\\SRV01\Logs\Exchange\Archive" Same behavior as in the first example, but the output path and the archival path is specified explicitly. Alias "Path" is used on the parameter "Source". .EXAMPLE PS C:\> Invoke-ELExchangeLogConvert -Source "\\$($env:USERDNSDOMAIN)\System\Logs\Exchange\Staging" -Destination "\\$($env:USERDNSDOMAIN)\System\Logs\Exchange\Reporting" -Archive "\\$($env:USERDNSDOMAIN)\system\Logs\Exchange\Archive" -MaxFileCount 1000 -LogFile "C:\Administration\Logs\Exchange\ExchangeLogConvert.log" In this case, the function will process a maxium of 1000 files (default is 200) and actions will be logged in logfile "C:\Administration\Logs\Exchange\ExchangeLogConvert.log" #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param( [Parameter(Mandatory = $true)] [Alias("Path")] [String] $Source, [String] $Destination = "$(Split-Path -Path $Source)\Reporting", [String] $Archive = "$(Split-Path -Path $Source)\Archive", [String] $Filter = "*.log", [int] $MaxFileCount = 200, [String] $LogFile = "C:\Administration\Logs\Exchange\ExchangeLogConvert.log" ) begin {} process {} end { #region Init and prerequisites if ($LogFile) { Initialize-LogFile -LogFile $LogFile -AlternateLogName $MyInvocation.MyCommand -LogInstanceName "ExchangeLogs" } Write-PSFMessage -Level Host -Message "----- Start script -----" -Tag "ExchangeLogs" # variables Write-PSFMessage -Level System -Message "Initialize variables" -Tag "ExchangeLogs" if ($Source.EndsWith("\")) { $Source = $Source.TrimEnd('\') } if ($Destination.EndsWith("\")) { $Destination = $Destination.TrimEnd('\') } if ($Archive.EndsWith("\")) { $Archive = $Archive.TrimEnd('\') } $dateStamp = Get-Date -Format "yyyy-MM-dd_HH-mm-ss" # check paths Write-PSFMessage -Level System -Message "Checking source-, destination- and archival-directory" -Tag "ExchangeLogs" # source try { $sourceDir = $Source | Get-Item -ErrorAction Stop } catch { Stop-PSFFunction -Message "Source directory not found! (Message: $($_.exception.message))" -ErrorRecord $_ -Exception $_.exception -ErrorAction Stop -Tag "ExchangeLogs" throw } # destination try { $destinationDir = $Destination | Get-Item -ErrorAction Stop } catch { Stop-PSFFunction -Message "Destination directory not found! (Message: $($_.exception.message))" -ErrorRecord $_ -Exception $_.exception -ErrorAction Stop -Tag "ExchangeLogs" throw } # archive try { $archiveDir = $Archive | Get-Item -ErrorAction Stop } catch { Stop-PSFFunction -Message "Archive directory not found! (Message: $($_.exception.message))" -ErrorRecord $_ -Exception $_.exception -ErrorAction Stop -Tag "ExchangeLogs" throw } #endregion Init and prerequisites #region recursing function function Invoke-SubDirectoryProcessing { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param ( $SourceDir, $DestinationDir, $ArchiveDir, $Filter, $LogFile, [string] $DateStamp, [int] $MaxFileCount, [String] $NamePart ) $subDirectory = $SourceDir | Get-ChildItem -Directory foreach ($directory in $subDirectory) { Write-PSFMessage -Level Verbose -Message "Working on directory '$($directory.fullname)'" -Tag "ExchangeLogs" # variables $destPath = "$($DestinationDir)\$($directory.Name)" $archivePath = "$($ArchiveDir)\$($directory.Name)" if ($NamePart) { $NamePart = "$($NamePart)-$($directory.Name)" } else { $NamePart = $directory.Name } $tempFile = "$($env:TEMP)\ELExchangeLogConvert_$([guid]::NewGuid().tostring()).csv" # folder tests Write-PSFMessage -Level System -Message "Checking directory '$($destPath)' exists" -Tag "ExchangeLogs" if (-not (Test-Path -Path $destPath)) { try { $null = New-Item -Path $destPath -ItemType Directory -Force -ErrorAction Stop } catch { Stop-PSFFunction -Message "Error creating '$($destPath)' (Message: $($_.exception.message))" -ErrorRecord $_ -Exception $_.exception -ErrorAction Stop -Tag "ExchangeLogs" throw } } Write-PSFMessage -Level System -Message "Checking directory '$($archivePath)' exists" -Tag "ExchangeLogs" if (-not (Test-Path -Path $archivePath)) { try { $null = New-Item -Path $archivePath -ItemType Directory -Force -ErrorAction Stop } catch { Stop-PSFFunction -Message "Error creating '$($archivePath)' (Message: $($_.exception.message))" -ErrorRecord $_ -Exception $_.exception -ErrorAction Stop -Tag "ExchangeLogs" throw } } # file processing $filesToProcess = $directory | Get-ChildItem -Filter $Filter -File -Force | Select-Object -First $MaxFileCount if ($filesToProcess) { Write-PSFMessage -Level Verbose -Message "$($filesToProcess.count) files to process in directory '$($directory.name)'" -Tag "ExchangeLogs" # import and convert data from logfiles Write-PSFMessage -Level System -Message "Start ExchangeLog conversion in temp file '$($tempFile)'" -Tag "ExchangeLogs" try { if ($pscmdlet.ShouldProcess("$($filesToProcess.count) files in $($directory) and save results to $($tempFile)", "Convert")) { $filesToProcess | Get-ELExchangeLog -ErrorAction Stop | Export-Csv -Path $tempFile -Delimiter ";" -Encoding UTF8 -NoTypeInformation -Force -Append -ErrorAction Stop } } catch { Stop-PSFFunction -Message "Error importing and converting data from folder '$($directory.fullname)' (Message: $($_.exception.message))" -ErrorRecord $_ -Exception $_.exception -ErrorAction Stop -Tag "ExchangeLogs" throw } Write-PSFMessage -Level System -Message "Finsh conversion into temp file '$($tempFile)'" -Tag "ExchangeLogs" # explort data to csv file $exportFilePath = "$($destPath)\$($NamePart)_$($DateStamp).csv" Write-PSFMessage -Level System -Message "Copy temp file to destination '$($exportFilePath)'" -Tag "ExchangeLogs" try { if (Test-Path -Path $exportFilePath) { # unlikely, but possible if ($pscmdlet.ShouldProcess("Content from '$($tempFile)' into existing file $($exportFilePath)", "Add")) { Get-Content -Path $tempFile -ErrorAction Stop | Out-File -FilePath $exportFilePath -Encoding UTF8 -Append -ErrorAction Stop $exportFile = Get-Item -Path $exportFilePath -ErrorAction Stop } } else { # hopefully, the usual case if ($pscmdlet.ShouldProcess("'$($tempFile)' to '$($exportFilePath)'", "Copy")) { $exportFile = Copy-Item -Path $tempFile -Destination $exportFilePath -PassThru -ErrorAction Stop } } } catch { Stop-PSFFunction -Message "Error exporting data to csv file '$($exportFilePath)' (Message: $($_.exception.message))" -ErrorRecord $_ -Exception $_.exception -ErrorAction Stop -Tag "ExchangeLogs" throw } Write-PSFMessage -Level Verbose -Message "Done with data export. Filesize: $($exportFile.Length / 1KB) KB, File: $($exportFile.FullName)" -Tag "ExchangeLogs" # temp file cleanup Write-PSFMessage -Level System -Message "Going to clean up '$($tempFile)'" -Tag "ExchangeLogs" if ($pscmdlet.ShouldProcess("$($tempFile)", "Remove")) { Remove-Item -Path $tempFile -Force -Confirm:$false -WhatIf:$false } # move processed data to archive Write-PSFMessage -Level Verbose -Message "Moving processed files to '$($archivePath)'" -Tag "ExchangeLogs" try { if ($pscmdlet.ShouldProcess("$($filesToProcess.count) files from '$($destPath)' to '$($archivePath)'", "Move")) { $filesToProcess | Move-Item -Destination $archivePath -Force -ErrorAction Stop } } catch { Stop-PSFFunction -Message "Error moving files to archive directory '$($archivePath)' (Message: $($_.exception.message))" -ErrorRecord $_ -Exception $_.exception -ErrorAction Stop -Tag "ExchangeLogs" throw } } else { Write-PSFMessage -Level System -Message "No files to process in directory '$($directory.name)'" -Tag "ExchangeLogs" } # invoke recursion down the subdirectories Write-PSFMessage -Level System -Message "Calling recursive function for next directory" -Tag "ExchangeLogs" $paramsSubDirectoryProcessing = @{ "SourceDir" = $directory "DestinationDir" = $destPath "ArchiveDir" = $archivePath "Filter" = $Filter "MaxFileCount" = $MaxFileCount "Log" = $LogFile "DateStamp" = $DateStamp "NamePart" = $NamePart } if (Test-PSFParameterBinding -ParameterName "Whatif") { $paramsSubDirectoryProcessing.Add("Whatif", $true) } if (Test-PSFParameterBinding -ParameterName "Confirm") { $paramsSubDirectoryProcessing.Add("Confirm", $true) } Invoke-SubDirectoryProcessing @paramsSubDirectoryProcessing # trim name part if ($NamePart) { $NamePart = $NamePart.TrimEnd("-$($directory.Name)") } } } #endregion recursing function #region main script Write-PSFMessage -Level Host -Message "Start parsing '$($Source)' for log files to process." -Tag "ExchangeLogs" $paramsSubDirectoryProcessing = @{ "SourceDir" = $Source "DestinationDir" = $Destination "ArchiveDir" = $Archive "Filter" = $Filter "MaxFileCount" = $MaxFileCount "Log" = $LogFile "DateStamp" = $DateStamp "NamePart" = $NamePart } if (Test-PSFParameterBinding -ParameterName "Whatif") { $paramsSubDirectoryProcessing.Add("Whatif", $true) } if (Test-PSFParameterBinding -ParameterName "Confirm") { $paramsSubDirectoryProcessing.Add("Confirm", $true) } Invoke-SubDirectoryProcessing @paramsSubDirectoryProcessing Write-PSFMessage -Level Host -Message "*** Finishing script ***" -Tag "ExchangeLogs" #endregion main script } } |