Source/Public/Get-3LLogonEventAccountName.ps1

Function Get-3LLogonEventAccountName {

    #requires -RunAsAdministrator

    #requires -Modules PSScriptTools

    <#
 
    .SYNOPSIS
        Get all logon events (Success and Failure) and extract some logon details
 
    .SYNOPSIS
        Get all authenticate events that are used to authenticate.
        Logon attempts are retrieved from events in the security eventlog with eventid 4624 and 4625.
        Each computer is queried as a separate job. The output of all jobs is merged in the end.
 
    .EXAMPLE
        Get-3LLogonEventAccountName -InformationAction Continue
         
        Get all logon events on the local machine and displays status messages.
 
    .EXAMPLE
        Get-3LLogonEventAccountName -afterDate (Get-Date).addDays(-14)
 
        Get all logon events on the local machine in the last 14 days
 
    .EXAMPLE
        Get-3LLogonEventAccountName -afterDate (Get-Date).addHours(-1)
 
        Get all logon events on the local machine in the hour
 
    .EXAMPLE
        Get-3LLogonEventAccountName -computerName SVR1, SVR2 -Progress
 
        Get all logon events on the specified computers.
        During analyzing the Security log Events, a progressbar is displayed.
         
 
    .EXAMPLE
        Get-3LLogonEventAccountName -InformationVariable info
        $info | Where Tags -eq 'Success'
 
        The first command collect all logon events on the local computer.
        Messages from the Information stream are collected in the info variable
        The second command shows all Information messages tagged as 'Success'
 
 
    .NOTES
 
        Author: Frits van Drie (3-Link.nl)
        Date : 2021-10-20
     
    #>


    [CmdletBinding()]

    param(

        [string[]]$computerName,
        [datetime]$afterDate = (Get-Date).AddDays(-1),
        [switch]$progress

    )

    Begin {

        Function Write--JobProgress {

            <#
                .NOTES
                    Date: 2021-10-19
            #>


            param($job)

            # Make sure the first child job exists
            if($job.ChildJobs[0].Progress -ne $null) {

                # Extract the latest progress of the job and write the progress
                $jobProgressHistory    = $job.ChildJobs[0].Progress
                $latestProgress        = $jobProgressHistory[$jobProgressHistory.Count - 1]
                $latestPercentComplete = $latestProgress | Select -expand PercentComplete
                $latestActivity        = $latestProgress | Select -expand Activity
                $latestStatus          = $latestProgress | Select -expand StatusDescription

                # a unique ID per ProgressBar

                if ( $latestProgress.PercentComplete -ge 0 ) {

                    Write-Progress -Id $job.Id -Activity $latestActivity -Status $latestStatus -PercentComplete $latestPercentComplete

                }
            }
        }

        # module PSScriptTools required
        try { 
            $module = 'PSScriptTools'
            Import-Module $module -ErrorAction Stop
            Write-Information "SUCCESS: Imported Module: $module"

            $function_ConvertEventLogRecord = "Function Convert-EventLogRecord {" + (Get-Item function:Convert-EventLogRecord).ScriptBlock + " }"

        }
        catch {
            Write-Information "FAILURE: Could not import required Module: $module"
            Write-Error $_
            Break
        }

        if ( ($PSBoundParameters.Keys -notcontains 'computerName') -or ($computerName -eq 'localhost') ) {
            $computerName = $env:COMPUTERNAME
        }

        $msec = ((Get-Date)- $afterDate).TotalMilliseconds

        $xml  = @"
        <QueryList>
          <Query Id="0" Path="Security">
            <Select Path="Security">*[System[(Level=2 or Level=4 or Level=0) and (EventID=4624 or EventID=4625) and (TimeCreated[timediff(@SystemTime) `&lt;= $msec])]]</Select>
          </Query>
        </QueryList>
"@
 # xmlFilter

        [array]$startedJobs = @()


    } # end Begin{}

    Process {

        foreach ( $computer in $computerName ) {

            $jobName = "LogonEvents-$computer"
            Write-Information "STATUS : Running Job $jobName" -Tags 'Status'
            $job = Start-Job -Name $jobName -ArgumentList $xml, $computer, $progress, $function_ConvertEventLogRecord {

                param($xml, $computer, $progress, $function_ConvertEventLogRecord)

                Write-Information "STATUS : Setting up session to computer $computer" -Tags 'Status'

                try {
                    New-PSSession -ComputerName $computer -ErrorAction Stop
                    Write-Information "SUCCESS: Connected to computer $computer" -Tags 'Success'
                } # try New-PSSession
                catch {
                    Write-Information "FAILURE: Session to computer $computer failed" -Tags 'Failure'
                    continue

                }

                if ( $session = Get-PSSession -ComputerName $computer -ErrorAction SilentlyContinue) {
                    Write-Information "STATUS : Starting Invoke-Command to computer $computer" -Tags 'Status'
                    $outputInvoke = Invoke-Command -Session $session -ArgumentList $xml, $progress, $activity, $function_ConvertEventLogRecord {

                        param($xml, $progress, $activity, $function_ConvertEventLogRecord)

                        iex $function_ConvertEventLogRecord  # load function: Convert-EventLogRecord

                        $computer = $env:COMPUTERNAME

                        $splatParam = @{}
                        $splatParam.add('FilterXml', $xml)
                        $splatParam.add('ComputerName', $computer)

                        try {

                            Write-Information "STATUS : Reading events from computer $computer" -Tags 'Status'
                            $events = Get-WinEvent @splatParam -ErrorAction Stop

                            $total  = $events.Count

                        }
                        catch {
                            Write-Information "INFO : $($_.tostring())"
                            Write-Error $_
                            return
                        }


                        $activity = "Analyzing $total Events from computer $computer"
                        Write-Information "STATUS : $activity" -Tags 'Status'

                        $done = 0

                        $returnInvoke  = @()

                        foreach ($event in $events) {

                            $obj = Convert-EventLogRecord $event

                            $returnInvoke += $obj

                            if ( $progress ) {
                                $done++
                                Write-Progress -Activity $activity -Status "$done\$total" -PercentComplete ($done/$total*100)
                            }

                        } # end foreach event

                        if ( $progress ) {
                            Write-Progress -Activity $activity -Status "$done\$total" -PercentComplete ($done/$total*100)
                            #Write-Progress -Activity $activity -Completed
                        }


                        return $returnInvoke


                    }
                    # end Invoke-Command


                    Get-PSSession -ComputerName $computer | Remove-PSSession

                    return $outputInvoke

                } # end if Get-PSSession

            } # end Start-Job

            [array]$startedJobs += $job

        } # end foreach computer
        Write-Information ""

    } # end Process{}


    End {

# while ( $jobs = (Get-Job -Name '*-LogonEvents') ) {
        Write-Information "STATUS : Waiting for jobs to finish`n"

        while ( $startedJobs ) {
# foreach ( $job in $jobs ) {
            foreach ( $job in $startedJobs ) {

                $jobState = $job.State  # to prevent status changes during processing

                if ($jobState -eq 'Completed' ) {

                    Write--JobProgress -job $job

                    Write-Information "STATUS : Importing results from job: $(($job).Name)" -Tags 'Status'
                     $receivedJob = Receive-Job $job

                    $job | Remove-Job
                    $startedJobs = $startedJobs | Where {$_ -ne $job}

                    if ($receivedJob.length -gt 1) {
                        # remove the first item which is the job
                        [array]$outputJob += $receivedJob[1..($receivedJob.length-1)]
                    }

                    Write-Information "SUCCESS: Job ready: $(($job).Name)`n" -Tags 'Success'
                }
                else {
                    Write--JobProgress -job $job
                    #sleep 1
                }


            } # end foreach job
            
        } # end While



        Write-Output $outputJob

    } # end End{}
        
}