Created
April 14, 2025 17:04
-
-
Save bharkr/4e7ae7880035429a21bf06d408c67e66 to your computer and use it in GitHub Desktop.
Retrieves user login information from remote computers within the last 60 days.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #region Script Header | |
| <# | |
| .SYNOPSIS | |
| Retrieves user login information from remote computers within the last 60 days. | |
| .DESCRIPTION | |
| This script queries remote computers for user login events within the last 60 days. | |
| It retrieves the username, computer name, login time, and session type. | |
| The script is designed for PowerShell Core and utilizes best practices for performance. | |
| .PARAMETER ComputerName | |
| Specifies one or more computer names to query. Can be a single computer name or an array of computer names. | |
| .EXAMPLE | |
| .\Get-RemoteUserSessions.ps1 -ComputerName "Server01" | |
| Queries user login information for the computer named "Server01". | |
| .EXAMPLE | |
| .\Get-RemoteUserSessions.ps1 -ComputerName "Server01", "Server02", "Server03" | |
| Queries user login information for the computers "Server01", "Server02", and "Server03". | |
| .EXAMPLE | |
| "Server01","Server02" | .\Get-RemoteUserSessions.ps1 | |
| Queries user login information for the computers "Server01", and "Server02" using pipeline input. | |
| .NOTES | |
| Requires remote computers to have WinRM enabled and accessible. | |
| The script targets Event IDs 4624 (Successful login) and 4647 (User Logoff initiated) | |
| Error Handling is included to continue if a computer is not available. | |
| #> | |
| #endregion Script Header | |
| param ( | |
| [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] | |
| [Alias("CN")] | |
| [string[]]$ComputerName | |
| ) | |
| #region Functions | |
| function Get-RemoteUserSession { | |
| [CmdletBinding()] | |
| param( | |
| [Parameter(Mandatory = $true)] | |
| [string]$ComputerName | |
| ) | |
| try { | |
| Write-Verbose "Querying $ComputerName for login events." | |
| # Calculate the start time for the 60-day period. | |
| $StartTime = (Get-Date).AddDays(-60) | |
| # Define Event IDs for user login and Logoff. | |
| $LoginEventID = 4624 | |
| $LogoffEventID = 4647 | |
| # Query the Security event log for login events within the time period, ordered by time created | |
| $LoginEvents = Get-WinEvent -ComputerName $ComputerName -FilterHashtable @{ LogName = 'Security'; Id = $LoginEventID; StartTime = $StartTime } -ErrorAction Stop | Sort-Object TimeCreated | |
| if ($LoginEvents) { | |
| #Create a hashtable to store our ongoing session data. | |
| $UserSessionData = @{} | |
| $LoginEvents | ForEach-Object { | |
| $loginEvent = $_ | |
| $properties = $loginEvent.Properties | |
| #Extract username and Domain | |
| $AccountName = ($properties | Where-Object { $_.Name -eq 'TargetUserName' }).Value | |
| $AccountDomain = ($properties | Where-Object { $_.Name -eq 'TargetDomainName' }).Value | |
| $LoginType = ($properties | Where-Object { $_.Name -eq 'LogonType' }).Value | |
| $FullAccountName = "$AccountDomain\$AccountName" | |
| if (-not [string]::IsNullOrEmpty($AccountName)) { | |
| #if no session is defined for the user yet, we define it. | |
| if (-not $UserSessionData.ContainsKey($FullAccountName)) { | |
| $UserSessionData.Add($FullAccountName, @{ LoginTime = @(); LogoffTime = @(); LoginType = @() }) | |
| } | |
| #Add the login time, and type to our dictionary. | |
| $UserSessionData[$FullAccountName].LoginTime += $loginEvent.TimeCreated | |
| $UserSessionData[$FullAccountName].LoginType += $LoginType | |
| } | |
| } | |
| #Query the security log for logout events, ordered by time created | |
| $LogoffEvents = Get-WinEvent -ComputerName $ComputerName -FilterHashtable @{ LogName = 'Security'; Id = $LogoffEventID; StartTime = $StartTime } -ErrorAction Stop | Sort-Object TimeCreated | |
| if ($LogoffEvents) { | |
| $LogoffEvents | ForEach-Object { | |
| $logoffEvent = $_ | |
| $properties = $logoffEvent.Properties | |
| #Extract username and Domain | |
| $AccountName = ($properties | Where-Object { $_.Name -eq 'TargetUserName' }).Value | |
| $AccountDomain = ($properties | Where-Object { $_.Name -eq 'TargetDomainName' }).Value | |
| $FullAccountName = "$AccountDomain\$AccountName" | |
| if ($UserSessionData.ContainsKey($FullAccountName)) { | |
| $UserSessionData[$FullAccountName].LogoffTime += $logoffEvent.TimeCreated | |
| } | |
| } | |
| } | |
| #Generate custom objects for each user session. | |
| foreach ($User in $UserSessionData.keys) { | |
| for ($i = 0; $i -lt $UserSessionData[$User].LoginTime.Count; $i++) { | |
| [PSCustomObject]@{ | |
| ComputerName = $ComputerName | |
| UserName = $User | |
| LoginTime = $UserSessionData[$User].LoginTime[$i] | |
| LogoffTime = if ($i -lt $UserSessionData[$User].LogoffTime.Count) { $UserSessionData[$User].LogoffTime[$i] } else { $null } | |
| SessionType = $UserSessionData[$User].LoginType[$i] | |
| } | |
| } | |
| } | |
| } | |
| } | |
| catch { | |
| Write-Warning "Failed to query $ComputerName $($_.Exception.Message)" | |
| return $null # Return null if error is encountered. | |
| } | |
| } | |
| #endregion Functions | |
| #region Main Script | |
| # Check if computer names were piped in or specified as a parameter. | |
| if ($PSBoundParameters.ContainsKey('ComputerName')) { | |
| Write-Verbose "Processing computer names from parameter or pipeline." | |
| $ComputersToProcess = $ComputerName | |
| } | |
| # process computers in parallel for performance gain. | |
| $Jobs = @() | |
| foreach ($ComputerName in $ComputersToProcess) { | |
| $Jobs += Start-Job -ScriptBlock ${function:Get-RemoteUserSession} -ArgumentList $ComputerName | |
| } | |
| #Wait for all jobs to be finished. | |
| Wait-Job -Job $Jobs | |
| #Get all results from the jobs | |
| $results = $Jobs | Receive-Job | |
| #Remove our jobs from memory, since they are done. | |
| Remove-Job -Job $Jobs | |
| #Output the results | |
| $results | Sort-Object -Property ComputerName, LoginTime | Format-Table -AutoSize | |
| #endregion Main Script |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment