How to Set Up Log Analytics with Windows Event Logs on Windows Server 2025
Windows Event Logs are the primary audit trail for security events, application errors, and system state changes on any Windows Server. Collecting those logs into an Azure Log Analytics workspace centralizes your data, enables powerful Kusto Query Language (KQL) searches, and lays the foundation for a full SIEM using Microsoft Sentinel. Windows Server 2025 pairs excellently with the modern Azure Monitor Agent (AMA), which replaces the legacy Log Analytics agent (MMA) and introduces granular, channel-level filtering through Data Collection Rules (DCRs). This tutorial covers every step from workspace creation through Sentinel enablement, including XPath event filtering and advanced KQL analytics.
Prerequisites
- Windows Server 2025 connected to Azure Arc (see the Azure Monitor tutorial for Arc onboarding steps)
- Azure Monitor Agent (AMA) deployed to the Arc-connected server
- Azure subscription with Log Analytics Contributor and Microsoft Sentinel Contributor roles
- PowerShell Az module (
Az.OperationalInsights,Az.Monitor) installed on your admin workstation - Outbound HTTPS to
*.ods.opinsights.azure.comand*.oms.opinsights.azure.com - At least 90 days log retention planned (recommended for security compliance)
Step 1: Create the Log Analytics Workspace
If you already have a workspace, skip to Step 2. Otherwise, create a dedicated workspace for security log collection. It is best practice to separate security logs from operational monitoring logs to control costs and apply independent RBAC.
Import-Module Az.OperationalInsights
Connect-AzAccount
Set-AzContext -SubscriptionId "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$workspace = New-AzOperationalInsightsWorkspace `
-ResourceGroupName "rg-security" `
-Name "law-security-ws2025" `
-Location "eastus" `
-Sku "PerGB2018" `
-RetentionInDays 180 # 180 days for compliance requirements
Write-Host "Workspace ID : $($workspace.CustomerId)"
Write-Host "Workspace RID : $($workspace.ResourceId)"
# Enable immutable archival for compliance (90 days hot + 90 days archive)
$workspaceRid = $workspace.ResourceId
$token = (Get-AzAccessToken).Token
$archiveBody = @{
properties = @{
retentionInDays = 90
totalRetentionInDays = 180
}
} | ConvertTo-Json
Invoke-RestMethod `
-Method Patch `
-Uri "https://management.azure.com$workspaceRid`?api-version=2023-09-01" `
-Headers @{ Authorization = "Bearer $token"; "Content-Type" = "application/json" } `
-Body $archiveBody
Step 2: Configure a DCR to Collect Windows Event Log Channels
Data Collection Rules are the AMA’s instruction set. You specify which event channels to collect using W3C XPath 1.0 queries. The three most important channels for security are Security, System, and Application.
$subId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$rg = "rg-security"
$workspaceRid = $workspace.ResourceId
$token = (Get-AzAccessToken).Token
$dcrName = "dcr-security-eventlogs"
$dcrBody = @{
location = "eastus"
properties = @{
dataSources = @{
windowsEventLogs = @(
@{
name = "securityChannel"
streams = @("Microsoft-SecurityEvent")
xPathQueries = @(
# All Audit Success and Audit Failure events in Security log
"Security!*[System[(band(Keywords,13510798882111488))]]"
)
}
@{
name = "systemChannel"
streams = @("Microsoft-Event")
xPathQueries = @(
# Critical, Error, and Warning events from System
"System!*[System[(Level=1 or Level=2 or Level=3)]]"
)
}
@{
name = "applicationChannel"
streams = @("Microsoft-Event")
xPathQueries = @(
# Critical and Error events from Application
"Application!*[System[(Level=1 or Level=2)]]"
)
}
)
}
destinations = @{
logAnalytics = @(
@{
workspaceResourceId = $workspaceRid
name = "securityWorkspace"
}
)
}
dataFlows = @(
@{
streams = @("Microsoft-SecurityEvent")
destinations = @("securityWorkspace")
outputStream = "Microsoft-SecurityEvent"
}
@{
streams = @("Microsoft-Event")
destinations = @("securityWorkspace")
outputStream = "Microsoft-Event"
}
)
}
} | ConvertTo-Json -Depth 10
Invoke-RestMethod `
-Method Put `
-Uri "https://management.azure.com/subscriptions/$subId/resourceGroups/$rg/providers/Microsoft.Insights/dataCollectionRules/$dcrName`?api-version=2022-06-01" `
-Headers @{ Authorization = "Bearer $token"; "Content-Type" = "application/json" } `
-Body $dcrBody
Step 3: Advanced XPath Filtering — Specific Event IDs Only
Collecting every Security event can generate millions of records per day and drive up costs. Use XPath predicates to filter to only the event IDs you care about. The most security-relevant IDs for logon auditing are 4624 (successful logon) and 4625 (failed logon).
# Targeted DCR for logon events only
$dcrNameTargeted = "dcr-logon-events"
$targetedDcrBody = @{
location = "eastus"
properties = @{
dataSources = @{
windowsEventLogs = @(
@{
name = "logonEventsSource"
streams = @("Microsoft-SecurityEvent")
xPathQueries = @(
# Logon success (4624) and failure (4625)
"Security!*[System[(EventID=4624 or EventID=4625)]]"
# Account lockout (4740) and password change (4723, 4724)
"Security!*[System[(EventID=4740 or EventID=4723 or EventID=4724)]]"
# Privilege use — sensitive privilege (4673) and special logon (4672)
"Security!*[System[(EventID=4672 or EventID=4673)]]"
# Process creation with command line (4688) — requires audit policy
"Security!*[System[(EventID=4688)]]"
)
}
)
}
destinations = @{
logAnalytics = @(
@{
workspaceResourceId = $workspaceRid
name = "securityWorkspace"
}
)
}
dataFlows = @(
@{
streams = @("Microsoft-SecurityEvent")
destinations = @("securityWorkspace")
outputStream = "Microsoft-SecurityEvent"
}
)
}
} | ConvertTo-Json -Depth 10
Invoke-RestMethod `
-Method Put `
-Uri "https://management.azure.com/subscriptions/$subId/resourceGroups/$rg/providers/Microsoft.Insights/dataCollectionRules/$dcrNameTargeted`?api-version=2022-06-01" `
-Headers @{ Authorization = "Bearer $token"; "Content-Type" = "application/json" } `
-Body $targetedDcrBody
# Test XPath filter locally on the server before deploying the DCR
# Run this ON the Windows Server 2025 machine:
$xpath = "*[System[(EventID=4624 or EventID=4625)]]"
Get-WinEvent -LogName Security -FilterXPath $xpath -MaxEvents 10 |
Select-Object TimeCreated, Id, Message
Step 4: Associate the DCR with the Arc-Connected Server
$machineId = "/subscriptions/$subId/resourceGroups/$rg/providers/Microsoft.HybridCompute/machines/WS2025-SRV01"
$dcrId = "/subscriptions/$subId/resourceGroups/$rg/providers/Microsoft.Insights/dataCollectionRules/$dcrNameTargeted"
$assocBody = @{
properties = @{ dataCollectionRuleId = $dcrId }
} | ConvertTo-Json
Invoke-RestMethod `
-Method Put `
-Uri "https://management.azure.com$machineId/providers/Microsoft.Insights/dataCollectionRuleAssociations/dcra-logon-events?api-version=2022-06-01" `
-Headers @{ Authorization = "Bearer $token"; "Content-Type" = "application/json" } `
-Body $assocBody
# Verify association
Invoke-RestMethod `
-Method Get `
-Uri "https://management.azure.com$machineId/providers/Microsoft.Insights/dataCollectionRuleAssociations?api-version=2022-06-01" `
-Headers @{ Authorization = "Bearer $token" } |
Select-Object -ExpandProperty value |
Select-Object name, @{n="dcrId";e={$_.properties.dataCollectionRuleId}}
Step 5: Query Collected Events with KQL
After data begins flowing (typically within 10 minutes of DCR association), run KQL queries in the Log Analytics workspace. The SecurityEvent table receives data from the Microsoft-SecurityEvent stream.
Import-Module Az.OperationalInsights
$workspaceId = $workspace.CustomerId
# Failed logon attempts — count by account and computer
$failedLogonQuery = @"
SecurityEvent
| where TimeGenerated > ago(24h)
| where EventID == 4625
| summarize FailedAttempts = count() by Account, Computer, IpAddress
| where FailedAttempts > 5
| order by FailedAttempts desc
"@
Invoke-AzOperationalInsightsQuery `
-WorkspaceId $workspaceId `
-Query $failedLogonQuery |
Select-Object -ExpandProperty Results
# Successful logons after business hours (before 07:00 or after 19:00)
$afterHoursQuery = @"
SecurityEvent
| where TimeGenerated > ago(7d)
| where EventID == 4624
| where LogonType == 2 or LogonType == 10 // Interactive or RemoteInteractive
| extend Hour = datetime_part("Hour", TimeGenerated)
| where Hour 19
| project TimeGenerated, Account, Computer, LogonType, IpAddress
| order by TimeGenerated desc
"@
Invoke-AzOperationalInsightsQuery `
-WorkspaceId $workspaceId `
-Query $afterHoursQuery |
Select-Object -ExpandProperty Results
# Accounts with both successful AND failed logons (potential credential stuffing)
$bruteForceQuery = @"
let failed = SecurityEvent
| where EventID == 4625
| summarize FailCount = count() by Account, Computer;
let success = SecurityEvent
| where EventID == 4624
| summarize SuccessCount = count() by Account, Computer;
failed
| join kind=inner success on Account, Computer
| where FailCount > 10 and SuccessCount >= 1
| project Account, Computer, FailCount, SuccessCount
| order by FailCount desc
"@
Invoke-AzOperationalInsightsQuery `
-WorkspaceId $workspaceId `
-Query $bruteForceQuery |
Select-Object -ExpandProperty Results
Step 6: Enable Microsoft Sentinel over the Log Analytics Workspace
Microsoft Sentinel transforms your Log Analytics workspace into a full SIEM, adding threat intelligence, analytics rules, playbooks, and SOAR capabilities.
# Install the Sentinel solution (enable Sentinel on the workspace)
$token = (Get-AzAccessToken).Token
$workspaceRid = $workspace.ResourceId
Invoke-RestMethod `
-Method Put `
-Uri "https://management.azure.com$workspaceRid/providers/Microsoft.SecurityInsights/onboardingStates/default?api-version=2022-12-01-preview" `
-Headers @{ Authorization = "Bearer $token"; "Content-Type" = "application/json" } `
-Body (@{ properties = @{} } | ConvertTo-Json)
# Enable the Windows Security Events data connector (uses AMA)
$connectorBody = @{
kind = "WindowsSecurityEvents"
properties = @{
dataTypes = @{
windowsSecurityEvents = @{ state = "Enabled" }
}
}
} | ConvertTo-Json
Invoke-RestMethod `
-Method Put `
-Uri "https://management.azure.com$workspaceRid/providers/Microsoft.SecurityInsights/dataConnectors/WindowsSecurityEvents?api-version=2022-12-01-preview" `
-Headers @{ Authorization = "Bearer $token"; "Content-Type" = "application/json" } `
-Body $connectorBody
Step 7: KQL Parsing and Enrichment Functions
Save frequently used KQL queries as functions in Log Analytics to simplify complex analytics rules and reduce duplication.
# Save a KQL function for failed logon enrichment
$functionBody = @{
properties = @{
category = "Security"
displayName = "FailedLogonEnriched"
query = @"
SecurityEvent
| where EventID == 4625
| extend
LogonTypeName = case(
LogonType == 2, "Interactive",
LogonType == 3, "Network",
LogonType == 4, "Batch",
LogonType == 5, "Service",
LogonType == 7, "Unlock",
LogonType == 10, "RemoteInteractive",
LogonType == 11, "CachedInteractive",
"Other"
),
RiskScore = case(
LogonType == 10 and Status == "0xc000006d", 80, // RDP bad password
LogonType == 3 and Status == "0xc0000064", 60, // Network bad username
30
)
| project TimeGenerated, Account, Computer, IpAddress, LogonTypeName, Status, RiskScore
"@
functionAlias = "FailedLogonEnriched"
functionParameters = ""
}
} | ConvertTo-Json -Depth 5
Invoke-RestMethod `
-Method Put `
-Uri "https://management.azure.com$workspaceRid/savedSearches/FailedLogonEnriched?api-version=2020-08-01" `
-Headers @{ Authorization = "Bearer $token"; "Content-Type" = "application/json" } `
-Body $functionBody
# After the function is saved, use it in queries:
$useFunctionQuery = @"
FailedLogonEnriched
| where TimeGenerated > ago(1h)
| where RiskScore >= 60
| summarize HighRiskCount = count() by Account, Computer, IpAddress
"@
Invoke-AzOperationalInsightsQuery `
-WorkspaceId $workspaceId `
-Query $useFunctionQuery |
Select-Object -ExpandProperty Results
Conclusion
You now have a production-grade Windows Event Log collection pipeline for Windows Server 2025. The Azure Monitor Agent collects precisely the event channels and IDs you specified via XPath filters in your Data Collection Rule, sending them to a Log Analytics workspace with appropriate retention. KQL queries surface actionable security insights—from failed logon spikes to after-hours access—and saved KQL functions reduce repetition as your analytics library grows. With Microsoft Sentinel enabled on the same workspace, you gain access to built-in threat detection analytics rules, playbooks, and incident management that elevate your environment from simple log collection to a fully operational SIEM. As your infrastructure grows, simply create additional DCR associations for new Arc-connected servers to bring them into the same collection and detection pipeline.