// Log-source readiness for hunting ARTEX-style intrusions (The Tokyo Nomad, 4 Nov 2026). // Read-only. Not executed in a live tenant; checked with Microsoft's Kusto parser against Microsoft Learn schemas. // Usage gives 90 days of ingestion history per table cheaply; a one-hour scan adds freshness and delay. // MISSING = no ingestion in 90 days. STALE = nothing in the last 24 hours. Edit Expected to match your estate. // Defender XDR tables reach Sentinel only through the XDR connector; without it they stay in Defender for 30 days. let Expected = datatable(Layer:string, DataType:string, UsedFor:string)[ "Identity", "SigninLogs", "Password spray and sign-in sources", "Identity", "AADNonInteractiveUserSignInLogs", "Token use outside interactive sign-ins", "Identity", "AADServicePrincipalSignInLogs", "App and service-principal sign-ins", "Identity", "AuditLogs", "Credential, role, owner and consent changes", "Identity", "MicrosoftGraphActivityLogs", "Graph calls by apps; not kept unless exported", "Cloud", "AzureActivity", "Subscription control-plane changes", "Cloud", "AZKVAuditLogs", "Key Vault secret reads and key operations", "Cloud", "OfficeActivity", "Mailbox, SharePoint and Teams access", "Cloud", "CloudAppEvents", "OAuth app activity", "Web", "W3CIISLog", "IIS requests with user agent", "Web", "AppServiceHTTPLogs", "App Service requests with user agent", "Web", "AGWAccessLogs", "Application Gateway requests with user agent", "Web", "AGWFirewallLogs", "Application Gateway WAF matches", "Network", "CommonSecurityLog", "Firewall, proxy or WAF events (CEF)", "Endpoint", "DeviceProcessEvents", "Process lineage on servers", "Endpoint", "DeviceNetworkEvents", "Listening ports and outbound connections", "Endpoint", "DeviceFileEvents", "Web shells and dropped tools", "Endpoint", "DeviceEvents", "DNS answers and other endpoint events", "Endpoint", "DeviceInfo", "Sensor health against inventory" ]; let History = Usage | where TimeGenerated > ago(90d) | summarize FirstIngest=min(TimeGenerated), LastIngest=max(TimeGenerated), DaysWithData=dcount(bin(TimeGenerated, 1d)), TotalMB=sum(Quantity) by DataType | extend AvgDailyMB=round(TotalMB / DaysWithData, 2); let Fresh = union withsource=DataType isfuzzy=true SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs, AuditLogs, MicrosoftGraphActivityLogs, AzureActivity, AZKVAuditLogs, OfficeActivity, CloudAppEvents, W3CIISLog, AppServiceHTTPLogs, AGWAccessLogs, AGWFirewallLogs, CommonSecurityLog, DeviceProcessEvents, DeviceNetworkEvents, DeviceFileEvents, DeviceEvents, DeviceInfo | where TimeGenerated > ago(1h) | summarize Newest=max(TimeGenerated), MedianDelayMin=round(percentile(datetime_diff("second", ingestion_time(), TimeGenerated) / 60.0, 50), 1) by DataType; Expected | join kind=leftouter History on DataType | join kind=leftouter Fresh on DataType | extend Status=case(isnull(LastIngest), "MISSING", LastIngest < ago(1d), "STALE", "FLOWING") | extend Rank=case(Status == "MISSING", 0, Status == "STALE", 1, 2) | order by Rank asc, Layer asc, DataType asc | project Layer, DataType, Status, FirstIngest, LastIngest, DaysWithData, AvgDailyMB, Newest, MedianDelayMin, UsedFor | take 100