Загрузка данных


<#
.SYNOPSIS
    Находит события в журнале Security, в которых фигурирует указанная учётная запись.
.DESCRIPTION
    Скрипт ищет события по имени пользователя (или SID) в полях SubjectUserName, TargetUserName,
    AccountName, MemberName (для имени) или SubjectUserSid, TargetUserSid, AccountSid, MemberSid (для SID).
    Результат можно ограничить по времени и максимальному количеству записей.
.PARAMETER Identity
    Имя учётной записи (например, 'Admin' или 'DOMAIN\Admin') или SID (например, 'S-1-5-21-...').
.PARAMETER LogName
    Имя журнала (по умолчанию 'Security').
.PARAMETER MaxEvents
    Максимальное количество возвращаемых событий (по умолчанию 1000; установите 0 для отключения лимита).
.PARAMETER StartTime
    Начальная дата/время для фильтрации событий (опционально).
.PARAMETER EndTime
    Конечная дата/время для фильтрации событий (опционально).
.EXAMPLE
    .\Find-SecurityEventsByAccount.ps1 -Identity "Admin"
    Находит все события (до 1000) с участием пользователя 'Admin'.
.EXAMPLE
    .\Find-SecurityEventsByAccount.ps1 -Identity "S-1-5-21-123456789-123456789-123456789-500" -MaxEvents 0 -StartTime (Get-Date).AddDays(-1)
    Находит все события за последние сутки с участием указанного SID (без ограничения количества).
.NOTES
    Автор: Скрипт сгенерирован по запросу.
    Версия: 1.0
#>

param(
    [Parameter(Mandatory=$true, Position=0)]
    [string]$Identity,

    [string]$LogName = 'Security',

    [int]$MaxEvents = 1000,

    [datetime]$StartTime,

    [datetime]$EndTime,

    [switch]$Verbose
)

# Функция для построения XPath-запроса
function Build-XPathFilter {
    param(
        [string]$Identity,
        [bool]$IsSID
    )

    # Поля для имени пользователя
    $nameFields = @('SubjectUserName', 'TargetUserName', 'AccountName', 'MemberName')
    # Поля для SID
    $sidFields = @('SubjectUserSid', 'TargetUserSid', 'AccountSid', 'MemberSid')

    $fields = if ($IsSID) { $sidFields } else { $nameFields }
    $conditions = @()

    # Экранирование одинарных кавычек в Identity (замена ' на &apos; в XPath, но проще использовать concat)
    # Для простоты предполагаем, что Identity не содержит кавычек, либо используем двойные кавычки в XPath.
    # В XPath строки можно заключать в одинарные или двойные кавычки.
    # Используем одинарные, а внутри экранируем, заменяя ' на &apos; (но это не всегда работает).
    # Лучше использовать concat, но для простоты предположим, что в Identity нет апострофов.
    $escapedIdentity = $Identity -replace "'", "&apos;"

    foreach ($field in $fields) {
        $conditions += "Data[@Name='$field']='$escapedIdentity'"
    }

    if ($conditions.Count -eq 0) {
        throw "Нет полей для поиска."
    }

    # Собираем условие через OR
    $conditionString = $conditions -join ' or '

    # XPath-запрос: все события, где в EventData есть хотя бы одно совпадение
    $xpath = "*[EventData[$conditionString]]"

    return $xpath
}

# Проверяем, является ли Identity SID
$isSID = $Identity -match '^S-1-\d+(-\d+)*$'

# Формируем XPath
try {
    $xpathFilter = Build-XPathFilter -Identity $Identity -IsSID $isSID
} catch {
    Write-Error "Ошибка построения XPath: $_"
    exit 1
}

# Параметры для Get-WinEvent
$getWinEventParams = @{
    LogName = $LogName
    FilterXPath = $xpathFilter
    ErrorAction = 'Stop'
}

if ($MaxEvents -gt 0) {
    $getWinEventParams.MaxEvents = $MaxEvents
}

if ($StartTime) {
    $getWinEventParams.StartTime = $StartTime
}
if ($EndTime) {
    $getWinEventParams.EndTime = $EndTime
}

if ($Verbose) {
    Write-Verbose "Используемый XPath: $xpathFilter"
    Write-Verbose "Поиск в журнале: $LogName"
}

# Выполняем запрос
try {
    $events = Get-WinEvent @getWinEventParams
} catch {
    Write-Error "Не удалось получить события из журнала '$LogName': $_"
    exit 1
}

# Проверяем, найдены ли события
if ($events.Count -eq 0) {
    Write-Host "Событий с участием '$Identity' не найдено." -ForegroundColor Yellow
    exit 0
}

# Выводим результаты в удобном виде
$events | ForEach-Object {
    [PSCustomObject]@{
        TimeCreated = $_.TimeCreated
        Id          = $_.Id
        Level       = $_.LevelDisplayName
        Message     = $_.Message -replace "`r`n", " " -replace "`n", " " # убираем переносы для компактности
    }
} | Sort-Object TimeCreated -Descending | Format-Table -AutoSize -Wrap

# Дополнительно выводим общее количество найденных событий
Write-Host "Найдено событий: $($events.Count)" -ForegroundColor Green