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