Rotate Windows Eventlogs

  • By Lehbot
  • Sat 06 January 2018

Hi,

as i tried to establish a reasonable way to write my own eventlogs i stumbled across eventlog rotation.

There was a script that established a way of copying the evtx-files from $env:systemdrive\windows\system32\winevt\logs* to somewhere else for each logfile and then cleared every eventlog.

But it has several drawbacks and in this case powershell has no built-in solution, but read next:

I found three ways to rotate the EventLogs:

  1. Copy away the .evtx file of the corresponding eventlog and then do a clear -> Copy-Eventlog

  2. Use wevtutil cl $logname /bu:$Path -> wevtutil

  3. Use .NET [System.Diagnostics.Eventing.Reader.EventLogSession]::GlobalSession.ExportLogAndMessages("$Logname",'LogName','*',"$Path") [System.Diagnostics.Eventing.Reader.EventLogSession]::GlobalSession.ClearLog("$Logname") -> .NET

In the first scenario i recognized the biggest gap between exporting and clearing the log -> EventLogGap

Randomly i had no events in the exported file, if i start with a fresh eventlog and also generate some events before i export and delete them. It seems that events are not written directly into the evtx-file but somewhere in a buffer -> EventLogNirvana

So we have two objectives to cover:

  • EventLogGap gets bigger, as the file grows or the content in the file is, which is more likely to be when using the way of "Copy-Eventlog"

  • EventLogNirvana seems also just to happen, when copying the underlying .evtx file, which has not been written completely

Conclusion would be to use wevtutil as this seems to be the only reliable method. The .NET-Method produced some unneccessary additional entries for each eventlog entry.

Followed is a script is use to rotate eventlogs:

Function Rotate-Eventlog($Retentiondays=7)
{
    <#
    .Synopsis
    Copies Eventlogfiles for each eventlog into a folder.

    .Inputs
    Eventlogname and Source is necessary to create one.

    .Parameter $Retentiondays=7
    Is the time that logs are kept before deleted
    #>

    Begin
    {
        $strExportPath = $env:WinDir + "\system32\Logfiles\EventLogs\"

        $DateTime = Get-Date -format "yyyyMMdd"

        $DateToCompare = (Get-Date).AddDays(-$Retentiondays)

        ###### READ ALL EXISTING EVENT-LOGS ######

        $Array_AllEventLogs = (Get-WinEvent -ListLog *) | Where {$_.isenabled -eq $true}
    }

    Process
    {
        ###### CLEANUP FOR EACH EVENTLOG ######
        Try {
            foreach ($item in $Array_AllEventLogs)
            {
                $CorrectedFileNameTarget = $item.Logname.Replace("/","-")

                ###### CREATE BACKUP-FOLDER IF IT DOES NOT EXIST ######

                if (!(Test-Path -path "$strExportPath\$CorrectedFileNameTarget"))
                {
                    New-Item "$strExportPath\$CorrectedFileNameTarget" -type directory
                }

                Try {
                    If (Test-Path -path "$($strExportPath+"\"+$CorrectedFileNameTarget+"\"+$CorrectedFileNameTarget+"-"+$DateTime+".evtx")"){
                        Remove-Item -Path $($strExportPath+"\"+$CorrectedFileNameTarget+"\"+$CorrectedFileNameTarget+"-"+$DateTime+".evtx") -Force -ErrorAction Stop
                    }
                }
                Catch{
                    Throw "Could not delete $($strExportPath+"\"+$CorrectedFileNameTarget+"\"+$CorrectedFileNameTarget+"-"+$DateTime+".evtx") prior to export the Log $($item.Logname)"
                }
                #Clear Event Log and backup it via wevtutil.
                Try {
                    & $env:Systemroot\system32\wevtutil.exe cl $($item.logname) /bu:$($strExportPath+"\"+$CorrectedFileNameTarget+"\"+$CorrectedFileNameTarget+"-"+$DateTime+".evtx")
                }
                Catch{
                    Throw "Error executing wevtutil.exe on log $($item.logname). Message: $PSItem"
                }

                ###### CLEANUP EVENT-LOG BACKUP-FOLDER ######

                cd "$strExportPath\$CorrectedFileNameTarget"

                Get-Childitem -recurse | where-object {$_.lastwritetime -lt $DateToCompare -and $_.PSisContainer -eq $false} | Remove-Item -recurse -force -ErrorAction Stop

                [Array]$ProcessedLogfiles += "`n" + $($item.Logname)
            }
            $Return = "Successfully rotated eventlogs $ProcessedLogfiles"
        }
        Catch{
            Throw "An Error occured while processing $($item.Logname). Message: $PSItem"
        }
    }
    End{
        Return $Return
        Remove-Variable $ProcessedLogfiles
        Remove-Variable $Return
    }
}