internal/functions/Expand-LogRecordPopImap.ps1

function global:Expand-LogRecordPopImap {
    <#
    .SYNOPSIS
        Expand the data from records group into a flat data record
 
    .DESCRIPTION
        Expand the data from records group into a flat data record
 
    .PARAMETER InputObject
        The dataset to expand
 
    .PARAMETER SessionIdName
        The name of the session grouping attribute
 
    .PARAMETER ShowProgress
        If specified, progress information on record processing is showed
 
    .PARAMETER IncludeActivityContextData
        Include full exchange internals in POP3 log expansion process.
        There is a lot of internal system information in the logfiles, with unneccessary content for statistical output.
        Suppressing this data is reducing the log footage.
 
    .EXAMPLE
        PS C:\> Expand-LogRecordPop -InputObject $DataSet
 
        Expand the data from records group into a flat data record
#>

    [CmdletBinding()]
    param (
        $InputObject,

        [string]
        $SessionIdName = "sessionId",

        [switch]
        $ShowProgress,

        [switch]
        $IncludeActivityContextData
    )

    begin {
        $Error.Clear()
    }

    process {
        if ($ShowProgress) {
            $i = 0
            if ($InputObject.count -lt 100) { $refreshInterval = 1 } else { $refreshInterval = [math]::Round($InputObject.count / 100) }
        }

        foreach ($record in $InputObject) {
            # assure qualified session in log
            $_startIndicator = $record.Group[0] | Where-Object command -like "OpenSession"
            if (-not $_startIndicator) {
                Write-PSFMessage -Level Warning -Message "Detect fragmented record! Missing previous logfile with partital records. Skip processing $($SessionIdName) '$($record.$SessionIdName)' in $($record.LogFolder)\$($record.LogFileName)"
                continue
            }

            # data text record in array, avoids parsing full data array
            $commands = $record.Group.command
            $context = $record.Group.context #| Select-Object -Unique

            # full text log
            $logLines = New-Object -TypeName "System.Collections.ArrayList"
            foreach ($item in $record.Group) {
                $logline = $item.seqNumber + " " + $item.command
                if ($item.parameters.Length -gt 0) {
                    $logline = $logline + " | parameters: " + $item.parameters
                }
                if ($item.context.Length -gt 0) {
                    if (-not $IncludeActivityContextData) {
                        if ($item.context -like "*;ActivityContextData=*") {
                            $logline = $logline + ([string]::Join(";", ($item.context.Split(";") -notlike "*ActivityContextData=*")))
                        } else {
                            $logline = $logline + " | context: " + $item.context
                        }
                    } else {
                        $logline = $logline + " | context: " + $item.context
                    }
                }
                $null = $logLines.add($logLine)
            }
            if ($logLines) {
                $logText = [string]::Join("`n", ($logLines | ForEach-Object { $_ }) )
            } else {
                $logtext = ""
            }

            # LocalEndpoint
            [string]$localEndpoint = $record.Group[-1].sIP

            # RemoteEndpoint
            [string]$remoteEndpoint = $record.Group[-1].cIp


            # Errors
            #Log-type - IMAP4 Log
            $errMsgs = $context | Where-Object { $_ -like 'R="*' -or $_ -like 'ErrMsg=*' }
            if ($errMsgs) {
                $hasError = $true
                $errorMessage = foreach ($errMsg in $errMsgs) {
                    if($errMsg.StartsWith("ErrMsg=")) {
                        $errMsg -replace "ErrMsg="
                    } elseif($errMsg.StartsWith("R=")) {
                        $_msg = $errMsg.split(";") | Where-Object { $_ -like "R=*" }
                        if($_msg) {
                            (([string]($errMsg.split(";")[0] -replace "R=").Trim('"')) -replace "z NO ") -replace "-ERR "
                        } else {
                            $errMsg
                        }
                    } else {
                        $errMsg
                    }
                }
                if ($errorMessage.count -gt 1) {
                    $errorMessage = [string]::Join(" | ", $errorMessage)
                }
            } else {
                $hasError = $false
                $errorMessage = ""
            }

            # IsProxysession
            $proxycontext = $context -like "*Proxy:*"
            if ("proxy" -in $commands) {
                $isProxysession = $true
                $proxyServer = $record.Group | Where-Object { $_.command -like "proxy" } | Select-Object -ExpandProperty parameters
                $proxyStatus = ""
            } elseif ($proxycontext) {
                $isProxysession = $true
                $proxycontext = ((($proxycontext -split "Msg=") | Where-Object { $_ -like "*Proxy:*" }) -split '"' | Where-Object { $_ -like "*Proxy:*" }) -replace "Proxy:"
                $proxyServer = ($proxycontext -split ";")[0]
                $proxyStatus = ($proxycontext -split ";")[1]
            } else {
                $isProxysession = $false
                $proxyServer = ""
                $proxyStatus = ""
            }

            # Authentication
            if ("auth" -in $commands -or "user" -in $commands -or "authenticate" -in $commands -or "login" -in $commands) { $authenticationEnabled = $true } else { $authenticationEnabled = $false }
            if ($authenticationEnabled) {
                $userName = $record.Group | Where-Object { $_.command -like "auth" -or $_.command -like "user" -or $_.command -like "authenticate" -or $_.command -like "login" } | Select-Object -ExpandProperty User -Unique
                if ($userName.count -gt 1) {
                    $userName = [string]::Join(", ", $userName)
                }
            } else {
                $userName = ""
            }

            if ($authenticationEnabled -and ("auth" -in $commands -or "pass" -in $commands -or "authenticate" -in $commands -or "login" -in $commands)) {
                $authDetails = ((($record.Group | Where-Object { $_.command -like "auth" -or $_.command -like "pass" -or $_.command -like "authenticate" -or $_.command -like "login"}).context -split ";")[1] -replace 'Msg="').TrimEnd('"') -Split ", "
                $authHash = @{
                    "RecipientType"                = ($authDetails | Where-Object { $_ -like "RecipientType: *" }) -replace "RecipientType: "
                    "RecipientTypeDetails"         = ($authDetails | Where-Object { $_ -like "RecipientTypeDetails: *" }) -replace "RecipientTypeDetails: "
                    "DisplayName"                  = ($authDetails | Where-Object { $_ -like "Selected Mailbox: Display Name: *" }) -replace "Selected Mailbox: Display Name: "
                    "MailboxGuid"                  = ($authDetails | Where-Object { $_ -like "Mailbox Guid: *" }) -replace "Mailbox Guid: "
                    "DatabaseGuid"                 = ($authDetails | Where-Object { $_ -like "Database: *" }) -replace "Database: "
                    "ServerFqdn"                   = ($authDetails | Where-Object { $_ -like "Location: ServerFqdn: *" }) -replace "Location: ServerFqdn: "
                    "ServerVersion"                = ($authDetails | Where-Object { $_ -like "ServerVersion: *" }) -replace "ServerVersion: "
                    "DatabaseName"                 = ($authDetails | Where-Object { $_ -like "DatabaseName: *" }) -replace "DatabaseName: "
                    "HomePublicFolderDatabaseGuid" = ($authDetails | Where-Object { $_ -like "HomePublicFolderDatabaseGuid: *" }) -replace "HomePublicFolderDatabaseGuid: "
                }
            } else {
                $authDetails = ""
                $authHash = @{}
            }

            # Statistics
            $stats = (($record.Group | Where-Object { $_.command -like "stat" }).Context -split ";") | Where-Object { $_ -like "Rows=*" -or $_ -like "TotalSize=*" }
            if ($stats) {
                $rows = ($stats -like "Rows=*") -replace "Rows="
                if ($rows) { $rows = [int]::Parse($rows) } else { [int]$rows = 0 }
                $totalSize = ($stats -like "TotalSize=*") -replace "TotalSize="
                if ($totalSize) { $totalSize = $totalSize = [int]::Parse($totalSize) } else { [int]$totalSize = 0 }
            } else {
                $rows = 0
                $totalSize = 0
            }

            $budgetinfo = ((([array]$context -like "*Budget=*") | Select-Object -Last 1) -split 'Budget="')[-1]
            if ($budgetinfo) {
                $budgetDetails = $budgetinfo.TrimEnd('"') -Split ","
                $budgetHash = @{
                    "OwnerSID"         = (($budgetDetails | Where-Object { $_ -like "Owner:Sid~*" }) -split "~")[1]
                    "Conn"             = [int](($budgetDetails | Where-Object { $_ -like "Conn:*" }) -replace "Conn:")
                    "MaxConn"          = ($budgetDetails | Where-Object { $_ -like "MaxConn:*" }) -replace "MaxConn:"
                    "MaxBurst"         = ($budgetDetails | Where-Object { $_ -like "MaxBurst:*" }) -replace "MaxBurst:"
                    "Balance"          = ($budgetDetails | Where-Object { $_ -like "Balance:*" }) -replace "Balance:"
                    "Cutoff"           = ($budgetDetails | Where-Object { $_ -like "Cutoff:*" }) -replace "Cutoff:"
                    "RechargeRate"     = ($budgetDetails | Where-Object { $_ -like "RechargeRate:*" }) -replace "RechargeRate:"
                    "Policy"           = ($budgetDetails | Where-Object { $_ -like "Policy:*" }) -replace "Policy:"
                    "IsServiceAccount" = [bool]::Parse( (($budgetDetails | Where-Object { $_ -like "IsServiceAccount:*" }) -replace "IsServiceAccount:") )
                    "LiveTime"         = [timespan]::Parse( (($budgetDetails | Where-Object { $_ -like "LiveTime:*" }) -replace "LiveTime:") )
                }
            } else {
                $budgetDetails = ""
                $budgetHash = @{}
            }

            $stls = ($record.group.command | Where-Object { $_ -like "stls" -or $_ -like "starttls"}).count
            $auth = ($record.group.command | Where-Object { $_ -like "auth" -or $_ -like "authenticate"}).count
            $user = ($record.group.command | Where-Object { $_ -like "user" -or $_ -like "login"}).count
            $pass = ($record.group.command | Where-Object { $_ -like "pass" }).count
            $stat = ($record.group.command | Where-Object { $_ -like "stat*"}).count
            $uidl = ($record.group.command | Where-Object { $_ -like "uidl" }).count
            $list = ($record.group.command | Where-Object { $_ -like "list" }).count
            $retr = ($record.group.command | Where-Object { $_ -like "retr" }).count
            $dele = ($record.group.command | Where-Object { $_ -like "dele" }).count

            # construct output object
            $outputRecord = [PSCustomObject]@{
                "PSTypeName"                   = "ExchangeLog.$($record.metadataHash['Log-type'].Replace(' ','')).Record"
                "LogFolder"                    = $record.LogFolder
                "LogFileName"                  = $record.LogFileName
                $SessionIdName                 = $record.$SessionIdName
                "DateStart"                    = ($record.Group | Sort-Object 'dateTime')[0].'dateTime' -as [datetime]
                "DateEnd"                      = ($record.Group | Sort-Object 'dateTime')[-1].'dateTime' -as [datetime]
                "SequenceCount"                = $record.Group.count
                "LocalIP"                      = $localEndpoint -replace ":$([string]$localEndpoint.split(":")[-1])", ""
                "LocalPort"                    = $localEndpoint.split(":")[-1]
                "RemoteIP"                     = $remoteEndpoint -replace ":$([string]$remoteEndpoint.split(":")[-1])", ""
                "RemotePort"                   = $remoteEndpoint.split(":")[-1]
                "AuthenticationEnabled"        = $authenticationEnabled
                "User"                         = $userName
                "RecipientType"                = (. { if ($authDetails) { $authHash["RecipientType"               ] } })
                "RecipientTypeDetails"         = (. { if ($authDetails) { $authHash["RecipientTypeDetails"        ] } })
                "DisplayName"                  = (. { if ($authDetails) { $authHash["DisplayName"                 ] } })
                "MailboxGuid"                  = (. { if ($authDetails) { $authHash["MailboxGuid"                 ] } })
                "DatabaseGuid"                 = (. { if ($authDetails) { $authHash["DatabaseGuid"                ] } })
                "ServerFqdn"                   = (. { if ($authDetails) { $authHash["ServerFqdn"                  ] } })
                "ServerVersion"                = (. { if ($authDetails) { $authHash["ServerVersion"               ] } })
                "DatabaseName"                 = (. { if ($authDetails) { $authHash["DatabaseName"                ] } })
                "HomePublicFolderDatabaseGuid" = (. { if ($authDetails) { $authHash["HomePublicFolderDatabaseGuid"] } })
                "OwnerSID"                     = (. { if ($budgetDetails) { $budgetHash["OwnerSID"] } })
                "ConnectionCount"              = (. { if ($budgetDetails) { $budgetHash["Conn"] } })
                "ConnectionMax"                = (. { if ($budgetDetails) { $budgetHash["MaxConn"] } })
                "MaxBurst"                     = (. { if ($budgetDetails) { $budgetHash["MaxBurst"] } })
                "Balance"                      = (. { if ($budgetDetails) { $budgetHash["Balance"] } })
                "Cutoff"                       = (. { if ($budgetDetails) { $budgetHash["Cutoff"] } })
                "RechargeRate"                 = (. { if ($budgetDetails) { $budgetHash["RechargeRate"] } })
                "Policy"                       = (. { if ($budgetDetails) { $budgetHash["Policy"] } })
                "IsServiceAccount"             = (. { if ($budgetDetails) { $budgetHash["IsServiceAccount"] } })
                "LiveTime"                     = (. { if ($budgetDetails) { $budgetHash["LiveTime"] } })
                "TotalObjects"                 = $rows
                "TotalSize"                    = $totalSize
                "HasError"                     = $hasError
                "ErrorMessage"                 = $errorMessage
                "IsProxysession"               = $isProxysession
                "ProxyServer"                  = $proxyServer
                "ProxyStatus"                  = $proxyStatus
                "CmdCountStls"                 = $stls
                "CmdCountAuth"                 = $auth
                "CmdCountUser"                 = $user
                "CmdCountPass"                 = $pass
                "CmdCountStat"                 = $stat
                "CmdCountUidl"                 = $uidl
                "CmdCountList"                 = $list
                "CmdCountRetr"                 = $retr
                "CmdCountDele"                 = $dele
                "LogText"                      = $logText
            }

            # add metadata attributes
            foreach ($key in $record.metadataHash.Keys) {
                if ($key -like "Date") {
                    $value = $record.metadataHash[$key] -as [datetime]
                } else {
                    $value = $record.metadataHash[$key]
                }
                $outputRecord | Add-Member -MemberType NoteProperty -Name $key -Value $value -Force
            }

            # output data
            $outputRecord

            # report in detail if errors occur (for debugging because the processing in operating in runspaces)
            if ($Error) {
                Write-Warning "Error detected while processing $($outputRecord.LogFolder)\$($outputRecord.LogFileName) with $($record.$SessionIdName)"
                $Error.Clear()
            }

            # output progress of switch is set (only debugging purpose)
            if ($ShowProgress) {
                if (($i % $refreshInterval) -eq 0) {
                    Write-Progress -Activity "Process logfile record " -Status "$($record.LogFileName) - $($SessionIdName): $($record.$SessionIdName) ($($i) / $($InputObject.count))" -PercentComplete ($i / $InputObject.count * 100)
                }
                $i = $i + 1
            }
        }
    }

    end {
    }
}

(Get-Command Expand-LogRecordPopImap).Visibility = "Private"