The Windows Security Event Log
The Windows Security event log is the primary source of security-relevant audit records on Windows Server 2022. Located under Windows Logs → Security in Event Viewer, this log records authentication events, account management changes, object access, privilege use, process activity, and policy modifications — provided the appropriate audit policies are enabled. Without comprehensive audit policies configured, many critical security events will never be generated, making the Security log appear deceptively quiet.
The Security event log can be found at the following path on disk:
C:WindowsSystem32winevtLogsSecurity.evtx
By default on Windows Server 2022, the Security log is configured with a maximum size of 20 MB and is set to overwrite events as needed when the log is full. For production security monitoring, this is completely inadequate. Increase the log size and configure it to archive old events rather than overwrite them:
# Set Security log to 1 GB maximum and archive when full
$Log = [System.Diagnostics.Eventing.Reader.EventLogConfiguration]::new("Security")
$Log.MaximumSizeInBytes = 1073741824 # 1 GB
$Log.LogMode = [System.Diagnostics.Eventing.Reader.EventLogMode]::AutoBackup
$Log.SaveChanges()
# Verify the change
wevtutil gl Security | Select-String "maxSize|logMode"
Event Viewer (eventvwr.msc) provides a GUI for browsing and filtering the Security log, but it becomes impractical for high-volume analysis. PowerShell’s Get-WinEvent cmdlet provides far more powerful querying capability and is essential for any automated security monitoring workflow.
Key Security Event IDs to Monitor
Not all security events are equally important. Focusing on a curated set of high-value event IDs produces actionable security intelligence without overwhelming analysts with noise. The following events are the most critical to monitor on Windows Server 2022.
Event ID 4624 — Logon Success: Generated whenever an account successfully logs on. The logon type field is critical for analysis: Type 2 = interactive (console), Type 3 = network, Type 4 = batch, Type 5 = service, Type 10 = remote interactive (RDP), Type 11 = cached credentials. Monitor for unexpected Type 10 events (RDP logons) from unusual source IP addresses, or Type 3 network logons to sensitive servers outside of business hours.
Event ID 4625 — Logon Failure: Generated whenever a logon attempt fails. The failure reason codes are invaluable: Sub Status 0xC000006A means wrong password, 0xC0000064 means username does not exist, 0xC000006D means general logon failure, 0xC0000234 means account is locked out. A surge in 4625 events from a single source IP indicates a brute-force attack in progress.
Event ID 4648 — Explicit Credential Use: Generated when a process explicitly uses different credentials via RunAs or CredSSP. Legitimate use is uncommon in normal server operations. Unexpected 4648 events — especially those involving administrator credentials being used from unusual processes — are a strong indicator of lateral movement or credential theft.
Event ID 4688 — Process Creation: Generated when a new process is created, provided process auditing is enabled. This event includes the full command line (when enabled via Group Policy), parent process, and the account that created the process. Monitoring for suspicious process creations — such as cmd.exe or powershell.exe spawned from unusual parent processes like w3wp.exe (IIS) or sqlservr.exe — is foundational for detecting code execution attacks.
# Enable command line logging in process creation events via registry
Set-ItemProperty `
-Path "HKLM:SOFTWAREMicrosoftWindowsCurrentVersionPoliciesSystemAudit" `
-Name "ProcessCreationIncludeCmdLine_Enabled" `
-Value 1 -Type DWord
Event ID 4697 — Service Installed in the System: Generated when a new service is installed. Attackers frequently install malicious services for persistence. Any unexpected 4697 event — especially outside of planned change windows — should be investigated immediately. Note that Event ID 7045 from the System log (not Security) also records service installations and should be monitored in parallel.
Event ID 4720 — User Account Created: Generated when a new local or domain user account is created. Rogue account creation is a common persistence technique. Monitor for accounts created outside of your provisioning process.
Event IDs 4728, 4732, 4756 — Group Membership Changes: Generated when a member is added to a security-enabled global group (4728), local group (4732), or universal group (4756). Adding accounts to privileged groups like Domain Admins, Administrators, or Remote Desktop Users outside of approved processes is a critical indicator of privilege escalation.
Event ID 4740 — Account Locked Out: Generated on the domain controller that processed the lockout when a user account is locked out. This event identifies the source machine causing the lockout, which is critical for diagnosing both legitimate lockouts (stale credential in cached application) and brute-force attacks.
Querying Security Events with Get-WinEvent
Get-WinEvent with XPath or hashtable filter syntax is the most efficient way to query the Security event log programmatically. The -FilterHashtable parameter is simpler for basic queries; -FilterXPath allows more complex conditions.
# Query recent logon failures (4625) - last 24 hours
$StartTime = (Get-Date).AddHours(-24)
Get-WinEvent -FilterHashtable @{
LogName = "Security"
Id = 4625
StartTime = $StartTime
} | Select-Object TimeCreated, Message | Format-List
# Count logon failures by source IP to detect brute force
Get-WinEvent -FilterHashtable @{
LogName = "Security"
Id = 4625
StartTime = (Get-Date).AddHours(-1)
} | ForEach-Object {
$xml = [xml]$_.ToXml()
$SourceIP = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq "IpAddress"}).'#text'
$UserName = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq "TargetUserName"}).'#text'
[PSCustomObject]@{ SourceIP = $SourceIP; UserName = $UserName }
} | Group-Object SourceIP | Sort-Object Count -Descending | `
Select-Object -First 20 Name, Count
# Find all RDP logons (Type 10) in the last 7 days
Get-WinEvent -FilterHashtable @{
LogName = "Security"
Id = 4624
StartTime = (Get-Date).AddDays(-7)
} | Where-Object {
$xml = [xml]$_.ToXml()
$LogonType = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq "LogonType"}).'#text'
$LogonType -eq "10"
} | ForEach-Object {
$xml = [xml]$_.ToXml()
[PSCustomObject]@{
TimeCreated = $_.TimeCreated
UserName = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq "TargetUserName"}).'#text'
SourceIP = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq "IpAddress"}).'#text'
}
} | Format-Table -AutoSize
Creating Custom Views in Event Viewer
For administrators who prefer the GUI, Event Viewer’s Custom Views feature allows saving frequently used filters as named views that persist across sessions. Custom views are stored as XML filter definitions and can be exported and deployed to multiple servers via Group Policy Preferences.
To create a custom view for security monitoring in Event Viewer: in the Actions pane, click “Create Custom View.” Select “By log” and choose “Security.” In the Event IDs field, enter the IDs you want to monitor, separated by commas or spaces:
4624 4625 4648 4688 4697 4720 4728 4732 4740 4756 7045
Custom views can also be created programmatically by deploying an XML view definition to the appropriate location. Deploy custom views via Group Policy to all servers in your environment:
# Custom View XML for Security Monitoring
$CustomViewXML = @"
Security Monitoring - Critical Events
*[System[(EventID=4624 or EventID=4625 or EventID=4648 or EventID=4688
or EventID=4697 or EventID=4720 or EventID=4728 or EventID=4732
or EventID=4740 or EventID=4756)]]
*[System[EventID=7045]]
"@
$ViewPath = "$env:ProgramDataMicrosoftEvent ViewerViewsSecurityMonitoring.xml"
$CustomViewXML | Out-File -FilePath $ViewPath -Encoding UTF8
Write-Host "Custom view deployed to $ViewPath"
Exporting and Parsing Security Logs with PowerShell XML
For offline analysis, evidence preservation, or feeding events to an external system, export the Security log to XML format which preserves all structured event data:
# Export all 4625 events from the last 24 hours to XML
$ExportPath = "C:SecurityLogs$(Get-Date -Format 'yyyyMMdd_HHmmss')_logon_failures.xml"
New-Item -ItemType Directory -Path (Split-Path $ExportPath) -Force | Out-Null
$Events = Get-WinEvent -FilterHashtable @{
LogName = "Security"
Id = 4625
StartTime = (Get-Date).AddHours(-24)
} -ErrorAction SilentlyContinue
# Save as structured XML
$Events | ForEach-Object { $_.ToXml() } | `
Out-File -FilePath $ExportPath -Encoding UTF8
Write-Host "Exported $($Events.Count) events to $ExportPath"
# Parse the exported XML for analysis
$ParsedEvents = Get-Content $ExportPath | ForEach-Object {
if ($_ -match "<Event ") {
$xml = [xml]$_
$Data = $xml.Event.EventData.Data
[PSCustomObject]@{
Time = $xml.Event.System.TimeCreated.SystemTime
EventID = $xml.Event.System.EventID
UserName = ($Data | Where-Object {$_.Name -eq "TargetUserName"}).'#text'
Domain = ($Data | Where-Object {$_.Name -eq "TargetDomainName"}).'#text'
SourceIP = ($Data | Where-Object {$_.Name -eq "IpAddress"}).'#text'
SubStatus = ($Data | Where-Object {$_.Name -eq "SubStatus"}).'#text'
LogonType = ($Data | Where-Object {$_.Name -eq "LogonType"}).'#text'
}
}
}
$ParsedEvents | Export-Csv -Path ($ExportPath -replace '.xml','.csv') -NoTypeInformation
Write-Host "Parsed events exported to CSV"
Forwarding Security Events to a SIEM
For enterprise environments, individual server log analysis is insufficient. Security events must be centrally collected in a Security Information and Event Management (SIEM) system for correlation, alerting, and long-term retention. Windows Server 2022 supports two primary log forwarding mechanisms.
Windows Event Forwarding (WEF): A built-in, agentless mechanism where Windows Event Collectors receive events pushed from subscribing computers. Configure a WEF collector server and deploy subscription configuration via Group Policy:
# On the WEF Collector server - configure and start the WEC service
wecutil qc /q
# On source servers - configure WinRM for event forwarding
winrm quickconfig -q
netsh advfirewall firewall add rule name="WinRM-HTTP" dir=in localport=5985 protocol=TCP action=allow
# The subscription XML defines what events to forward and to which collector
# Deploy via: wecutil cs subscription.xml
Log Analytics Agent / Azure Monitor Agent: For organizations using Microsoft Sentinel or Azure Monitor, the Log Analytics Agent (MMA) or the newer Azure Monitor Agent (AMA) installed on each server streams events directly to a Log Analytics workspace. This is covered in more detail in the Azure Sentinel integration article. For non-Azure SIEMs (Splunk, Elastic, QRadar), vendor-specific forwarder agents are installed on each monitored server.
Regardless of the forwarding mechanism, establish a log retention policy. Security best practices and many compliance frameworks (PCI-DSS, SOC 2, HIPAA) require security log retention of at least 12 months, with at least 3 months immediately accessible for investigation. Configure your SIEM or log storage platform accordingly.
Event Log Monitoring Best Practices for Windows Server 2022
Effective security event monitoring requires more than just enabling audit policies and collecting logs. To get actionable value from the Security event log, follow these practices: establish a baseline of normal activity for each server role so that deviations are immediately obvious; configure alerting thresholds (e.g., more than 10 event 4625 occurrences from one IP in 5 minutes triggers an alert); correlate events across multiple sources — a 4624 on a server should correspond to a matching Kerberos authentication event on the DC; regularly review and tune your detection rules to reduce false positives; and ensure all servers have accurate time synchronization (W32tm /query /status) because event correlation depends on consistent timestamps across systems.
# Verify time synchronization status on all servers
w32tm /query /status
w32tm /query /source
# Force time sync if needed
w32tm /resync /force