如何解析和删除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中,你可能想调整函数中的路径,或者在那里保存可执行文件。

+0

谢谢,遗憾的是根据句柄问题的文件没有打开,所以这并没有帮助我。 :(但它确实向我展示了如何在交互式/不可读的输出方式中使用Handle,所以请注意这一点。再次感谢。 – 2014-09-03 16:34:27

+0

所以,只需要清楚,你可以在函数中添加一行, Close-LockedFile $ log'就在你的'Remove-Item $ log'这行之前?或者你只是在脚本的外部看着Handle的文件,并且看不到任何东西锁定它们?不批评,只是想在同一页面上。 – TheMadTechnician 2014-09-03 17:24:07

+0

昨天我在处理Handle,并没有显示任何文件被锁定,而PS窗口反复吐出文件中的使用错误;我没有尝试将此代码添加到上面的脚本中,但是我尝试过代码在不同的脚本中,并且还没有打开关于打开文件的任何阻塞错误,这在我之前一直都有。如果你想知道,我不介意测试上述内容吗? – 2014-09-03 17:49:25

我有一个非常类似的问题,经过大量的搜索发现这篇文章。虽然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来关闭文件了。