functions/Get-Sessions.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
<#
.SYNOPSIS
Gets information about interactive logins.
.DESCRIPTION
Gets information about interactive logins to a system. If no further parameters are specified, the local system is
examined and the logins of the last week are displayed in short format. An attempt is made to logically summarize
the times of logon sessions, this means for a new connection after a disconnect a new record is displayed.
.PARAMETER UserName
Match filter for the username of the sessions to be displayed. Default: empty.
.PARAMETER ComputerName
Computer to be examined. Standard: local system.
.PARAMETER After
Start time of the events to be examined. Default: a week ago.
.PARAMETER Before
End time of the events to be examined. Default: now.
.PARAMETER Detailed
Expanded output with session id and name or IP of the remote host. Default: $FALSE.
.EXAMPLE
Get-Sessions | Format-Table
 
Lists sessions of the local computer of the last week.
.EXAMPLE
Get-Sessions -User Markus -After 12/01/2018 -Before 12/02/2018 -ComputerName OtherServer -Detailed
 
Detailed list of sessions of the day 12/01/2018 from System "OtherServer".
.NOTES
Name: Get-Sessions
Author: Markus Scholtes
Version: 1.01 - Improved error handling and english output
Date: 2019-01-28
Based upon the script Get-ULogged.ps1 from VGSandz: https://gallery.technet.microsoft.com/scriptcenter/Find-Users-who-Logged-in-07dbe5f6/
#>

function Get-Sessions([STRING]$UserName = "", [STRING]$ComputerName = "$ENV:COMPUTERNAME", [STRING]$After = "", [STRING]$Before = "", [SWITCH]$Detailed)
{
    Begin
    {
        # Array für Anmeldeeinträge initialisieren
        $SCRIPT:AnmeldeListe = @()

        # Eintrag ausgeben
        function PrintEntry([INT]$Index)
        {
            if (![STRING]::IsNullOrEmpty($SCRIPT:AnmeldeListe[$Index].Account))
            {
                if ($Detailed)
                {
                    $SCRIPT:AnmeldeListe[$Index]
                } else {
                    $SCRIPT:AnmeldeListe[$Index] | Select-Object -Property * -ExcludeProperty SessionId, RemoteHost
                }
            }
        }

        # Alle Einträge ausgeben
        function PrintAll
        {
            for ($i; $i -lt $AnmeldeListe.Length; $i++) { PrintEntry -Index $i }
        }

        # Eintrag zu Benutzernamen suchen, Rückgabe Index (wenn gefunden) oder Arraylänge (wenn nicht gefunden)
        function FindEntry([STRING]$Account)
        {
            $Zaehler = 0
            foreach ($Eintrag in $SCRIPT:AnmeldeListe)
            {
                if ($Eintrag.Account -eq $Account) { break; }
                $Zaehler ++;
            }
            return $Zaehler
        }

        # Eintrag um Ereignis ergänzen oder - falls noch nicht da - neuen Eintrag erstellen
        # bei Bedarf wird ein vorhandener Eintrag zum Benutzer oder der aktuelle Eintrag (wenn beendet) ausgegeben
        function AddEntry([STRING]$Account, $SessionId = $NULL, $RemoteHost = $NULL, $LogonTime = $NULL, $ConnectTime = $NULL, $LogoffTime = $NULL, $DisconnectTime = $NULL)
        {
            # gibt es einen Eintrag zum Benutzer
            $Index = FindEntry -Account $Account
            if ($Index -eq ($SCRIPT:AnmeldeListe).Length)
            { # nein, neuen Eintrag erstellen
                $NeuerEintrag = New-Object PSCustomObject
                $NeuerEintrag | Add-Member -MemberType NoteProperty -Name Account -Value $Account
                $NeuerEintrag | Add-Member -MemberType NoteProperty -Name SessionId -Value $SessionId
                $NeuerEintrag | Add-Member -MemberType NoteProperty -Name RemoteHost -Value $RemoteHost
                $NeuerEintrag | Add-Member -MemberType NoteProperty -Name LogonTime -Value $LogonTime
                $NeuerEintrag | Add-Member -MemberType NoteProperty -Name ConnectTime -Value $ConnectTime
                $NeuerEintrag | Add-Member -MemberType NoteProperty -Name LogoffTime -Value $LogoffTime
                $NeuerEintrag | Add-Member -MemberType NoteProperty -Name DisconnectTime -Value $DisconnectTime

                $SCRIPT:AnmeldeListe += $NeuerEintrag
            } else {
                # prüfen, ob ein bestehender Eintrag zum Benutzer vorhanden ist, der beendet, also ausgegeben werden muss
                $Flush = $FALSE
                if ($LogonTime) { $Flush = $TRUE }
                if ($ConnectTime) { $Flush = $TRUE }
                if ($LogoffTime -And $SCRIPT:AnmeldeListe[$Index].LogoffTime) { $Flush = $TRUE }
                if ($DisconnectTime -And $SCRIPT:AnmeldeListe[$Index].DisconnectTime) { $Flush = $TRUE }
                if ($SessionId -ne $SCRIPT:AnmeldeListe[$Index].SessionId) { $Flush = $TRUE }
                if (!$LogoffTime -And $SCRIPT:AnmeldeListe[$Index].RemoteHost -And ($RemoteHost -ne $SCRIPT:AnmeldeListe[$Index].RemoteHost)) { $Flush = $TRUE }

                if ($Flush)
                { # es muss ein bestehender Eintrag zum Benutzer ausgegeben werden
                    PrintEntry -Index $Index
                    # neuer Eintrag überschreibt den bestehenden Datensatz
                    $SCRIPT:AnmeldeListe[$Index].SessionId = $SessionId
                    $SCRIPT:AnmeldeListe[$Index].RemoteHost = $RemoteHost
                    $SCRIPT:AnmeldeListe[$Index].LogonTime = $LogonTime
                    $SCRIPT:AnmeldeListe[$Index].DisconnectTime = $DisconnectTime
                    $SCRIPT:AnmeldeListe[$Index].ConnectTime = $ConnectTime
                    $SCRIPT:AnmeldeListe[$Index].LogoffTime = $LogoffTime
                } else {
                    # bestehender Eintrag wird ergänzt
                    if (!$SCRIPT:AnmeldeListe[$Index].RemoteHost) { $SCRIPT:AnmeldeListe[$Index].RemoteHost = $RemoteHost }
                    if ($DisconnectTime) { $SCRIPT:AnmeldeListe[$Index].DisconnectTime = $DisconnectTime }
                    if ($LogoffTime) { $SCRIPT:AnmeldeListe[$Index].LogoffTime = $LogoffTime }
                }
            }

            if ($SCRIPT:AnmeldeListe[$Index].DisconnectTime -And $SCRIPT:AnmeldeListe[$Index].LogoffTime)
            { # ist der Eintrag beendet? Ja -> Eintrag ausgeben und löschen
                PrintEntry -Index $Index
                $SCRIPT:AnmeldeListe[$Index].Account = ""
            }
        }

        # Parameter interpretieren
        if ([STRING]::IsNullOrEmpty($Before))
        { $BeforeLog = Get-Date } else { $BeforeLog = Get-Date $Before }
        if ([STRING]::IsNullOrEmpty($After))
        { $AfterLog = (Get-Date).AddDays(-7) } else { $AfterLog = Get-Date $After }
    }

    Process
    {
        try
        { # An- und Abmeldeereignisse auslesen. Wegen Beschleunigung alle Filter in Hashtable übergeben
            $EventDataCollector = Get-Winevent -FilterHashTable @{ LogName = "Microsoft-Windows-TerminalServices-LocalSessionManager/Operational"; Id = 21,23,24,25; StartTime = $AfterLog; EndTime = $BeforeLog } -ComputerName $ComputerName -Oldest -EA "SilentlyContinue"
            foreach ($DataCollected in $EventDataCollector)
            { # Ereignisse durchlaufen
                # Nachricht lesen
                $MessageSplit = $DataCollected.Message.Split("`n")
                # Benutzernamen extrahieren
                $UserLogged = ($MessageSplit[2].Split(":"))[1].Trim()
                # Session-ID extrahieren
                $IdLogged = ($MessageSplit[3].Split(":"))[1].Trim().TrimEnd(".")
                if ($DataCollected.Id -ne "23")
                {    # Remotehost extrahieren
                    $SourceLogged = ($MessageSplit[4].Split(":"))[1].Trim().TrimEnd(".")
                } else { # Information nicht vorhanden bei Abmeldenachricht
                    $SourceLogged = $NULL
                }

                if ($UserLogged -match $UserName)
                { # wenn Ereignis zu gesuchter Benutzernamensmaske
                    switch ($DataCollected.Id)
                    { # Anmeldeereignis
                        "21" { AddEntry -Account $UserLogged -SessionId $IdLogged -RemoteHost $SourceLogged -LogonTime $DataCollected.TimeCreated }
                        # Abmeldeereignis
                        "23" { AddEntry -Account $UserLogged -SessionId $IdLogged -RemoteHost $SourceLogged -LogoffTime $DataCollected.TimeCreated }
                        # Trennungsereignis
                        "24" { AddEntry -Account $UserLogged -SessionId $IdLogged -RemoteHost $SourceLogged -DisconnectTime $DataCollected.TimeCreated }
                        # Verbidungsereignis
                        "25" { AddEntry -Account $UserLogged -SessionId $IdLogged -RemoteHost $SourceLogged -ConnectTime $DataCollected.TimeCreated }
                    }
                }
            }
            if ($SCRIPT:AnmeldeListe.Length -eq 0)
            { # kein Eintrag gefunden
                if ([STRING]::IsNullOrEmpty($UserName))
                { # passende Meldung erzeugen
                    Write-Output "No logon events between $AfterLog and $BeforeLog on computer $ComputerName"
                } else {
                    Write-Output "No logon events of users with partial name '$UserName' between $AfterLog and $BeforeLog on computer $ComputerName"
                }
            }
        }
        catch
        { # Fehler beim Ermitteln der Ereignisse
            Write-Error "Error processing event log from computer $ComputerName`: $($_.Exception.Message)"
        }
    }

    End
    { # noch nicht ausgegebene Datensätze ausgeben
        PrintAll
    }
}