Introduction to Event Log Management on Windows Server 2019

Windows Event Log is the centralized logging system in Windows Server 2019 that records system events from the operating system, applications, security subsystem, and applications. Effective event log management involves configuring appropriate log sizes, setting up log forwarding for centralized collection, filtering events for analysis, creating automated responses to specific events, and archiving logs for compliance. Windows Server 2019 stores event logs in the EVTX format in C:WindowsSystem32winevtLogs. The Windows Event Forwarding (WEF) mechanism allows collecting events from multiple servers into a central Windows Event Collector server without requiring third-party agents, making it an excellent foundation for a lightweight SIEM infrastructure.

Event Log Architecture and Key Logs

Windows Server 2019 organizes event logs into several categories. Windows Logs include Application (application errors and information), Security (authentication, authorization, and audit events), System (OS component events), Setup (installation events), and Forwarded Events (events from remote machines). Applications and Services Logs contain logs for specific Windows roles and features such as DNS Server, DHCP Server, Hyper-V, PowerShell, and Task Scheduler. List all available event logs:

Get-WinEvent -ListLog * | Where-Object {$_.RecordCount -gt 0} | Select-Object LogName, RecordCount, MaximumSizeInBytes, FileSize | Sort-Object RecordCount -Descending | Format-Table -AutoSize
wevtutil el | findstr -i "security|system|application|powershell"

Configuring Event Log Size and Retention

Default event log sizes are often too small for enterprise security monitoring. Increase log sizes to retain enough history for incident response. The Security log should be at least 1 GB for active domain controllers:

wevtutil sl Security /ms:1073741824    # 1 GB
wevtutil sl System /ms:524288000       # 500 MB
wevtutil sl Application /ms:524288000  # 500 MB
wevtutil sl "Windows PowerShell" /ms:209715200  # 200 MB
wevtutil sl "Microsoft-Windows-PowerShell/Operational" /ms:524288000  # 500 MB
wevtutil sl "Microsoft-Windows-Sysmon/Operational" /ms:1073741824  # 1 GB if Sysmon is installed

Using PowerShell to set log sizes for all critical logs:

$logSettings = @{
    "Security" = 1GB
    "System" = 512MB
    "Application" = 512MB
    "Microsoft-Windows-PowerShell/Operational" = 256MB
    "Microsoft-Windows-TaskScheduler/Operational" = 128MB
    "Microsoft-Windows-Windows Defender/Operational" = 128MB
}
foreach ($log in $logSettings.Keys) {
    Limit-EventLog -LogName $log -MaximumSize $logSettings[$log] -OverflowAction OverwriteAsNeeded -ErrorAction SilentlyContinue
    # For Applications and Services logs use wevtutil
    wevtutil sl $log /ms:$($logSettings[$log])
}

Setting Up Windows Event Forwarding

Windows Event Forwarding (WEF) collects events from multiple source computers to a central Windows Event Collector (WEC) server without third-party agents. Set up the collector server first:

winrm quickconfig -quiet
wecutil qc -quiet
# Enable the Windows Event Collector service
Set-Service wecsvc -StartupType Automatic
Start-Service wecsvc

Configure source computers to forward events using Group Policy. Navigate to Computer Configuration > Windows Settings > Security Settings > System Services and ensure Windows Remote Management (WinRM) is set to Automatic. Then in Computer Configuration > Administrative Templates > Windows Components > Event Forwarding, configure the SubscriptionManager URL:

# GPO setting: SubscriptionManager URL
# Server=http://WECSERVER01:5985/wsman/SubscriptionManager/WEC,Refresh=60

On source computers, configure WinRM to allow event forwarding:

winrm quickconfig -quiet
netsh advfirewall firewall add rule name="WinRM HTTP" dir=in action=allow protocol=tcp localport=5985

Creating WEF Subscriptions

On the WEC server, create subscriptions that define which events to collect from source computers. Create a security subscription collecting critical security events:

wecutil cs SecuritySubscription.xml

The SecuritySubscription.xml file defines the subscription. Example content for collecting critical security events:



  SecurityEvents
  SourceInitiated
  Critical Security Events
  true
  http://schemas.microsoft.com/wbem/wsman/1/windows/EventLog
  Custom
  900000
  <![CDATA[*[System[(EventID=4624 or EventID=4625 or EventID=4648 or EventID=4720 or EventID=4728 or EventID=4740 or EventID=4756)]]]]>
  false
  HTTP
  RenderedText
  
  ForwardedEvents
  O:NSG:NSD:(A;;GA;;;DC)

Querying Events with PowerShell

Use Get-WinEvent to query event logs with powerful filtering capabilities. Filter by multiple criteria simultaneously:

Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4625; StartTime=(Get-Date).AddHours(-24)} -MaxEvents 100 | Select-Object TimeCreated, Id, Message | Format-List
Get-WinEvent -FilterHashtable @{LogName='System'; Level=2; StartTime=(Get-Date).AddDays(-7)} | Select-Object TimeCreated, ProviderName, Id, Message
Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName='MSSQLSERVER'; Level=1,2,3; StartTime=(Get-Date).AddDays(-1)}

Use XPath queries for complex filtering combining multiple fields:

$xpathQuery = "*[System[(EventID=4624 or EventID=4625)] and EventData[Data[@Name='LogonType']='3']]"
Get-WinEvent -LogName Security -FilterXPath $xpathQuery -MaxEvents 50 | Select-Object TimeCreated, Message

Exporting and Archiving Event Logs

Archive event logs regularly for compliance and historical analysis. Export logs to EVTX files for archiving:

$archivePath = "\FileServer01EventLogArchive$env:COMPUTERNAME$(Get-Date -Format yyyyMM)"
New-Item -ItemType Directory -Path $archivePath -Force
wevtutil epl Security "$archivePathSecurity_$(Get-Date -Format yyyyMMdd_HHmmss).evtx"
wevtutil epl System "$archivePathSystem_$(Get-Date -Format yyyyMMdd_HHmmss).evtx"
wevtutil epl Application "$archivePathApplication_$(Get-Date -Format yyyyMMdd_HHmmss).evtx"

Create a scheduled task to archive and clear logs monthly:

$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-NonInteractive -File C:ScriptsArchive-EventLogs.ps1"
$trigger = New-ScheduledTaskTrigger -Monthly -DaysOfMonth 1 -At 2am
Register-ScheduledTask -TaskName "Monthly Log Archive" -Action $action -Trigger $trigger -RunLevel Highest -User "System"

Monitoring Event Logs with Task Scheduler

Create automated responses to specific events using Task Scheduler event-based triggers. For example, trigger an alert script when a critical service fails (Event ID 7034 in System log):

$trigger = New-ScheduledTaskTrigger -AtStartup
$eventTrigger = New-ScheduledTaskTrigger -AtEvent -LogName System -Source Service_Control_Manager -EventId 7034
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-File C:ScriptsAlert-ServiceFailure.ps1"
Register-ScheduledTask -TaskName "Alert on Service Failure" -Trigger $eventTrigger -Action $action -RunLevel Highest -User "SYSTEM"

Using the GUI, in Task Scheduler create a new task, select the Triggers tab, create a new trigger On an Event, specify the Log (System), Source (Service Control Manager), and Event ID (7034).

Analyzing Event Logs for Security Events

Create a comprehensive security event summary report from event logs:

$reportPath = "C:ReportsSecurityEventSummary_$(Get-Date -Format yyyyMMdd).txt"
$startTime = (Get-Date).AddDays(-7)
"=== Security Event Summary: Last 7 Days ===" | Out-File $reportPath
"Generated: $(Get-Date)" | Add-Content $reportPath
"" | Add-Content $reportPath
"Failed Logons (4625):" | Add-Content $reportPath
Get-WinEvent -FilterHashtable @{LogName='Security';Id=4625;StartTime=$startTime} | Measure-Object | Select-Object -ExpandProperty Count | ForEach-Object {"  Count: $_"} | Add-Content $reportPath
"Successful Logons (4624) by type:" | Add-Content $reportPath
Get-WinEvent -FilterHashtable @{LogName='Security';Id=4624;StartTime=$startTime} | Group-Object -Property {([xml]$_.ToXml()).Event.EventData.Data | Where-Object {$_.Name -eq 'LogonType'} | Select-Object -ExpandProperty '#text'} | Select-Object Name, Count | Format-Table | Out-String | Add-Content $reportPath
"Account Lockouts (4740):" | Add-Content $reportPath
(Get-WinEvent -FilterHashtable @{LogName='Security';Id=4740;StartTime=$startTime} -ErrorAction SilentlyContinue | Measure-Object).Count | ForEach-Object {"  Count: $_"} | Add-Content $reportPath
Get-Content $reportPath