Cost & Ingest
Ingest Trends
30-60-90-day-common-security-log-ingest-trends.kql
Compare `CommonSecurityLog` volume by `DeviceAction` across 30-, 60-, and 90-day periods to spot which actions are driving growth.
// Author: Ian D. Hanley (DevSecOpsDad) | linkedin.com/in/ianhanley | devsecopsdad.com | devsecopsdadattack.com
// Compare CommonSecurityLog ingest trends by DeviceAction across 30, 60, and 90 day periods
// This query analyzes security log volume changes to identify trends and cost impacts
// Period 1: 60-90 days ago (oldest period)
let Period90Days = CommonSecurityLog
| where TimeGenerated > ago(90d) and TimeGenerated <= ago(60d) // Filter to 60-90 days ago
| extend DeviceAction = tostring(coalesce(DeviceAction, "Unknown")) // Handle null DeviceAction values
| summarize Period90Count = count(), Period90GB = round(todouble(count() * 0.5) / 1024, 2) by DeviceAction; // Count events and estimate GB (0.5KB per event)
// Period 2: 30-60 days ago (middle period)
let Period60Days = CommonSecurityLog
| where TimeGenerated > ago(60d) and TimeGenerated <= ago(30d) // Filter to 30-60 days ago
| extend DeviceAction = tostring(coalesce(DeviceAction, "Unknown")) // Handle null DeviceAction values
| summarize Period60Count = count(), Period60GB = round(todouble(count() * 0.5) / 1024, 2) by DeviceAction; // Count events and estimate GB
// Period 3: Last 30 days (most recent period)
let Period30Days = CommonSecurityLog
| where TimeGenerated > ago(30d) // Filter to last 30 days
| extend DeviceAction = tostring(coalesce(DeviceAction, "Unknown")) // Handle null DeviceAction values
| summarize Period30Count = count(), Period30GB = round(todouble(count() * 0.5) / 1024, 2) by DeviceAction; // Count events and estimate GB
// Join all three periods together to compare trends
Period90Days
| join kind=fullouter Period60Days on DeviceAction // Full outer join to include all DeviceActions from both periods
| join kind=fullouter Period30Days on DeviceAction // Full outer join to include all DeviceActions from all periods
| extend
// Ensure we have a single DeviceAction column after joins
DeviceAction = coalesce(DeviceAction, DeviceAction1, DeviceAction2),
// Fill null values with 0 for calculations
Period90Count = coalesce(Period90Count, 0),
Period60Count = coalesce(Period60Count, 0),
Period30Count = coalesce(Period30Count, 0),
Period90GB = coalesce(Period90GB, 0.0),
Period60GB = coalesce(Period60GB, 0.0),
Period30GB = coalesce(Period30GB, 0.0)
| extend
// Calculate absolute changes in event counts between periods
Change60to30Count = Period30Count - Period60Count, // Change from 30-60d ago to last 30d
Change90to60Count = Period60Count - Period90Count, // Change from 60-90d ago to 30-60d ago
Change90to30Count = Period30Count - Period90Count, // Change from 60-90d ago to last 30d (total change)
// Calculate absolute changes in GB between periods
Change60to30GB = Period30GB - Period60GB,
Change90to60GB = Period60GB - Period90GB,
Change90to30GB = Period30GB - Period90GB,
// Calculate percentage changes (handle division by zero)
ChangePct60to30 = iff(Period60Count > 0, round(((todouble(Period30Count) - todouble(Period60Count)) / todouble(Period60Count)) * 100, 1), iff(Period30Count > 0, 100.0, 0.0)),
ChangePct90to60 = iff(Period90Count > 0, round(((todouble(Period60Count) - todouble(Period90Count)) / todouble(Period90Count)) * 100, 1), iff(Period60Count > 0, 100.0, 0.0)),
ChangePct90to30 = iff(Period90Count > 0, round(((todouble(Period30Count) - todouble(Period90Count)) / todouble(Period90Count)) * 100, 1), iff(Period30Count > 0, 100.0, 0.0))
| extend
// Classify the trend pattern based on volume changes across periods
TrendDirection = case(
Period30Count > Period60Count and Period60Count > Period90Count, "Increasing", // Consistent growth
Period30Count < Period60Count and Period60Count < Period90Count, "Decreasing", // Consistent decline
Period30Count > Period60Count and Period60Count < Period90Count, "Recovery", // Dipped then recovered
Period30Count < Period60Count and Period60Count > Period90Count, "Peak and Drop", // Peaked then dropped
"Variable" // All other patterns
)
| project
// Output columns with descriptive names
DeviceAction = DeviceAction, // The security device action type
Period90DaysCount = Period90Count, // Event count 60-90 days ago
Period60DaysCount = Period60Count, // Event count 30-60 days ago
Period30DaysCount = Period30Count, // Event count last 30 days
Period90DaysGB = Period90GB, // Estimated GB 60-90 days ago
Period60DaysGB = Period60GB, // Estimated GB 30-60 days ago
Period30DaysGB = Period30GB, // Estimated GB last 30 days
Change30vs60Count = Change60to30Count, // Count change from 30-60d to last 30d
Change30vs60Pct = ChangePct60to30, // Percentage change from 30-60d to last 30d
Change60vs90Count = Change90to60Count, // Count change from 60-90d to 30-60d
Change60vs90Pct = ChangePct90to60, // Percentage change from 60-90d to 30-60d
Change30vs90Count = Change90to30Count, // Total count change from 60-90d to last 30d
Change30vs90Pct = ChangePct90to30, // Total percentage change from 60-90d to last 30d
TrendPattern = TrendDirection, // Classified trend pattern
CostImpact30vs60 = strcat("$", round(Change60to30GB * 5.16, 2)), // Cost impact of recent change ($5.16/GB)
CostImpact30vs90 = strcat("$", round(Change90to30GB * 5.16, 2)) // Cost impact of total change ($5.16/GB)
| where Period30DaysCount > 0 or Period60DaysCount > 0 or Period90DaysCount > 0 // Only show DeviceActions with activity
| order by abs(Change30vs90Count) desc // Sort by largest absolute change over 90 days