# encoding: ascii
# api: powershell
# title: Search ExTrackingLogs
# description: Search Exchange tracking logs using jobs and remoting.
# version: 0.1
# type: function
# author: evetsleep
# license: CC0
# function: Start-TransportLogSearch
# x-poshcode-id: 4626
# x-archived: 2013-11-25T06:37:30
# x-published: 2013-11-19T18:41:00
#
#
#Requires -version 3
 
function Start-TransportLogSearch
{
        <#
        .Synopsis
        Searches one or more Exchange transport logs and sends the output to XML.
       
        .DESCRIPTION
        Creates a PowerShell job for each Exchange transport server to be searched and
        that job searches based onteh parameters defined in the search.  
       
        The output is exported as XML to the path defined.  By default only 4 jobs
        run at a time.  As jobs complete additional jobs are submitted.
               
        .PARAMETER ComputerName
        One or more computer names (Exchange transort servers) to search.
               
        .PARAMETER Start
        The start time of the search
               
        .PARAMETER End
        The end time of the search
               
        .PARAMETER Sender
        Return results where the sender matches this address
               
        .PARAMETER Recipient
        Return results for e-mail addressed to the recipient provided.
               
        .PARAMETER EventId
        The eventId of the tracking event to match.
               
        .PARAMETER Threshold
        How many transport jobs to spawn at one time.
       
        .PARAMETER Path
        Where to create the resulting XML files.  The default is the current working directory.
               
        .EXAMPLE
        Start-TransportLogSearch -ComputerName server01a,server01b,server01c -Start (get-date "08/01/2013 00:00") -end (get-date 08/02/2013 "00:00") -EventId RECEIVE -Sender 'bill@microsoft.com'
       
        This starts 3 PowerShell jobs that run in parallel that searches for all mail with the
        eventId of RECEIVE that was submitted by Mr. Gates 08/01/2013 through 08/02/2013.
               
        .EXAMPLE
        $start     = (get-date).addDays(-3)
        $end       = (get-date)
        $recipient = 'john_smith@contoso.com'
        $path      = 'c:\Temp\ExchangeLogs'
        Get-TransportServer | Start-TransportLogSearch -start $start -end $end -recipient $recipient -Path $path
       
        This starts a transport log search against all transport servers in the Exchange organization for mail addressed to
        john_smith@contoso.com that has been submitted over the past 3 days.  Each job that is spawned will generate an XML
        file that will be deposited in c:\Temp\ExchangeLogs that can be processed later.
        #>
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true,
                ValueFromPipelineByPropertyName=$true,
                                ValueFromPipeline=$true,       
                Position=0)]
        [Alias('Identity')]
        [String[]]$ComputerName,
 
        [Parameter(Mandatory=$false,
                                ValueFromPipelineByPropertyName=$false,
                                Position=1)]
        [datetime]$Start = $(get-date).addHours(-1),
 
        [Parameter(Mandatory=$false,
                                ValueFromPipelineByPropertyName=$false,
                                Position=2)]
        [datetime]$End = $(Get-Date),
 
        [Parameter(Mandatory=$false,
                                ValueFromPipelineByPropertyName=$false,
                                Position=3)]
        [String]$Sender = $null,
 
        [Parameter(Mandatory=$false,
                                ValueFromPipelineByPropertyName=$false,
                                Position=4)]
        [String]$Recipient = $Null,
 
        [Parameter(Mandatory=$false,
                                ValueFromPipelineByPropertyName=$false,
                                Position=5)]
                        [ValidateSet("BADMAIL","DEFER","DELIVER","DSN","EXPAND","FAIL","PROCESS","RECEIVE","RESOLVE","SEND","TRANSFER")]
        [String]$EventId = 'RECEIVE',
 
        [Parameter(Mandatory=$false,
                                ValueFromPipelineByPropertyName=$false,
                                Position=6)]
        [int]$Threshold = 4,
 
        [Parameter(Mandatory=$false,
                                ValueFromPipelineByPropertyName=$false,
                                Position=6)]
        [String]$Path = (Get-Location).Path
    )
 
    Begin
    {
        #Code to run within the job.
        $openScriptBlock = {
            param($serverName,$start,$End,$Sender,$Recipient,$EventId)
                       
                        Function Connect-ExchangeServer
                        {
                                [CmdletBinding()]
                                [OutputType([PSObject])]
                                Param
                                (
                                #Computer name to connect to.
                                [Parameter(Mandatory=$true,
                                        ValueFromPipelineByPropertyName=$false,
                                        Position=0)]
                                        [Alias('Identity')]
                                $ComputerName,
                               
                                #Computer name to connect to.
                                [Parameter(Mandatory=$false,
                                        ValueFromPipelineByPropertyName=$false,
                                        Position=1)]
                                [String[]]$Commands    
                                )
 
                            $sessionParam = @{
                                ConfigurationName = 'Microsoft.Exchange'
                                ConnectionUri     = "http://$ComputerName/PowerShell/"
                                Authentication    = 'Kerberos'
                                ErrorAction       = 'STOP'
                                }      
 
                                Try
                                {
                                        $psSession = New-PSSession @sessionParam               
                                }
                                # Catch specific types of exceptions thrown by one of those commands
                                Catch
                                {
                                        Write-Warning -Message ("[{0}]There was an error creating the PSSession: {1}" -f $ComputerName,$_.exception.message)
                                        return
                                }      
 
 
                                $sessionImport = @{
                                        Session             = $psSession
                                        AllowClobber        = $true
                                        ErrorAction         = 'STOP'
                                        }      
                               
                                if ($Commands)
                                {
                                        $Commands += 'Set-ADServerSettings'
                                        $sessionImport.Add('CommandName',$Commands)
                                }
 
                                Try
                                {
                                        Import-Module (Import-PSSession @sessionImport) -Global -DisableNameChecking
                                }
                                # Catch specific types of exceptions thrown by one of those commands
                                Catch
                                {
                                        Write-Warning -Message ("[{0}]Could not complete import: {1}" -f $ComputerName,$_.exception.message)
                                        return
                                }
 
                        } #END Connect-ExchangeServer
 
 
            Connect-ExchangeServer -ComputerName $serverName -commands Get-MessageTrackingLog                  
                       
            #Parameters to pass to Get-MessageTrackingLog
            $splat = @{
                Server     = $serverName
                Start      = $start
                End        = $end
                EventId    = $EventId
                ResultSize = 'Unlimited'
                }
           
            #If a sender is specified, add that as a parameter.                  
            if($Sender)
            {
                $splat.Add('Sender',$Sender)
            }
 
            #Same thing as a recipient.
            if($Recipient)
            {
                $splat.Add('Recipient',$Recipient)
            }
 
            #Get the data.
            Get-MessageTrackingLog @splat
            }
 
        #Keep track of batches
        $batch = 0
 
        #BatchTime
        [string]$batchTime = get-date -Format hhmmss
    } #END BEGIN
 
    Process
    {
        #Go through each computer\identity and query the logs.
        foreach ($computer in $ComputerName)
        {
            #Check to see if there are any running jobs.  If so wait until the count of
            #running jobes is below the threshold before continuing.
            while ( $(Get-Job -State Running).count -ge $Threshold )
            {
                Write-Verbose -Message "There are $threshold jobs running.  Sleeping."
                Start-Sleep -Seconds 5
            }
 
            #Check to see if there are any completed jobs.  If so export them and clean up.
            if( $(Get-Job -State Completed) )
            {
                foreach ($job in $(Get-Job -State Completed))
                {
                    #Build the file name for the XML file that we'll generate.
                    $timeStamp = Get-Date -Format hhmmss
                    $jobName = $job.Name
                    $XMLPath = Join-Path $path "Batch-$timeStamp-$jobName.xml"
 
                    try
                    {
                        #Get the results of the job and store them as an XML file.
                        Receive-Job -Wait -AutoRemoveJob -id $job.Id -ErrorAction Stop | Export-Clixml -Path $XMLPath -Force -ErrorAction Stop
                    }
                    catch
                    {
                        Write-Warning -Message $("{0} : {1}" -f $computer,$_.exception.message)
                        continue
                    }
 
                    #Keeps memory usage optimal.
                    Write-Verbose -Message "Taking out the garbage"
                    [GC]::Collect()
                }
            }
 
            #Start a new job.
            Write-Verbose -Message $("Processing: {0}" -f $computer)
            Start-Job -Name $computer -ScriptBlock $openScriptBlock -ArgumentList $computer,$Start,$End,$Sender,$Recipient,$EventId | Out-Null
        } #END foreach computer
 
    } #END PROCESS
    End
    {
        #Wait until all running jobs are done and then continue.
        Write-Verbose "Waiting for jobs to finish"
        Get-Job | Wait-Job | out-null
 
        Write-Verbose -Message "Cleaning up remaining jobs."
        foreach ($job in $(Get-Job -State Completed))
        {
            $timeStamp = Get-Date -Format hhmmss
            $jobName = $job.Name
            $XMLPath = Join-Path $path "Batch-$timeStamp-$jobName.xml"
           
            try
            {
                Receive-Job -Wait -AutoRemoveJob -id $job.Id -ErrorAction Stop | Export-Clixml -Path $XMLPath -Force -ErrorAction Stop
            }
            catch
            {
                Write-Warning -Message $("{0} : {1}" -f $computer,$_.exception.message)
                continue
            }
            Write-Verbose -Message "Taking out the garbage"
            [GC]::Collect()
        }
        Write-Verbose -Message "XML files written to $path"
    }
} #END Start-TransportLogSearch
