Public/Sort-EMLFiles.ps1
function Sort-EMLFiles { <# .NOTES Company: BitTitan, Inc. Title: EMLFileSorter.psm1 Author: SUPPORT@BITTITAN.COM Requirements: Version: 1.0 Date: JANUARY 05, 2017 Disclaimer: This script is provided ‘AS IS’. No warrantee is provided either expresses or implied. Copyright: Copyright© 2016 BitTitan. All rights reserved. .SYNOPSIS Sorts EML files produced by a MIMECast archive for migration by MigrationWiz to a supported source. .DESCRIPTION Sorts EML files produced by a MIMECast export to be migrated by MigrationWiz to a supported destination (e.g. Office 365). The tool will return a hashtable with properties/values reflecting importing values of the tool's operation (i.e. whether the 'cleanup', 'archive', and 'safe' switches were specified; the source directory; the destination directory; and the total number of EML files processed). .INPUTS [System.String] [System.Int32] .OUTPUTS [System.Collections.Hashtable] .EXAMPLE Simple usage of the tool. Sort-EMLFiles -source c:\users\username\desktop\Export\ -dest c:\users\username\Desktop\WorkingDirectory\ .EXAMPLE Produce zipped archives of the EML files. Sort-EMLFiles -source C:\users\username\Desktop\Export -dest c:\users\username\Desktop\WorkingDirectory -Archive .EXAMPLE Produce zipped archives and cleanup all raw-data after the fact. Sort-EMLFiles -source C:\users\username\Desktop\Export -dest c:\users\username\Desktop\WorkingDirectory -Archive -CleanUp .EXAMPLE Perform itterative disk-capacity checks on the drive of the destination directory, to ensure that the job doesn't fill up the disk. The -safe switch can be used with any of the example scenarios above. Sort-EMLFiles -source C:\users\username\Desktop\Export -dest c:\users\username\Desktop\WorkingDirectory -Archive -CleanUp #> [CmdletBinding()] Param ( [Parameter( Mandatory = $true, HelpMessage = 'Specify the local path where the EML files reside.', ParameterSetName = 'local' )] [Alias('Source')] [string]$sourceDirectory, [Parameter( Mandatory = $true, HelpMessage = 'Specify the local path where the sorted EML files will be exported', ParameterSetName = 'local' )] [Alias('dest')] [string]$destinationDirectory, [Parameter( Mandatory = $false, HelpMessage = "Specify a prefix for archive files, default is 'ExportBatch'" )] [string]$BatchPrefix = 'ExportBatch', [Parameter()] [int]$Throttle = 10000, [Parameter()] [Alias('logDest')] [string]$logDestination = "$env:USERPROFILE\AppData\", [Parameter()] [Alias('zip')] [switch]$Archive, [Parameter()] [switch]$Safe, [Parameter()] [Alias('Clean')] [switch]$CleanUp ) Begin {} Process { # get module version for logging configuration $moduleVersion = (Get-Module EMLFileSorter).version.ToString() # if -Debug switch is specified, just log to console if (($PSBoundParameters.ContainsKey('Debug'))) { Try { Set-loggingConfiguration -NoTextLogging -LogToConsole -RunType 'Debug' -ScriptName ($MyInvocation.MyCommand.Name) -ScriptVersion $moduleVersion -ErrorAction 'Stop' } Catch { throw "Unable to set global logging configuration...aborting." exit } } # elseif -Verbose switch is specified, set global logging vars for verbose mode (logs to console and file) elseif (($PSBoundParameters.ContainsKey('Verbose'))) { Try { Set-LoggingConfiguration -logFolder $logDestination -logToConsole -RunType 'Customer' -ScriptName ($MyInvocation.MyCommand.Name) -ScriptVersion $moduleVersion -ErrorAction 'Stop' Add-LogEntry -message "Current log directory is: $logDestination" } Catch { throw "Unable to set global logging configuration...aborting" exit } } # else, ONLY log to disk else { Try { Set-LoggingConfiguration -logFolder $logDestination -RunType 'Customer' -ScriptName ($MyInvocation.MyCommand.Name) -ScriptVersion $moduleVersion } Catch { throw "Unable to set global logging configuration...aborting" exit } } Write-Debug 'All parameter validation conditions were met...in Process block.' Disable-SelectMode | Out-Null # read *.eml files int o var, validate that var contains 1 or more eml files before continuing Try { $EMLFiles = Get-ChildItem -Path $sourceDirectory -Include "*.eml" -Recurse -ErrorAction Stop -ErrorVariable dirError [int]$totalEMLCount = $EMLFiles.count } Catch { log -message "Unable to read eml files from path: $sourceDirectory with error: $($dirError.errorrecord.exception)" -logLevel 'Error' break } if ($totalEMLCount -lt 1) { log -message "No .eml files found in directory $sourceDirectory. Ensure that you are referencing the correct path, with .eml files." -logLevel 'Error' break } else { log -message "Found $($EMLFiles.count.tostring()) in directory $sourceDirectory." } # set scope of destination directory to avoid namespace collision $script:destinationDirectory = $destinationDirectory # end validation stuffs # parse out root drive in destination path to do capacity checks for each eml itteration if (($safe)) { $checkDrive = ($script:destinationDirectory.split('\')[0]).tostring() Write-Debug "root dir is: $checkDrive" } # loop through all the eml files. foreach ($eml in $EMLFiles) { log -message "Reading eml file into stream" # Instantiate ADO com object, and open stream Try { $adoDBStream = New-Object -ComObject ADODB.Stream -ErrorAction Stop -ErrorVariable adoError -Verbose $adoDBStream.Open() log -message "Successfully instantiated ADODB.Stream com object" } Catch { log -message "Failed to instantiate ADODB.Stream com object, with error: $($adoError.errorrecord.exception)" -logLevel 'Error' break } Try { $ErrorActionPreference = 'Stop' $adoDBStream.LoadFromFile($eml) } Catch { log -message "Failed to to perform .LoadFromFile() method, with error: $($error[0].exception)" -logLevel 'Error' log -message "Successfully called .LoadFromFile ADODB.Stream method on current eml file" } Finally { $ErrorActionPreference = 'Continue' } Try { $CDOMessage = New-Object -ComObject CDO.Message -ErrorAction Stop -ErrorVariable cdoError -Verbose log -message "Successfully instantiated CDO.Message com object." } Catch { log -message "Failed to instantiate the CDO.Message com object, error: $($cdoError.errorrecord.exception)" -logLevel 'Error' } Try { $ErrorActionPreference = 'Stop' $CDOMessage.DataSource.OpenObject($AdoDbStream,"_Stream") log -message "Successfully read ADODB stream into CDO.Message object." } Catch { log -message "Unable to perform .DataSource.OpenObject() method, with error: $($error[0].exception)" -logLevel 'Error' } Finally { $ErrorActionPreference = 'Continue' } if ($CdoMessage.to -ne '"root"') { log -message "Parsing all email addresses in from, to and cc lines to create duplicates of current eml file." # temp array to hold raw data to be parsed $parseMe = @() # working arrays for lower forreach loop $EMLCol = @() $parseMe += $CDOMessage.to $parseMe += $CDOMessage.from $parseMe += $CDOMessage.cc # select just '<emailAddress>', the remove '<' and '>', and add to $EMLCol $parsed = Select-String -Pattern '(<.*?>)+' -InputObject $parseMe -AllMatches | ForEach-Object { $_.matches } $parsed = $parsed.Value | ForEach-Object {$_ -replace '<' -replace '>'} $parsed | ForEach-Object {$EMLCol += $_} log -message "All meta-data for current eml file parsed. Moving on to copying operartion." [bool]$Proceed = $true # Check to ensure that the root drive for the destination directory has over 1GB free space before continuing to copy operation. if (($safe)) { $diskCheck = ((Resolve-Path $checkDrive ).drive.free) log -message "Checking capacity on drive: $($checkDrive.drive.name)" # set to 1gb after testing if ($diskCheck -le 1GB) { [bool]$Proceed = $false log -message "Destination dirve: $checkDrive , has over 1GB free, continuing to copy operation." log -message "Current capacity is $(($diskCheck / 1GB).tostring()) GB" } } if (($Proceed)) { foreach ($e in $EMLCol) { # Set destination direstory to to global destination + email address $workingDestination = $script:destinationDirectory + $e # mw apparently needs parent dir capitalized $workingDestination = $workingDestination.ToUpper() log -message "Copying current eml file to path: $workingDestination" if (test-path $workingDestination) { Try { Copy-Item -Path $EML -Destination $workingDestination -ErrorAction Stop -ErrorVariable copyError | Out-Null } Catch { log -message "Failed to copy eml file to destination: $workingDestination, with error: $($copyError.errorrecord.exception)" -logLevel 'Error' } } else { Try { New-Item $workingDestination -type Directory -ErrorAction Stop -ErrorVariable dirError | Out-Null } Catch { log -message "Failed to create new directory: $workingDestination, with error: $($dirError.errorrecord.exception)" -logLevel 'Error' } Try { Copy-Item -Path $EML -Destination $workingDestination -ErrorAction Stop -ErrorVariable copyError | Out-Null } Catch { log -message "Failed to copy eml file to destination: $workingDestination, with error: $($copyError.errorrecord.exception)" } } # end inner else } } else { log -message "Less than 1GB remaining on drive: $checkDrive" -logLevel 'Error' Enable-SelectMode | Out-Null exit } } # end CDOMessage parsing block } # end foreach EMLFile block $exports = (Get-ChildItem $script:destinationDirectory -Verbose).FullName if (($Archive)) { log -message "Archive switch specified, creating archives with prefix: $BatchPrefix in path: $script:destinationDirectory" $script:exportBatchName = $BatchPrefix $script:exportBatchCount = 1 $script:emlCount = 0 foreach ($e in $exports) { # Stage data in archive folders with BatchPrefix + itteration number (number of times throttle was exceeded) if ($script:emlCount -lt $Throttle) { $workingDir = "$script:destinationDirectory$script:exportBatchName"+"$($script:exportBatchCount.tostring())"+'\' $workingDir = $workingDir.ToUpper() } else { $script:emlCount = 0 $script:exportBatchCount++ $workingDir = "$script:destinationDirectory$script:exportBatchName"+"$($script:exportBatchCount.tostring())"+'\' $workingDir = $workingDir.ToUpper() } if ((Test-Path "$workingDir" -Verbose)) { Try { Copy-Item -Path $e -Destination $workingDir -ErrorAction Stop -ErrorVariable moveError -Recurse | Out-Null $script:emlCount++ } Catch { log -message "Failed to move eml file, with error: $($moveError.errorrecord.exception)" -logLevel 'Error' } } else { Try { New-Item -ItemType Directory $workingDir -ErrorAction Stop -ErrorVariable dirError | Out-Null } Catch { log -message "Failed to create new directory: $workingDir, with error: $($dirError.errorrecord.exception)" } Try { Copy-Item $e -Destination $workingDir -ErrorAction Stop -ErrorVariable moveError -Recurse | Out-Null $script:emlCount++ } Catch { log -message "Failed to move eml file, with error: $($moveError.errorrecord.exception)" -logLevel 'Error' } } } # zip all the batch folders log -message "Compressing all folders with batch prefix: $BatchPrefix in path: $script:destinationDirectory" Get-ChildItem $script:destinationDirectory -Filter "$BatchPrefix*" | ForEach-Object { Try { Compress-Archive -Path "$($_.FullName)" -DestinationPath "$($_.FullName)" -CompressionLevel Optimal -ErrorAction Stop -ErrorVariable archiveError | Out-Null } Catch { log -message "Unable to compress archive, with error: $($archiveError.errorrecord.exception)" -logLevel 'Error' } } # Delete all but the archive folders from the destination directory if (($CleanUp)) { Try { log -message "CleanUp switch specified, deleting all files from path: $workingDestination, excluding *.zip" Get-ChildItem -Path $script:destinationDirectory -Exclude '*.zip' | Remove-Item -Recurse -ErrorAction Stop -ErrorVariable rmError | Out-Null } Catch { log -message "Unable to clean up directory: $script:destinationDirectory, with error: $($rmError.errorrecord.exception)" -logLevel 'Error' } } # end cleanUp } # end Archive $output = @{ 'CleanUp' = $CleanUp; 'Safe' = $Safe; 'Archive' = $Archive; 'Source' = $sourceDirectory; 'Destination' = $script:destinationDirectory; 'BatchPrefix' = $BatchPrefix; 'SourceFileCount' = $totalEMLCount } return $output Enable-SelectMode | Out-Null } # end process block End {} } # end Sort-EMLFiles function |