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