Consulte Logs de Eventos de Forma Eficiente com Get-EventLog do PowerShell

Cada administrador de sistema do Windows provavelmente está familiarizado com o Log de Eventos do Windows. O uso deste cmdlet no PowerShell permite aos sysadmins analisar muitos eventos de uma vez em vários computadores. Isso libera os sysadmins de clicar ao redor no Visualizador de Eventos tentando descobrir o filtro certo e determinar onde precisamente o evento crítico está armazenado. No entanto, Get-EventLog tem suas desvantagens, como você verá.

Listando Logs de Eventos com Get-EventLog

O cmdlet Get-EventLog está disponível em todas as versões modernas do Windows PowerShell. Em seu uso mais direto, este cmdlet precisa de um log de eventos para consultar e, em seguida, exibirá todos os eventos nesse log.

Mas e se você não souber o nome do log de eventos em primeiro lugar? Nesse caso, precisamos descobrir todos os logs de eventos disponíveis em nosso computador local. Fazemos isso usando o comando Get-EventLog -List.

Get-EventLog -List

Você pode ver que tenho alguns logs de eventos no meu sistema local agora, mas você pode estar se perguntando onde estão os outros? Dezenas de outros logs de eventos aparecem em Registros de Aplicativos e Serviços no Visualizador de Eventos. Por que eles não estão aqui?

Se você precisar desses eventos, infelizmente, Get-EventLog não funcionará. Em vez disso, você precisará conferir Get-WinEvent. O cmdlet Get-EventLog pode ser considerado um cmdlet legado neste ponto, mas ainda o uso frequentemente simplesmente porque é muito fácil de usar.

Consultando Eventos com Get-EventLog

Agora que conhecemos todos os registros de eventos disponíveis, podemos ler os eventos dentro desse registro de eventos. Talvez eu queira ver todos os eventos no registro de eventos de Aplicação. Para obter esses eventos, preciso especificar o parâmetro LogName com Get-EventLog e o cmdlet obedecerá retornando todos os eventos nesse registro de eventos.

Get-EventLog -LogName Application

Por padrão, você verá apenas seis propriedades na saída:

  • Index
  • Time
  • EntryType
  • Source
  • InstanceId
  • Message

Na realidade, Get-EventLog retorna 16 delas. A razão pela qual você vê apenas seis é devido às regras de formatação do PowerShell que definem a saída. Abaixo está um exemplo da saída real encontrada ao encadear Get-EventLog com Select-Object e selecionar todas as propriedades.

Get-EventLog -LogName Application | Select-Object -Property * EventID

Filtrando com Get-EventLog

Provavelmente, ao procurar eventos, não precisamos de todos os eventos. Em vez disso, só precisamos de alguns. Nesse caso, precisamos filtrar eventos específicos. O cmdlet Get-EventLog oferece algumas maneiras diferentes de fazer isso. Ele pode filtrar com base em carimbo de data/hora, tipo de entrada, ID do evento, mensagem, origem e nome de usuário. Isso cobre a maioria das maneiras de encontrar eventos.

Para demonstrar filtragem, talvez eu esteja consultando eventos de tempos em tempos e queira encontrar os dez eventos mais recentes. Nesse caso, posso usar o parâmetro Newest e especificar quantos eventos gostaria de ver. Get-EventLog -LogName Application -Newest 10 retornará apenas os dez eventos mais recentes.

Talvez eu queira encontrar todos os eventos após um ponto específico no tempo. Para isso, temos o parâmetro After. O parâmetro After recebe uma data/hora, então, se eu quiser encontrar apenas os eventos dentro do log de Aplicativos que ocorreram após 26/01/19 às 10h17, eu poderia fazer isso Get-EventLog -LogName Application -After '26/01/19 10:17'. Também podemos realizar o mesmo processo, mas selecionar eventos que ocorreram antes de uma determinada data com, você já deve ter imaginado, o parâmetro Before.

O Get-EventLog tem muitas maneiras diferentes de filtrar, não incluindo com base em um carimbo de data/hora. Também podemos filtrar eventos com base em outros atributos como ID do evento (ID da Instância) e mensagem, que costumam ser atributos comuns para pesquisar. Talvez eu saiba que estou procurando um evento com ID 916; nós passaríamos 916 para o parâmetro InstanceId.

PS> Get-EventLog -LogName Application -InstanceId 916

Também podemos combinar filtros. Talvez eu receba muitos eventos retornados com um ID de 916, mas queira aqueles eventos com a string svchost na mensagem. Nesse caso, podemos adicionar o parâmetro Message ao Get-EventLog e especificar um curinga como svchost.

PS> Get-EventLog -LogName Application -InstanceId 916 -Message '*svchost*'

Script de Bônus!

Você precisa de um ótimo exemplo de como usar o Get-EventLog em um script do mundo real? Se sim, você está com sorte! Abaixo está um caso de uso avançado do Get-EventLog que você pode baixar e usar hoje!

.SYNOPSIS
    Este script procura em um computador com Windows todas as entradas de log de eventos ou logs de texto
    entre um horário de início e término especificado.
.DESCRIPTION
    Este script enumera todos os logs de eventos dentro de um intervalo de tempo especificado e todos os logs de texto
    que têm um horário de última gravação dentro desse intervalo ou contêm uma linha com uma data/hora
    referência dentro do intervalo. Ele registra essas informações em um conjunto de arquivos e copia
    qualquer arquivo de log interessante para análise posterior.
.EXAMPLE
    PS> .\Get-EventsFromTimeframe.ps1 -StartTimestamp '01-29-2014 13:25:00' -EndTimeStamp '01-29-2014 13:28:00' -ComputerName NOME_DO_COMPUTADOR

    Este exemplo encontra todas as entradas de log de eventos entre StartTimestamp e EndTimestamp e qualquer arquivo
    com uma extensão dentro do parâmetro $FileExtension que foi gravado pela última vez dentro do intervalo
    ou contém uma linha com uma referência de data/hora dentro do intervalo.
.PARAMETER StartTimestamp
 A data/hora mais cedo que você gostaria de começar a procurar eventos.
.PARAMETER EndTimestamp
 A data/hora mais recente que você gostaria de começar a procurar eventos.
.PARAMETER Computername
 O nome do computador remoto (ou local) que você gostaria de pesquisar.
.PARAMETER OutputFolderPath
 O caminho da pasta que conterá os arquivos de texto com os eventos.
.PARAMETER LogAuditFilPath
 O caminho do arquivo de texto que documentará o arquivo de log, número da linha e tipo de correspondência
 para os logs que foram correspondidos.
.PARAMETER EventLogsOnly
 Use este parâmetro de switch se quiser procurar apenas nos logs de eventos.
.PARAMETER LogFilesOnly
 Use este parâmetro se quiser procurar apenas logs no sistema de arquivos.
.PARAMETER ExcludeDirectory
 Se estiver procurando no sistema de arquivos, especifique quais pastas você gostaria de pular.
.PARAMETER FileExtension
 Especifique uma ou mais conjuntos de extensões de arquivo separados por vírgula que você gostaria de procurar no sistema de arquivos.
 Isso padrão para extensões 'log,txt,wer'.
#>
[CmdletBinding(DefaultParameterSetName = 'Neither')]
param (
    [Parameter(Mandatory)]
    [datetime]$StartTimestamp,
    [Parameter(Mandatory)]
    [datetime]$EndTimestamp,
    [Parameter(ValueFromPipeline,
        ValueFromPipelineByPropertyName)]
    [string]$ComputerName = 'localhost',
    [Parameter()]
 [string]$OutputFolderPath = ".\$Computername",
 [Parameter(ParameterSetName = 'LogFiles')]
 [string]$LogAuditFilePath = "$OutputFolderPath\LogActivity.csv",
 [Parameter(ParameterSetName = 'EventLogs')]
 [switch]$EventLogsOnly,
    [Parameter(ParameterSetName = 'LogFiles')]
    [switch]$LogFilesOnly,
    [Parameter(ParameterSetName = 'LogFiles')]
 [string[]]$ExcludeDirectory,
 [Parameter(ParameterSetName = 'LogFiles')]
 [string[]]$FileExtension = @('log', 'txt', 'wer')
)
 
begin {
 if (!$EventLogsOnly.IsPresent) {
 ## Crie o diretório local onde armazenar os arquivos de log que correspondem aos critérios
 $LogsFolderPath = "$OutputFolderPath\logs"
 if (!(Test-Path $LogsFolderPath)) {
 mkdir $LogsFolderPath | Out-Null
 }
 }
 
 function Add-ToLog($FilePath,$LineText,$LineNumber,$MatchType) {
 $Audit = @{
 'FilePath' = $FilePath;
 'LineText' = $LineText
 'LineNumber' = $LineNumber
 'MatchType' = $MatchType
 }
 [pscustomobject]$Audit | Export-Csv -Path $LogAuditFilePath -Append -NoTypeInformation
 }
}
 
process {
 
    ## Execute apenas se o usuário quiser encontrar entradas de log de eventos
    if (!$LogFilesOnly.IsPresent) {
 ## Encontre todos os nomes de log de eventos que contenham pelo menos 1 evento
 $Logs = (Get-WinEvent -ListLog * -ComputerName $ComputerName | where { $_.RecordCount }).LogName
 $FilterTable = @{
 'StartTime' = $StartTimestamp
 'EndTime' = $EndTimestamp
 'LogName' = $Logs
 }
 
 ## Encontre todos os eventos em todos os logs de eventos que estão entre os horários de início e término
 $Events = Get-WinEvent -ComputerName $ComputerName -FilterHashtable $FilterTable -ea 'SilentlyContinue'
 Write-Verbose "Found $($Events.Count) total events"
 
 ## Converta as propriedades para algo mais amigável e acrescente cada evento ao arquivo de texto do log de eventos
 $LogProps = @{ }
 [System.Collections.ArrayList]$MyEvents = @()
 foreach ($Event in $Events) {
 $LogProps.Time = $Event.TimeCreated
 $LogProps.Source = $Event.ProviderName
 $LogProps.EventId = $Event.Id
 if ($Event.Message) {
 $LogProps.Message = $Event.Message.Replace("<code>n", '|').Replace("</code>r", '|')
 }
 $LogProps.EventLog = $Event.LogName
 $MyEvents.Add([pscustomobject]$LogProps) | Out-Null
 }
 $MyEvents | sort Time | Export-Csv -Path "$OutputFolderPath\eventlogs.txt" -Append -NoTypeInformation
 }
 
 ## Execute apenas se o usuário quiser encontrar arquivos de log
 if (!$EventLogsOnly.IsPresent) {
        ## Enumere todas as compartilhamentos administrativos remotos no computador remoto. Faço isso em vez de enumerar todas as unidades físicas porque
        ## a unidade pode estar lá e o compartilhamento pode não estar, o que significa que não consigo acessar a unidade de qualquer maneira.
 $Shares = Get-WmiObject -ComputerName $ComputerName -Class Win32_Share | where { $_.Path -match '^\w{1}:\\$' }
 [System.Collections.ArrayList]$AccessibleShares = @()
 ## Certifique-se de que é possível acessar todos os compartilhamentos administrativos remotos
 foreach ($Share in $Shares) {
 $Share = "\\$ComputerName\$($Share.Name)"
 if (!(Test-Path $Share)) {
 Write-Warning "Unable to access the '$Share' share on '$Computername'"
 } else {
 $AccessibleShares.Add($Share) | Out-Null 
 }
 }
 
 $AllFilesQueryParams = @{
 Path = $AccessibleShares
 Recurse = $true
 Force = $true
 ErrorAction = 'SilentlyContinue'
 File = $true
 }
 ## Adicione quaisquer diretórios especificados no parâmetro $ExcludeDirectory para não procurar arquivos de log
 if ($ExcludeDirectory) {
 $AllFilesQueryParams.ExcludeDirectory = $ExcludeDirectory 
 }
 ## Crie a sequência de regex complicada que uso para procurar por uma variedade de formatos de data/hora diferentes.
 ## Isso é usado na tentativa de procurar strings de data/hora em cada arquivo de texto encontrado
 ##TODO: Adicionar capacidade de corresponder em Jan,Feb,Mar,etc
 $DateTimeRegex = "($($StartTimestamp.Month)[\\.\-/]?$($StartTimestamp.Day)[\\.\-/]?[\\.\-/]$($StartTimestamp.Year))|($($StartTimestamp.Year)[\\.\-/]?$($StartTimestamp.Month)[\\.\-/]?[\\.\-/]?$($StartTimestamp.Day))"
 ## Enumere todos os arquivos que correspondam aos parâmetros de consulta que têm conteúdo
 Get-ChildItem @AllFilesQueryParams | where { $_.Length -ne 0 } | foreach {
 try {
 Write-Verbose "Processing file '$($_.Name)'"
 ## Grave o arquivo se o horário de última gravação estiver dentro do intervalo. Isso encontra arquivos de log que podem não registrar um 
 ## timestamp de data/hora, mas ainda podem estar envolvidos no evento que o usuário está tentando encontrar.
 if (($_.LastWriteTime -ge $StartTimestamp) -and ($_.LastWriteTime -le $EndTimestamp)) {
 Write-Verbose "Last write time within timeframe for file '$($_.Name)'"
 Add-ToLog -FilePath $_.FullName -MatchType 'LastWriteTime'
 }
 ## Se o arquivo encontrado corresponder ao conjunto de extensões que estou tentando encontrar e for realmente um arquivo de texto simples.
 ## Eu uso o Get-Content apenas para verificar se é texto simples antes de analisá-lo.
 if ($FileExtension -contains $_.Extension.Replace('.','') -and !((Get-Content $_.FullName -Encoding Byte -TotalCount 1024) -contains 0)) {
 ## Verifique o conteúdo do arquivo de texto para referências a datas no intervalo de tempo
 Write-Verbose "Checking log file '$($_.Name)' for date/time match in contents"
 $LineMatches = Select-String -Path $_.FullName -Pattern $DateTimeRegex
 if ($LineMatches) {
 Write-Verbose "Date/time match found in file '$($_.FullName)'"
 ## Registre todas as linhas correspondentes no arquivo de auditoria.
 foreach ($Match in $LineMatches) {
 Add-ToLog -FilePath $_.FullName -LineNumber $Match.LineNumber -LineText $Match.Line -MatchType 'Contents'
 }
 ## Crie o mesmo caminho para o arquivo de log no computador remoto dentro do diretório de log de saída e 
 ## copie o arquivo de log com um evento dentro do intervalo para esse caminho.
 ## Isso não funcionará se um caminho de arquivo acima de 255 caracteres for encontrado.
 $Trim = $_.FullName.Replace("\\$Computername\", '')
 $Destination = "$OutputFolderPath\$Trim"
 if (!(Test-Path $Destination)) {
 ##TODO: Remover a ação de erro quando o suporte a caminhos longos for implementado
 mkdir $Destination -ErrorAction SilentlyContinue | Out-Null
 }
 Copy-Item -Path $_.FullName -Destination $Destination -ErrorAction SilentlyContinue -Recurse
 }
 }
 } catch {
 Write-Warning $_.Exception.Message 
 }
 }
 }
}

Resumo

O cmdlet Get-EventLog é um ótimo comando para usar se você precisar consultar rapidamente um dos logs de eventos comuns. É fácil de usar e fornece alguma capacidade de filtragem básica. No entanto, se você precisar realizar uma investigação mais aprofundada nos logs de eventos, o comando Get-WinEvent provavelmente funcionará melhor, mas é um pouco mais difícil de usar e às vezes exige o conhecimento de sintaxe como o XPath.

Source:
https://adamtheautomator.com/get-eventlog/