使用Powershell获取过去一年登录过机器的用户列表

问题描述 投票:0回答:2

我们有几台计算机用于实验室进行处理,用户可以直接现场登录或通过远程桌面登录。我们正在尝试清理机器,并决定删除过去一年未登录的用户文件夹。我可以使用 Windows 事件查看器来查找信息,但我还没有找到导出所需信息的方法。

我发现这个脚本似乎完全符合我的需要,除了运行它时出现以下错误:https://github.com/adbertram/Random-PowerShell-Work/blob/master/ActiveDirectory/获取 UserLogonSessionHistory.ps1

PS C:\Users\vmc\Documents> .\userevents.ps1
Get-WinEvent : Could not retrieve information about the Security log. Error: Attempted to perform an unauthorized
operation..
At C:\Users\vmc\Documents\userevents.ps1:48 char:29
+ ...  ($events = Get-WinEvent -ComputerName $computer -LogName $logNames - ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Get-WinEvent], Exception
    + FullyQualifiedErrorId : LogInfoUnavailable,Microsoft.PowerShell.Commands.GetWinEventCommand

Get-WinEvent : There is not an event log on the BIGBERTHA computer that matches "Security".
At C:\Users\vmc\Documents\userevents.ps1:48 char:29
+ ...  ($events = Get-WinEvent -ComputerName $computer -LogName $logNames - ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Security:String) [Get-WinEvent], Exception
    + FullyQualifiedErrorId : NoMatchingLogsFound,Microsoft.PowerShell.Commands.GetWinEventCommand

C:\Users\vmc\Documents\userevents.ps1 : A positional parameter cannot be found that accepts argument '.'.
At line:1 char:1
+ .\userevents.ps1
+ ~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [userevents.ps1], ParameterBindingException
    + FullyQualifiedErrorId : PositionalParameterNotFound,userevents.ps1

当我查看 Windows 事件查看器时,确实有一个名为“安全”的日志,所以我不确定为什么无法查询该日志?

感谢您的任何帮助 - 非常感谢运行此脚本或以其他方式编译此列表的建议!

链接中的脚本(保存到 userevents.ps1,从上面的 powershell 调用)

<#  
.SYNOPSIS
    This script finds all logon, logoff and total active session times of all users on all computers specified. For this script
    to function as expected, the advanced AD policies; Audit Logon, Audit Logoff and Audit Other Logon/Logoff Events must be
    enabled and targeted to the appropriate computers via GPO or local policy.
.EXAMPLE
    
.PARAMETER ComputerName
    An array of computer names to search for events on. If this is not provided, the script will search the local computer.
.INPUTS
    None. You cannot pipe objects to Get-ActiveDirectoryUserActivity.ps1.
.OUTPUTS
    None. If successful, this script does not output anything.
#>
[CmdletBinding()]
param
(
    [Parameter()]
    [ValidateNotNullOrEmpty()]
    [string[]]$ComputerName = $Env:COMPUTERNAME
)

try {
    
    #region Defie all of the events to indicate session start or top
    $sessionEvents = @(
        @{ 'Label' = 'Logon'; 'EventType' = 'SessionStart'; 'LogName' = 'Security'; 'ID' = 4624 } ## Advanced Audit Policy --> Audit Logon
        @{ 'Label' = 'Logoff'; 'EventType' = 'SessionStop'; 'LogName' = 'Security'; 'ID' = 4647 } ## Advanced Audit Policy --> Audit Logoff
        @{ 'Label' = 'Startup'; 'EventType' = 'SessionStop'; 'LogName' = 'System'; 'ID' = 6005 }
        @{ 'Label' = 'RdpSessionReconnect'; 'EventType' = 'SessionStart'; 'LogName' = 'Security'; 'ID' = 4778 } ## Advanced Audit Policy --> Audit Other Logon/Logoff Events
        @{ 'Label' = 'RdpSessionDisconnect'; 'EventType' = 'SessionStop'; 'LogName' = 'Security'; 'ID' = 4779 } ## Advanced Audit Policy --> Audit Other Logon/Logoff Events
        @{ 'Label' = 'Locked'; 'EventType' = 'SessionStop'; 'LogName' = 'Security'; 'ID' = 4800 } ## Advanced Audit Policy --> Audit Other Logon/Logoff Events
        @{ 'Label' = 'Unlocked'; 'EventType' = 'SessionStart'; 'LogName' = 'Security'; 'ID' = 4801 } ## Advanced Audit Policy --> Audit Other Logon/Logoff Events
    )
    
    ## All of the IDs that designate when user activity starts
    $sessionStartIds = ($sessionEvents | where { $_.EventType -eq 'SessionStart' }).ID
    ## All of the IDs that designate when user activity stops
    $sessionStopIds = ($sessionEvents | where { $_.EventType -eq 'SessionStop' }).ID
    #endregion
    
    ## Define all of the log names we'll be querying
    $logNames = ($sessionEvents.LogName | select -Unique)
    ## Grab all of the interesting IDs we'll be looking for
    $ids = $sessionEvents.Id
        
    ## Build the insane XPath query for the security event log in order to query events as fast as possible
    $logonXPath = "Event[System[EventID=4624]] and Event[EventData[Data[@Name='TargetDomainName'] != 'Window Manager']] and Event[EventData[Data[@Name='TargetDomainName'] != 'NT AUTHORITY']] and (Event[EventData[Data[@Name='LogonType'] = '2']] or Event[EventData[Data[@Name='LogonType'] = '11']])"
    $otherXpath = 'Event[System[({0})]]' -f "EventID=$((@($ids).where({ $_ -ne '4624' })) -join ' or EventID=')"
    $xPath = '({0}) or ({1})' -f $logonXPath, $otherXpath

    foreach ($computer in $ComputerName) {
        ## Query each computer's event logs using the Xpath filter
        if (-not ($events = Get-WinEvent -ComputerName $computer -LogName $logNames -FilterXPath $xPath)) {
        Write-Warning -Message 'No logon events found'.
    } else {
        Write-Verbose -Message "Found [$($events.Count)] events to look through"

        ## Set up the output object
        $output = [ordered]@{
            'ComputerName'          = $computer
            'Username'              = $null
            'StartTime'             = $null
            'StartAction'           = $null
            'StopTime'              = $null
            'StopAction'            = $null
            'Session Active (Days)' = $null
            'Session Active (Min)'  = $null
        }
        
        ## Need current users because if no stop time, they're still probably logged in
        $getGimInstanceParams = @{
            ClassName = 'Win32_ComputerSystem'
        }
        if ($computer -ne $Env:COMPUTERNAME) {
            $getGimInstanceParams.ComputerName = $computer
        }
        $loggedInUsers = Get-CimInstance @getGimInstanceParams | Select-Object -ExpandProperty UserName | foreach { $_.split('\')[1] }

        ## Find all user start activity events and begin parsing
        @($events).where({ $_.Id -in $sessionStartIds }).foreach({
            try {
                $logonEvtId = $_.Id
                $output.StartAction = @($sessionEvents).where({ $_.ID -eq $logonEvtId }).Label
                $xEvt = [xml]$_.ToXml()

                ## Figure out the login session ID
                $output.Username = ($xEvt.Event.EventData.Data | where { $_.Name -eq 'TargetUserName' }).'#text'
                $logonId = ($xEvt.Event.EventData.Data | where { $_.Name -eq 'TargetLogonId' }).'#text'
                if (-not $logonId) {
                $logonId = ($xEvt.Event.EventData.Data | where { $_.Name -eq 'LogonId' }).'#text'
                }
                $output.StartTime = $_.TimeCreated

                Write-Verbose -Message "New session start event found: event ID [$($logonEvtId)] username [$($output.Username)] logonID [$($logonId)] time [$($output.StartTime)]"
                ## Try to match up the user activity end event with the start event we're processing
                if (-not ($sessionEndEvent = @($Events).where({ ## If a user activity end event could not be found, assume the user is still logged on
                        $_.TimeCreated -gt $output.StartTime -and
                        $_.ID -in $sessionStopIds -and
                        (([xml]$_.ToXml()).Event.EventData.Data | where { $_.Name -eq 'TargetLogonId' }).'#text' -eq $logonId
                    })) | select -last 1) {
                if ($output.UserName -in $loggedInUsers) {
                    $output.StopTime = Get-Date
                    $output.StopAction = 'Still logged in'
                } else {
                    throw "Could not find a session end event for logon ID [$($logonId)]."
                }
                } else {
                ## Capture the user activity end time
                $output.StopTime = $sessionEndEvent.TimeCreated
                Write-Verbose -Message "Session stop ID is [$($sessionEndEvent.Id)]"
                $output.StopAction = @($sessionEvents).where({ $_.ID -eq $sessionEndEvent.Id }).Label
                }

                $sessionTimespan = New-TimeSpan -Start $output.StartTime -End $output.StopTime
                $output.'Session Active (Days)' = [math]::Round($sessionTimespan.TotalDays, 2)
                $output.'Session Active (Min)'  = [math]::Round($sessionTimespan.TotalMinutes, 2)

                [pscustomobject]$output
            } catch {
                Write-Warning -Message $_.Exception.Message
            }
            })
        }
    }
} catch {
    $PSCmdlet.ThrowTerminatingError($_)
}
windows powershell event-log
2个回答
1
投票

首先你得到这个错误:

Attempted to perform an unauthorized

并且:

There is not an event log on the BIGBERTHA computer that matches "Security"

这清楚地表明用于对远程计算机运行查询的帐户没有必要的权限。由于访问权限不足,您无法访问和/或找到安全日志。

在使用正确的帐户和提升的 shell 进行第二次运行时,您确实取得了进一步的进展,因为您现在收到了错误:

The specified query is invalid

这意味着您已经能够连接到远程计算机并读取安全日志,但 get-winevent 无法执行该操作,因为查询语法无效。

这部分代码构建过滤器:

$logonXPath = "Event[System[EventID=4624]] and Event[EventData[Data[@Name='TargetDomainName'] != 'Window Manager']] and Event[EventData[Data[@Name='TargetDomainName'] != 'NT AUTHORITY']] and (Event[EventData[Data[@Name='LogonType'] = '2']] or Event[EventData[Data[@Name='LogonType'] = '11']])"
$otherXpath = 'Event[System[({0})]]' -f "EventID=$((@($ids).where({ $_ -ne '4624' })) -join ' or EventID=')"
$xPath = '({0}) or ({1})' -f $logonXPath, $otherXpath

这些脚本对我有用......但最终您不需要整个功能,认为这会有所帮助:

#Query
$XPath = "Event[System[EventID=4624]] and Event[EventData[Data[@Name='TargetDomainName'] != 'Window Manager']] and Event[EventData[Data[@Name='TargetDomainName'] != 'NT AUTHORITY']] and (Event[EventData[Data[@Name='LogonType'] = '2']] or Event[EventData[Data[@Name='LogonType'] = '11']])"
#Get List containing the dnsHostNames of the computers to query
$computer = gc [path]
#Run Query against computers and gather result
$result = @(
    foreach ($computer in $computers){
        #run query
        $events = get-winevent -LogName security -FilterXPath $XPath -ComputerName $computer
        #parse events as xml and extract necessary information, return object
        $eventobj = @(
            foreach ($event in $events){
                [xml]$xml = $event.toxml()
                $attrsht = [ordered]@{
                    TimeCreated=$xml.event.system.TimeCreated.SystemTime
                    eventId=$xml.event.system.eventId
                    SubjectUserSid=$xml.event.EventData.data[0].'#text'
                    SubjectUserName=$xml.event.EventData.data[1].'#text'
                    SubjectDomainName=$xml.event.EventData.data[2].'#text'
                    TargetUserSid=$xml.event.EventData.data[4].'#text'
                    TargetUserName=$xml.event.EventData.data[5].'#text'
                    TargetDomainName=$xml.event.EventData.data[6].'#text'
                    LogonType=$xml.event.EventData.data[8].'#text'
                    LogonProcessName=$xml.event.EventData.data[9].'#text'
                    ipAdress=$xml.event.EventData.data[18].'#text'
                }
                #return event object
                new-object -TypeName psobject -Property $attrsht
            }
        )
        $attrsht = @{
            Computer=$computer
            Events=$eventobj
        }
        #return object per computer containing all events
        new-object -TypeName psobject -Property $attrsht
    }
)

#As the property events is an array you can export it by using json
$result | ConvertTo-Json | set-content [path]

#If you want a csv we have to flattern the array 
$result = @(
    foreach ($computer in $computers){
        #run query
        $events = get-winevent -LogName security -FilterXPath $XPath -ComputerName $computer
        #parse events as xml and extract necessary information, return object
        foreach ($event in $events){
            [xml]$xml = $event.toxml()
            $attrsht = [ordered]@{
                computername=$computer
                TimeCreated=$xml.event.system.TimeCreated.SystemTime
                eventId=$xml.event.system.eventId
                SubjectUserSid=$xml.event.EventData.data[0].'#text'
                SubjectUserName=$xml.event.EventData.data[1].'#text'
                SubjectDomainName=$xml.event.EventData.data[2].'#text'
                TargetUserSid=$xml.event.EventData.data[4].'#text'
                TargetUserName=$xml.event.EventData.data[5].'#text'
                TargetDomainName=$xml.event.EventData.data[6].'#text'
                LogonType=$xml.event.EventData.data[8].'#text'
                LogonProcessName=$xml.event.EventData.data[9].'#text'
                ipAdress=$xml.event.EventData.data[18].'#text'
            }
            #return event object
            new-object -TypeName psobject -Property $attrsht
        }
    }
)

$result | export-csv [path] -NoClobber -NoTypeInformation -Delimiter ";"

0
投票

大家好,我必须在捕获后在下面添加一个结束}。然后它起作用了,但由于某种原因我得到了所有事件的重复。所以它显示每个条目两次。不知道为什么。

问候 LeCoeurBombarde

当我没有以管理员权限运行它时,我收到了相同的错误消息。

© www.soinside.com 2019 - 2024. All rights reserved.