如何解析和删除Powershell中的存档事件日志
我试图解析存档安全日志以跟踪更改权限的问题。该脚本通过+10天之前的.evtx文件进行查询。它目前输出我想要的内容,但是当它清理旧日志时(每天大约50GB,未压缩,每个都通过午夜运行的另一个脚本存档到它们自己的日常文件夹中),它开始抱怨日志是在使用中并且不能被删除。当我尝试通过资源管理器删除文件时,似乎正在使用的进程是DHCP客户端或事件查看器,同时停止这两种服务,但显然我无法在没有eventvwr的情况下运行。 DHCP客户端用于网络的好处,但不是必需的。如何解析和删除Powershell中的存档事件日志
接触.evtx文件的唯一东西就是这个脚本,它们没有备份,它们不受其他任何东西的监控,它们不会被事件日志服务自动分析,它们只是被存储在磁盘上等待。
该脚本最初删除的东西,因为它去了,但后来失败所有删除被移动到最后,然后到KillLogWithFire()函数。即使计时器似乎没有帮助。我也尝试将文件移动到Processed子文件夹,但这不起作用的原因相同。
我认为有一些方法可以释放此脚本在任何文件上打开的任何句柄,但试图在循环中的EventLog变量的.close()或.dispose()不起作用。
$XPath = @'
*[System[Provider/@Name='Microsoft-Windows-Security-Auditing']]
and
*[System/EventID=4670]
'@
$DeletableLogs = @()
$logfile = "L:\PermChanges.txt"
$AdminUsers = ("List","of","Admin","Users")
$today = Get-Date
$marker = "
-------------
$today
-------------
"
write-output $marker >> $logfile
Function KillLogWithFire($log){
Try {
remove-item $log
}
Catch [writeerror]{
$Timer += 1
sleep $timer
write-output "Killing log $log in $timer seconds"
KillLogWithFire($log)
}
}
Function LogPermissionChange($PermChanges){
ForEach($PermChange in $PermChanges){
$Change = @{}
$Change.ChangedBy = $PermChange.properties[1].value.tostring()
#Filter out normal non-admin users
if ($AdminUsers -notcontains $Change.ChangedBy){continue}
$Change.FileChanged = $PermChange.properties[6].value.tostring()
#Ignore temporary files
if ($Change.FileChanged.EndsWith(".tmp")){continue}
elseif ($Change.FileChanged.EndsWith(".partial")){continue}
$Change.MadeOn = $PermChange.TimeCreated.tostring()
$Change.OriginalPermissions = $PermChange.properties[8].value.tostring()
$Change.NewPermissions = $PermChange.properties[9].value.tostring()
write-output "{" >> $logfile
write-output ("Changed By : "+ $Change.ChangedBy) >> $logfile
write-output ("File Changed : "+ $Change.FileChanged) >> $logfile
write-output ("Change Made : "+ $Change.MadeOn) >> $logfile
write-output ("Original Permissions :
"+ $Change.OriginalPermissions) >> $logfile
write-output ("New Permissions :
"+ $Change.NewPermissions) >> $logfile
"}
" >> $logfile
}
}
GCI -include Archive-Security*.evtx -path L:\Security\$Today.AddDays(-10) -recurse | ForEach-Object{
Try{
$PermChanges = Get-WinEvent -Path $_ -FilterXPath $XPath -ErrorAction Stop
}
Catch [Exception]{
if ($_.Exception -match "No events were found that match the specified selection criteria."){
}
else {
Throw $_
}
}
LogPermissionChange($PermChanges)
$PermChanges = $Null
$DeletableLogs += $_
}
foreach ($log in $DeletableLogs){
$Timer = 0
Try{
remove-item $log
}
Catch [IOException]{
KillLogWithFire($log)
}
}
UPDATE
而不是编辑原始代码,因为我已经被告知不要做,我想后完整的代码,现在使用作为一个单独的答案。初始部分,其解析日志和运行每30分钟是大多与上述相同:
$XPath = @'
*[System[Provider/@Name='Microsoft-Windows-Security-Auditing']]
and
*[System/EventID=4670]
'@
$DeletableLogs = @()
$logfile = "L:\PermChanges.txt"
$DeleteList = "L:\DeletableLogs.txt"
$AdminUsers = ("List","Of","Admins")
$today = Get-Date
$marker = "
-------------
$today
-------------
"
write-output $marker >> $logfile
Function LogPermissionChange($PermChanges){
ForEach($PermChange in $PermChanges){
$Change = @{}
$Change.ChangedBy = $PermChange.properties[1].value.tostring()
#Filter out normal non-admin users
if ($AdminUsers -notcontains $Change.ChangedBy){continue}
$Change.FileChanged = $PermChange.properties[6].value.tostring()
#Ignore temporary files
if ($Change.FileChanged.EndsWith(".tmp")){continue}
elseif ($Change.FileChanged.EndsWith(".partial")){continue}
$Change.MadeOn = $PermChange.TimeCreated.tostring()
$Change.OriginalPermissions = $PermChange.properties[8].value.tostring()
$Change.NewPermissions = $PermChange.properties[9].value.tostring()
write-output "{" >> $logfile
write-output ("Changed By : "+ $Change.ChangedBy) >> $logfile
write-output ("File Changed : "+ $Change.FileChanged) >> $logfile
write-output ("Change Made : "+ $Change.MadeOn) >> $logfile
write-output ("Original Permissions :
"+ $Change.OriginalPermissions) >> $logfile
write-output ("New Permissions :
"+ $Change.NewPermissions) >> $logfile
"}
" >> $logfile
}
}
GCI -include Archive-Security*.evtx -path L:\Security\ -recurse | ForEach-Object{
Try{
$PermChanges = Get-WinEvent -Path $_ -FilterXPath $XPath -ErrorAction Stop
}
Catch [Exception]{
if ($_.Exception -match "No events were found that match the specified selection criteria."){
}
else {
Throw $_
}
}
LogPermissionChange($PermChanges)
$PermChanges = $Null
$DeletableLogs += $_
}
foreach ($log in $DeletableLogs){
write-output $log.FullName >> $DeleteList
}
第二部分不删除,包括上面承蒙TheMadTechnician提供的辅助函数。该代码仍然是循环的直删除比函数快,但并不总是成功的文件后,甚至年龄还没有被触动:
# Log Cleanup script. Works around open log issues caused by PS parsing of
# saved logs in EventLogParser.ps1
$DeleteList = "L:\DeletableLogs.txt"
$DeletableLogs = get-content $DeleteList
Function Close-LockedFile{
Param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)][String[]]$Filename
)
Begin{
$HandleApp = 'C:\sysinternals\Handle.exe'
If(!(Test-Path $HandleApp)){Write-Host "Handle.exe not found at $HandleApp`nPlease download it from www.sysinternals.com and save it in the afore mentioned location.";break}
}
Process{
$HandleOut = Invoke-Expression ($HandleApp+' '+$Filename)
$Locks = $HandleOut |?{$_ -match "(.+?)\s+pid: (\d+?)\s+type: File\s+(\w+?): (.+)\s*$"}|%{
[PSCustomObject]@{
'AppName' = $Matches[1]
'PID' = $Matches[2]
'FileHandle' = $Matches[3]
'FilePath' = $Matches[4]
}
}
ForEach($Lock in $Locks){
Invoke-Expression ($HandleApp + " -p " + $Lock.PID + " -c " + $Lock.FileHandle + " -y") | Out-Null
If (! $LastexitCode) { "Successfully closed " + $Lock.AppName + "'s lock on " + $Lock.FilePath}
}
}
}
Function KillLogWithFire($log){
Try {
Close-LockedFile $Log -
}
Catch [System.IO.IOException]{
$Timer += 1
sleep $timer
write-host "Killing $Log in $Timer seconds with fire."
KillLogWithFire($Log)
}
}
foreach ($log in $DeletableLogs){
Try {
remove-item $log -ErrorAction Stop
}
Catch [System.IO.IOException]{
$Timer = 0
KillLogWithFire($Log)
}
}
remove-item $DeleteList
一个解决办法是让HANDLE.EXE并用它来关闭任何打开的手柄。这是我使用的大致基于this script的功能。它使用handle.exe,找到锁定文件的内容,然后关闭锁定该文件的句柄。
Function Close-LockedFile{
Param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)][String[]]$Filename
)
Begin{
$HandleApp = 'C:\sysinternals\Handle.exe'
If(!(Test-Path $HandleApp)){Write-Host "Handle.exe not found at $HandleApp`nPlease download it from www.sysinternals.com and save it in the afore mentioned location.";break}
}
Process{
$HandleOut = Invoke-Expression ($HandleApp+' '+$Filename)
$Locks = $HandleOut |?{$_ -match "(.+?)\s+pid: (\d+?)\s+type: File\s+(\w+?): (.+)\s*$"}|%{
[PSCustomObject]@{
'AppName' = $Matches[1]
'PID' = $Matches[2]
'FileHandle' = $Matches[3]
'FilePath' = $Matches[4]
}
}
ForEach($Lock in $Locks){
Invoke-Expression ($HandleApp + " -p " + $Lock.PID + " -c " + $Lock.FileHandle + " -y") | Out-Null
If (! $LastexitCode) { "Successfully closed " + $Lock.AppName + "'s lock on " + $Lock.FilePath}
}
}
}
我把handle.exe保存在C:\ Sysinternals中,你可能想调整函数中的路径,或者在那里保存可执行文件。
我有一个非常类似的问题,经过大量的搜索发现这篇文章。虽然handle.exe工作,当我第一次尝试时,我注意到-c带有警告“关闭手柄可能导致应用程序或系统不稳定”
我也使用get-winevent,它似乎(有时)锁定.evtx正在处理的文件。我写了一个循环等待5秒重试。有时最多需要2分钟或文件才能发布,我已经在一夜之间运行了一次,并且有数百次重试。
当我第一次使用处理它完美的工作。然后我将它实现到脚本中,后来发现它正在循环一个“无法解释的错误”。最后,我不得不重新启动服务器以重新开始工作,因此从脚本中删除handle.exe并返回到等待文件关闭。
我可以通过停止脚本并关闭powershell ise来可靠地释放该文件。一旦ISE关闭,文件可以被删除而不会出现问题。
不幸的是,我需要这个脚本继续运行,而不是被保持打开的文件阻挡。我很惊讶,不得不求助于sysinternals来释放文件,并且powershell不提供关闭文件的简单方法。
我和GTEM有同样的问题,在处理数百个事件日志文件时,关闭句柄会导致损坏。最终Get-WinEvent将无法正常工作。它会冻结或给我相同的“无法解释的错误”。 因此,我用MS开了一个重要案例。他们把我引导到我正在存储Get-WinEvent事件的实际变量是什么锁定了文件。我想如果你仍然在使用这个变量,它实际上并不解锁文件。所以为了解决这个问题,我在将变量传递给一个新变量后,向脚本添加了一些代码。您可以看到我在下面列出的第三个区域添加的代码。
#***************************************************************************
#region *** Get the log entries.
# clear the log entry for each pass
$LogEntry = @()
# Get the vent from the log file and export it to the logentry variable and output to the screen
Get-WinEvent -Path $NewPath -FilterXPath $XPathFilter -ErrorAction SilentlyContinue | Tee-Object -Variable LogEntry
#endregion *** End get the log entries
#***************************************************************************
#***************************************************************************
#region *** This is where I copy it to the new variable for later output.
# if there are any log entries
if ($LogEntry.Count -gt 0) {
# Add the log entries to the log file
$LogEntries += $LogEntry
} # if there are any log entries
#endregion *** End were I copy to the new variable.
#***************************************************************************
#***************************************************************************
#region *** This is where I added code to allow me to remove the file lock.
# Remove the variable to release the evtx file lock
Remove-Variable -Name LogEntry
# Garbage collect to remove any additional memory tied to the file lock.
[GC]::Collect()
# sleep for 1 seconds
Sleep -Seconds 1
#endregion **** Code to remove the file lock.
#***************************************************************************
完成此操作后,我不再需要使用Handle.exe来关闭文件了。
谢谢,遗憾的是根据句柄问题的文件没有打开,所以这并没有帮助我。 :(但它确实向我展示了如何在交互式/不可读的输出方式中使用Handle,所以请注意这一点。再次感谢。 – 2014-09-03 16:34:27
所以,只需要清楚,你可以在函数中添加一行, Close-LockedFile $ log'就在你的'Remove-Item $ log'这行之前?或者你只是在脚本的外部看着Handle的文件,并且看不到任何东西锁定它们?不批评,只是想在同一页面上。 – TheMadTechnician 2014-09-03 17:24:07
昨天我在处理Handle,并没有显示任何文件被锁定,而PS窗口反复吐出文件中的使用错误;我没有尝试将此代码添加到上面的脚本中,但是我尝试过代码在不同的脚本中,并且还没有打开关于打开文件的任何阻塞错误,这在我之前一直都有。如果你想知道,我不介意测试上述内容吗? – 2014-09-03 17:49:25