使用 PowerShell 将 Windows 转发事件导入 SQL Server

在过去的几周里,我研究了多种解析Windows 转发事件并将其导入SQL Server 的方法:从使用 SSIS 到 LogParser 到 PowerShell,再到将链接服务器设置为“Forwarding Events.evtx”文件。

最终,唯一有效的是 PowerShell 的 Get-WinEvent cmdlet。然后,它只适用于我的一个特定情况——如果事件是在 Windows 2012 服务器上收集和解析的。截至今天,Get-WinEvent 中存在一个未解决的错误,该错误通常导致 NULL LevelDisplayName、Message 和 TaskDisplayName 列。我在 Win2k8 R2 服务器和 Win 8 工作站上复制了下面的确切代码,并反复遇到 NULLs 问题。但是,您的结果可能会有所不同,因为一些用户报告通过在 Win2k8 R2 Server 中调整一些东西取得了成功。

因此,启动一个 Windows 2012 机器,设置您的 SQL Server,让我们开始吧:

SQL 部分

查看Get-WinEvent返回的数据后,我发现以下列最有用:ID、LevelDisplayName、LogName、MachineName、Message、ProviderName、RecordID、TaskDisplayName、TimeCreated。然后我使用这些列创建了一个表:

CREATE DATABASE EventCollections
GO
USE EventCollections
GO
-- the table name loosely relates to the name of my Win Event Subscription name
CREATE TABLE [dbo].[GeneralEvents]( 
     [Id] [int] NULL,
     [LevelDisplayName] [varchar](255) NULL,
     [LogName] [varchar](255) NULL,
     [MachineName] [varchar](255) NULL,
     [Message] [varchar](max) NULL,
     [ProviderName] [varchar](255) NULL,
     [RecordID] [bigint] NULL,
     [TaskDisplayName] [varchar](255) NULL,
     [TimeCreated] [smalldatetime] NULL
)
-- Create Unique Clustered Index with IGNORE_DUPE_KEY=ON to avoid duplicates in sqlbulk imports
CREATE UNIQUE CLUSTERED INDEX [ClusteredIndex-EventCombo] ON [dbo].[GeneralEvents]
(
     [RecordID] ASC,
     [MachineName] ASC,
     [LogName] ASC
) WITH (IGNORE_DUP_KEY = ON)
GO

为了避免在每小时导入期间出现重复,我使用 IGNORE_DUP_KEY = ON 在 3 列上的唯一索引创建了表:RecordID、MachineName 和 LogName。

接下来,我必须决定如何将数据从 PowerShell 获取到 SQL Server。在阅读了sqlservercentral.comtechnet之后,我决定使用sqlbulkcopy.

PowerShell 部分

转发事件是一件棘手的事情。出于某种原因,通常使用FilterHasTable过滤 Get-WinEvent 结果的方式一直返回结果Get-WinEvent :未找到与指定选择条件匹配的事件。我发现许多其他人也遇到了这个问题,当人们尝试使用 LogParser 时也发生了类似的错误。毕竟,我对 FilterXML 的工作没有太大希望,但它确实做到了!因此,我们将在执行初始导入后使用它。

这是initial import在转发事件中收集所有事件的代码。

$events = Get-WinEvent ForwardedEvents |  Select-Object ID, LevelDisplayName, LogName, MachineName, Message, ProviderName, RecordID, TaskDisplayName, TimeCreated  

$connectionString = "Data Source=sqlserver;Integrated Security=true;Initial Catalog=EventCollections;"
$bulkCopy = new-object ("Data.SqlClient.SqlBulkCopy") $connectionString
$bulkCopy.DestinationTableName = "GeneralEvents"
$dt = New-Object "System.Data.DataTable"

# build the datatable
$cols = $events | select -first 1 | get-member -MemberType NoteProperty | select -Expand Name
foreach ($col in $cols)  {$null = $dt.Columns.Add($col)}
  
foreach ($event in $events)
  {
     $row = $dt.NewRow()
     foreach ($col in $cols) { $row.Item($col) = $event.$col }
     $dt.Rows.Add($row)
  }
  
 # Write to the database!
 $bulkCopy.WriteToServer($dt)

您可能已经注意到我手动构建了一个数据表,而不是使用Out-DataTable.ps1,这似乎是粉丝的最爱。我觉得上面的代码让事情变得更加整洁,性能仍然相当不错。

由于事件收集是一个持续的事情,您可能希望定期导入它们。我通过右键单击事件查看器中的转发事件->过滤当前日志...->记录:(更改为一小时)->单击顶部的 XML 选项卡->复制/粘贴->瞧,构建了必要的 XML 查询。

扫描二维码关注公众号,回复: 14440057 查看本文章

实际上,使用这个查询的语法,我找到了 FilterHashTable 的语法,但是让 GUI 构建我的查询很容易,所以我坚持了下来。这是hourly import您可以在任务计划程序中设置的代码。

# While this script is intended to run on an hourly basis, the filter is set for going back 65 minutes.
# This allows the script to run for 5 minutes without any missing any events. Because we setup the 
# table using the IGNORE_DUPE_KEY = ON, duplicate entries are ignored in the database.

$xml = @'
<QueryList>
  <Query Id="0" Path="ForwardedEvents">
    <Select Path="ForwardedEvents">*[System[TimeCreated[timediff(@SystemTime) &lt;= 3900000]]]</Select>
  </Query>
</QueryList>
'@

$events = Get-WinEvent -FilterXml $xml |  Select-Object ID, LevelDisplayName, LogName, MachineName, Message, ProviderName, RecordID, TaskDisplayName, TimeCreated  

$connectionString = "Data Source=sqlserver;Integrated Security=true;Initial Catalog=EventCollections;"
$bulkCopy = new-object ("Data.SqlClient.SqlBulkCopy") $connectionString
$bulkCopy.DestinationTableName = "GeneralEvents"
$dt = New-Object "System.Data.DataTable"

# build the datatable
$cols = $events | select -first 1 | get-member -MemberType NoteProperty | select -Expand Name
foreach ($col in $cols)  {$null = $dt.Columns.Add($col)}
  
foreach ($event in $events)
  {
     $row = $dt.NewRow()
     foreach ($col in $cols) { $row.Item($col) = $event.$col }
     $dt.Rows.Add($row)
  }
 
# Write to the database!
$bulkCopy.WriteToServer($dt)

运气好的话,您的 SQL 输出应该如下所示:

猜你喜欢

转载自blog.csdn.net/allway2/article/details/126141105
今日推荐