From facbcc091c9ba9f0e82af61aead7edcb0b7f0f6a Mon Sep 17 00:00:00 2001 From: spydisec <9101327+spydisec@users.noreply.github.com> Date: Wed, 2 Sep 2026 11:30:27 +1000 Subject: [PATCH 1/3] Docs: WEC Collector and Sentinel KQL pages Two generic pages for the collection and SIEM end of the chain, filling the gap between "generate the subscription" and "detections work": - WEC Collector: no default subscription exists; reading an existing collector back (wecutil export loop), subscription field anatomy, wide-open whole-channel queries (no channel wildcard exists), the two who-sends lists that drift, delivery-mode latency floors, the runtime-status three-way reconciliation, ForwardedEvents health and the classic silent failures table. - Sentinel KQL: forwarded events land in WindowsEvent (not SecurityEvent; Computer/Channel keep source-original values; payload in the EventData dynamic bag), the four-layer confirmation that AMA on a collector actually reads ForwardedEvents (DCR definition, association, local config cache, tracer event), and a nine-query KQL pack ending in the fleet three-list reconciliation. Deployment page links onward to both; nav gains the two entries after Deployment; changelog starts an Unreleased section (also noting the PowerShell 7 wording change from #23). Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 18 ++++ docs/deployment.md | 5 ++ docs/kql.md | 211 +++++++++++++++++++++++++++++++++++++++++++++ docs/wec.md | 164 +++++++++++++++++++++++++++++++++++ mkdocs.yml | 2 + 5 files changed, 400 insertions(+) create mode 100644 docs/kql.md create mode 100644 docs/wec.md diff --git a/CHANGELOG.md b/CHANGELOG.md index d32ddbb..275936a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,24 @@ All notable changes to WinLogKit. Versions follow [SemVer](https://semver.org/); releases are tagged `vX.Y.Z` and published with a zip + SHA256 checksum. +## Unreleased + +### Added +- Two docs pages for the collection and SIEM end of the chain: **WEC + Collector** (reading an existing collector: subscription anatomy, + wide-open queries, delivery modes, runtime-status reconciliation, + ForwardedEvents health, the classic silent failures) and **Sentinel + KQL** (which table forwarded events land in, the four-layer check that + AMA collects ForwardedEvents, and a query pack: fleet inventory, + direct-vs-forwarded split, silent/never-seen sources, latency, volume + attribution, collection-policy fingerprinting). + +### Changed +- Docs and CONTRIBUTING reworded so PowerShell 7 is explicitly + first-class: both engines are supported and CI-tested, Windows + PowerShell 5.1 is the compatibility floor (ships with Windows, and + Intune remediations execute under it), not the recommended shell. + ## v0.8.0 - 2026-08-31 ### Added diff --git a/docs/deployment.md b/docs/deployment.md index 25f889a..2e1da89 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -77,6 +77,11 @@ selection is the coarse filter. Graduate to curated per-event XPath queries using [Microsoft's WEF intrusion-detection guidance](https://learn.microsoft.com/windows/security/operating-system-security/device-management/use-windows-event-forwarding-to-assist-in-intrusion-detection) once you have observed real volume. +Beyond generating the subscription: the [WEC Collector](wec.md) page covers +reading and verifying an existing collector (subscription anatomy, runtime +status, the silent failures), and [Sentinel KQL](kql.md) covers the onward +hop to a SIEM workspace and the queries that prove the chain end to end. + ## GPO (domain-joined fleets) ```powershell diff --git a/docs/kql.md b/docs/kql.md new file mode 100644 index 0000000..4c87da9 --- /dev/null +++ b/docs/kql.md @@ -0,0 +1,211 @@ +# Sentinel KQL + +Verifying the last hop: a collector (or any host) shipping events to a Log +Analytics workspace with the Azure Monitor Agent (AMA), and the KQL that +proves the whole chain works. Generic Microsoft Sentinel / Azure Monitor +material - nothing here is specific to this kit, but every query assumes +the [WEC page's](wec.md) architecture: sources push to a collector's +ForwardedEvents log, AMA collects that log. + +## Which table the events land in + +Forwarded events collected by AMA land in the +[**WindowsEvent**](https://learn.microsoft.com/azure/azure-monitor/reference/tables/windowsevent) +table - that is what the +[Windows Forwarded Events connector](https://learn.microsoft.com/azure/sentinel/data-connectors/windows-forwarded-events) +configures: a +[Data Collection Rule](https://learn.microsoft.com/azure/azure-monitor/agents/data-collection-windows-events) +whose XPath list reads the `ForwardedEvents` channel. + +Three things people trip over: + +- **`SecurityEvent` is a different table.** The *Windows Security Events + via AMA* connector reads a machine's **own** Security channel into + `SecurityEvent`. Pointed at a collector, it ingests the collector's own + logs - not the forwarded fleet. For WEF you need the DCR reading + `ForwardedEvents!*`. A detection written only against `SecurityEvent` + will not see WEF-collected events (ASIM parsers union both tables - + see [normalisation](https://learn.microsoft.com/azure/sentinel/normalization)). +- **`Computer` is the original source**, not the collector. Forwarded + events keep the generating machine's name, which is what makes fleet + verification possible from the workspace end. +- **`Channel` is the original channel** (e.g. `Security`), not + `ForwardedEvents` - you cannot filter on the transport. The payload sits + in `EventData` as a dynamic bag (`EventData.CommandLine`), unlike + `SecurityEvent`'s flattened columns. + +## Confirming AMA actually collects ForwardedEvents + +Four layers; a green connector page proves none of them individually. + +**1. The DCR names the channel:** + +```bash +az monitor data-collection rule show --resource-group --name \ + --query "dataSources.windowsEventLogs[].xPathQueries" -o json +``` + +Look for `ForwardedEvents!*`. Only `Security!*` / `System!*` means the DCR +collects the collector's own logs - the classic looks-healthy failure. + +**2. The DCR is associated with the collector:** + +```bash +az monitor data-collection rule association list --resource "" -o table +``` + +**3. The agent received that config** (on the collector; AMA caches its +delivered DCRs locally): + +```powershell +Get-Service AzureMonitorAgent +Get-ChildItem "C:\WindowsAzure\Resources\AMADataStore.*\mcs\configchunks" -Recurse | + Select-String -Pattern "ForwardedEvents" -List +``` + +(Arc-enabled servers cache under `C:\Resources\Directory\AMADataStore.*`.) +No hit after 10-15 minutes points at the association or agent +connectivity, not the DCR definition. Agent liveness from the workspace: + +```kusto +Heartbeat +| where Computer == "" and Category == "Azure Monitor Agent" +| summarize max(TimeGenerated) +``` + +**4. End-to-end tracer.** Generate a known harmless event on a **member +server** (create and delete a test scheduled task = 4698/4699 in its +Security log), then watch it cross each hop: source Security log -> +collector ForwardedEvents -> workspace: + +```kusto +WindowsEvent +| where TimeGenerated > ago(1h) and EventID == 4698 +| where Computer == "" +| project TimeGenerated, Computer, Channel, EventData +``` + +A one-sentence acceptance criterion that exercises all four layers: *a +tracer event generated on a nominated source appears in WindowsEvent with +the source's Computer name within [delivery-mode floor + margin] minutes.* + +## Query pack + +**Fleet inventory** - who is arriving, how much, how fresh. If only the +collector's own name appears, the DCR reads the wrong channel: + +```kusto +WindowsEvent +| where TimeGenerated > ago(24h) +| summarize Events = count(), Channels = dcount(Channel), LastSeen = max(TimeGenerated) by Computer +| order by Events desc +``` + +**Direct vs forwarded** - a machine with its own AMA heartbeats; a +forwarded-only source does not. This splits the inventory into collection +paths and tells you whether WEF is in play at all: + +```kusto +let agented = Heartbeat | where TimeGenerated > ago(24h) | distinct Computer; +WindowsEvent +| where TimeGenerated > ago(24h) +| summarize Events = count(), Channels = dcount(Channel) by Computer +| extend Path = iff(Computer in (agented), "direct (AMA on box)", "forwarded (no agent)") +| order by Events desc +``` + +**Channel and event mix** - compare against the subscription query and the +source baseline (the [Reference page](reference.md) lists what each kit +setting emits): + +```kusto +WindowsEvent +| where TimeGenerated > ago(24h) +| summarize Computers = dcount(Computer), Events = count() by Channel, EventID +| order by Events desc +``` + +**Collection-policy fingerprinting** - machines sharing an identical +channel set are almost certainly under the same DCR or subscription; the +distinct fingerprints recover the collection design from the data alone: + +```kusto +WindowsEvent +| where TimeGenerated > ago(24h) +| summarize ChannelSet = make_set(Channel) by Computer +| extend Fingerprint = hash_sha256(tostring(array_sort_asc(ChannelSet))) +| summarize Machines = make_set(Computer), Count = dcount(Computer) by Fingerprint +| order by Count desc +``` + +**Silent sources** - previously seen, gone quiet (the workspace-side twin +of `wecutil gr`): + +```kusto +WindowsEvent +| where TimeGenerated > ago(7d) +| summarize LastSeen = max(TimeGenerated) by Computer +| where LastSeen < ago(2h) +| order by LastSeen asc +``` + +**Never-seen sources** - diff the expected fleet against reality: + +```kusto +let expected = dynamic(["server1", "server2", "server3"]); +let seen = toscalar(WindowsEvent | where TimeGenerated > ago(24h) | summarize make_set(Computer)); +print missing = set_difference(expected, seen) +``` + +**Ingestion latency** - against whatever target applies, remembering the +subscription delivery mode sets the floor before Azure is involved: + +```kusto +WindowsEvent +| where TimeGenerated > ago(24h) +| extend lag = ingestion_time() - TimeGenerated +| summarize p50 = percentile(lag, 50), p95 = percentile(lag, 95) by Computer +| order by p95 desc +``` + +**Volume and cost attribution** - the evidence for filtering further left +(source config, subscription query, or a +[DCR transform](https://learn.microsoft.com/azure/azure-monitor/data-collection/data-collection-transformations)): + +```kusto +WindowsEvent +| where TimeGenerated > ago(7d) +| summarize GB = sum(_BilledSize) / 1e9 by bin(TimeGenerated, 1d) +``` + +```kusto +WindowsEvent +| where TimeGenerated > ago(24h) +| summarize GB = sum(_BilledSize) / 1e9 by Computer, Channel +| order by GB desc +``` + +**Payload spot check** - pulling fields from the dynamic bag (4688 with +command line, assuming the source enables the kit's +[HighVolume tier](baselines.md#tiers)): + +```kusto +WindowsEvent +| where TimeGenerated > ago(1h) +| where Channel == "Security" and EventID == 4688 +| extend NewProcess = tostring(EventData.NewProcessName), CmdLine = tostring(EventData.CommandLine) +| project TimeGenerated, Computer, NewProcess, CmdLine +| take 20 +``` + +## The reconciliation that matters + +The single most useful standing assertion is a three-list comparison, and +none of the lists comes from a status page: + +1. the intended fleet (the AD group scoping the subscription), +2. the collector's registered Active sources (`wecutil gr`), +3. distinct `Computer` values in `WindowsEvent` over 24 hours. + +Equal counts and matching names = the chain works. Every gap has exactly +one broken hop to find, and the queries above locate which. diff --git a/docs/wec.md b/docs/wec.md new file mode 100644 index 0000000..df2fb10 --- /dev/null +++ b/docs/wec.md @@ -0,0 +1,164 @@ +# WEC Collector + +A practical guide to the Windows Event Collector side of central +collection: reading what a collector is already doing, understanding the +subscription that controls it, and verifying that sources are actually +sending. Everything here is read-only unless marked otherwise, so it is +safe on a production collector. + +Where this sits in the chain: + +``` +Member server WEC collector SIEM +[audit policy + channels] --push--> [subscription -> ForwardedEvents] --agent--> [workspace] + Gate 1 Gate 2 Gate 3 +``` + +The gates multiply. An event reaches the collector only if it is +**generated** on the source (Gate 1, this kit's job) *and* **matched** by +the subscription query (Gate 2, this page). A subscription cannot forward +what was never generated, and source config cannot force forwarding of a +channel the subscription does not name. Keeping both generated from the +same baseline selection is why +[`New-WefSubscription.ps1`](commands.md#new-wefsubscriptionps1) exists. +Gate 3 is covered on the [Sentinel KQL](kql.md) page. + +## There is no default subscription + +A Windows box ships with zero subscriptions; the Windows Event Collector +service is not even configured until `wecutil qc` runs. Whatever exists on +a collector today was put there by someone. Reading it back is the first +step of any assessment: + +```powershell +wecutil es # enumerate all subscription names +wecutil gs "" /f:xml # full configuration as XML +wecutil gr "" # runtime status: sources, state, heartbeats +``` + +Evidence-grade export of everything in one go: + +```powershell +wecutil es | ForEach-Object { wecutil gs $_ /f:xml | Out-File ".\WEF-Sub-$($_ -replace '[\\/:*?\"<>|]','_').xml" } +``` + +The same information is in Event Viewer under **Subscriptions**, but the +XML is what you keep. Command reference: +[wecutil](https://learn.microsoft.com/windows-server/administration/windows-commands/wecutil). + +## Reading a subscription: the fields that matter + +| Field in the XML | What it controls | +|---|---| +| `` | `SourceInitiated` (sources push to the collector over WinRM, the model that scales) or `CollectorInitiated` (the collector pulls; account-heavy, usually legacy) | +| `` | The authoritative "what is forwarded" filter - one `* + + + +``` + +`*` means every event in that channel - as open as WEF gets, and what this +kit's generator emits. Two structural facts: + +- There is **no channel wildcard**. "All channels on the machine" cannot be + expressed; channels must be listed (event queries cap out at 256 + `Select` expressions, far above any sane forwarding list). +- Whole-channel forwarding makes the **source configuration the effective + filter**, which is easy to verify ("every event in channels X, Y, Z + present on a source = forwarded") but means volume is governed entirely + upstream. Starting wide open and tightening with evidence after a pilot + is a defensible sequence; Microsoft's + [WEF intrusion-detection guidance](https://learn.microsoft.com/windows/security/operating-system-security/device-management/use-windows-event-forwarding-to-assist-in-intrusion-detection) + has curated per-event queries to graduate to. + +## Who sends: two lists that must agree + +With source-initiated WEF, a machine forwards only if **both** hold: + +1. It received the **SubscriptionManager** policy pointing at this + collector (GPO: Computer Configuration > Policies > Administrative + Templates > Windows Components > Event Forwarding). On a source, the + applied value is readable at + `HKLM:\SOFTWARE\Policies\Microsoft\Windows\EventLog\EventForwarding\SubscriptionManager`. +2. Its computer account is inside the subscription's + `` SDDL. + +When these are scoped by two different AD groups they drift independently - +worth a standing check. + +## Runtime status: the reconciliation + +`wecutil gr ""` lists every registered source with its state and +last heartbeat. The health assertion worth writing down as an acceptance +criterion is a three-way count: + +> AD group membership = registered sources = sources in state Active. + +Machines in the group but never registered have a broken hop (GPO not +applied, WinRM unreachable, or the Security-log permission below). Machines +registered but Inactive stopped sending. Name the discrepancies; do not +just record the counts. + +## ForwardedEvents channel health + +```powershell +wevtutil gl ForwardedEvents +Get-WinEvent -ListLog ForwardedEvents | Select-Object RecordCount, FileSize, IsLogFull, LastWriteTime +``` + +- **Size it like a busy log.** All forwarded volume concentrates here. +- **Retention must stay circular** (overwrite as needed). "Do not + overwrite" silently stops collection when full - the same trap + [Test-LoggingBaseline flags](safety.md#what-the-kit-will-never-do) on + any channel. +- **Know the headroom**: at the observed events/hour, how many hours does + the channel hold? That is the buffer available if the onward SIEM hop + goes down. + +## Classic silent failures + +| Symptom | Cause | +|---|---| +| Every channel forwards except Security, no loud error anywhere | NETWORK SERVICE cannot read the Security log on the source. Fix: add it to the **Event Log Readers** group (or grant via channel SDDL). | +| Sources registered, zero events arriving | Gate 1: the subscribed channels are not enabled/generating on the sources - verify with [`Test-LoggingBaseline.ps1`](commands.md#test-loggingbaselineps1) | +| Some machines never register | SubscriptionManager GPO scope vs `AllowedSourceDomainComputers` mismatch, or WinRM (5985/5986) blocked | +| Collection stops after working fine | ForwardedEvents full with non-circular retention | +| Volume far above estimate | `RenderedText` content format, or a high-volume source-side setting (see the [volume table](safety.md#volume-impact-settings-the-highvolume-tier-and-friends)) | + +## Verifying from this kit + +- `Test-LoggingBaseline.ps1 -WefRole Source` - is this machine configured + to forward (SubscriptionManager present, WinRM service state)? +- `Test-LoggingBaseline.ps1 -WefRole Collector` - is this machine + configured to collect? +- `New-WefSubscription.ps1 -BaselineFile ` - generate a subscription + whose channel list is provably identical to what the sources enable, so + Gates 1 and 2 cannot drift apart. + +Onward: [Sentinel KQL](kql.md) covers Gate 3 - confirming the collector's +agent ships ForwardedEvents to a workspace, and the queries that prove the +whole chain end to end. diff --git a/mkdocs.yml b/mkdocs.yml index db33cff..d2146a1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -65,6 +65,8 @@ nav: - Baselines: baselines.md - Commands: commands.md - Deployment: deployment.md + - WEC Collector: wec.md + - Sentinel KQL: kql.md - Coverage: mapping.md - Reference: reference.md - Architecture: architecture.md From 8f97f971a56dea2854bfec9cdf0ddbf08674a4de Mon Sep 17 00:00:00 2001 From: spydisec <9101327+spydisec@users.noreply.github.com> Date: Wed, 2 Sep 2026 11:44:46 +1000 Subject: [PATCH 2/3] Address CodeRabbit review on the WEC/KQL pages Accepted nearly everything - the substantive ones: - Tracer prerequisite documented: 4698/4699 need Success auditing on Other Object Access Events (Core tier), else the tracer reports a false forwarding failure - Heartbeat split reframed as agent *presence*: no-heartbeat is conclusive for the forwarded path, presence is not proof of direct collection (column renamed HasAgent, interpretation corrected) - Latency query caveat: UseTimeReceivedForForwardedEvents makes it measure only the post-receipt hop - Cost queries filter _IsBillable; never-seen diff notes FQDN matching; "exactly one broken hop" softened; Inactive no longer equated with "stopped sending"; LogFile=ForwardedEvents made an explicit assumption; delivery-mode timings qualified as approximate defaults with the wecutil reference; Security-log fix mentions the channel SDDL alternative with the WEF guidance link; Arc AMADataStore path corrected and the config-cache conclusion qualified; az CLI note for properties-nested output; fence language, hyphenation, and the changelog Intune claim now sourced - Dropped the unverified 256-expression figure rather than trade one unsourced number for another Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 3 ++- docs/deployment.md | 2 +- docs/kql.md | 52 ++++++++++++++++++++++++++++++++-------------- docs/wec.md | 32 ++++++++++++++++------------ 4 files changed, 58 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 275936a..c57b4e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,8 @@ releases are tagged `vX.Y.Z` and published with a zip + SHA256 checksum. - Docs and CONTRIBUTING reworded so PowerShell 7 is explicitly first-class: both engines are supported and CI-tested, Windows PowerShell 5.1 is the compatibility floor (ships with Windows, and - Intune remediations execute under it), not the recommended shell. + [Intune remediations execute under Windows PowerShell](https://learn.microsoft.com/intune/intune-service/fundamentals/remediations)), + not the recommended shell. ## v0.8.0 - 2026-08-31 diff --git a/docs/deployment.md b/docs/deployment.md index 2e1da89..025879b 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -80,7 +80,7 @@ once you have observed real volume. Beyond generating the subscription: the [WEC Collector](wec.md) page covers reading and verifying an existing collector (subscription anatomy, runtime status, the silent failures), and [Sentinel KQL](kql.md) covers the onward -hop to a SIEM workspace and the queries that prove the chain end to end. +hop to a SIEM workspace and the queries that prove the chain end-to-end. ## GPO (domain-joined fleets) diff --git a/docs/kql.md b/docs/kql.md index 4c87da9..1c1aa17 100644 --- a/docs/kql.md +++ b/docs/kql.md @@ -45,6 +45,11 @@ az monitor data-collection rule show --resource-group --name \ --query "dataSources.windowsEventLogs[].xPathQueries" -o json ``` +(Depending on CLI version the payload may nest under `properties` - if the +query returns nothing, retry with +`properties.dataSources.windowsEventLogs[].xPathQueries`. The portal +equivalent is the DCR's **Data sources** blade.) + Look for `ForwardedEvents!*`. Only `Security!*` / `System!*` means the DCR collects the collector's own logs - the classic looks-healthy failure. @@ -63,9 +68,11 @@ Get-ChildItem "C:\WindowsAzure\Resources\AMADataStore.*\mcs\configchunks" -Recur Select-String -Pattern "ForwardedEvents" -List ``` -(Arc-enabled servers cache under `C:\Resources\Directory\AMADataStore.*`.) -No hit after 10-15 minutes points at the association or agent -connectivity, not the DCR definition. Agent liveness from the workspace: +(Arc-enabled servers cache under `C:\Resources\Directory\AMADataStore`.) +No hit after 10-15 minutes means the delivered config lacks the data +source - the fault can sit in the DCR content, the association, or the +agent's connectivity; work back up the layers. Agent liveness from the +workspace: ```kusto Heartbeat @@ -75,8 +82,11 @@ Heartbeat **4. End-to-end tracer.** Generate a known harmless event on a **member server** (create and delete a test scheduled task = 4698/4699 in its -Security log), then watch it cross each hop: source Security log -> -collector ForwardedEvents -> workspace: +Security log - which requires Success auditing on the *Other Object +Access Events* subcategory, part of this kit's Core tier; confirm it +first or the tracer reports a false forwarding failure), then watch it +cross each hop: source Security log -> collector ForwardedEvents -> +workspace: ```kusto WindowsEvent @@ -101,16 +111,20 @@ WindowsEvent | order by Events desc ``` -**Direct vs forwarded** - a machine with its own AMA heartbeats; a -forwarded-only source does not. This splits the inventory into collection -paths and tells you whether WEF is in play at all: +**Agent presence** - a machine with its own AMA heartbeats; a +forwarded-only source does not. Absence of a heartbeat is conclusive +(the events can only have arrived via a collector); presence is not a +proof of path, since an agented machine can be collected directly *and* +forward through a subscription. Use this to find whether WEF is in play +at all, then confirm suspected direct collection against the machine's +own DCR associations: ```kusto let agented = Heartbeat | where TimeGenerated > ago(24h) | distinct Computer; WindowsEvent | where TimeGenerated > ago(24h) | summarize Events = count(), Channels = dcount(Channel) by Computer -| extend Path = iff(Computer in (agented), "direct (AMA on box)", "forwarded (no agent)") +| extend HasAgent = iff(Computer in (agented), "yes (direct collection possible)", "no (forwarded)") | order by Events desc ``` @@ -149,16 +163,21 @@ WindowsEvent | order by LastSeen asc ``` -**Never-seen sources** - diff the expected fleet against reality: +**Never-seen sources** - diff the expected fleet against reality. +`set_difference` compares exact strings and `Computer` usually carries the +FQDN, so list the expected fleet as FQDNs (or normalise both sides): ```kusto -let expected = dynamic(["server1", "server2", "server3"]); +let expected = dynamic(["server1.corp.example", "server2.corp.example"]); let seen = toscalar(WindowsEvent | where TimeGenerated > ago(24h) | summarize make_set(Computer)); print missing = set_difference(expected, seen) ``` **Ingestion latency** - against whatever target applies, remembering the -subscription delivery mode sets the floor before Azure is involved: +subscription delivery mode sets the floor before Azure is involved. One +caveat: if the DCR sets `UseTimeReceivedForForwardedEvents`, AMA stamps +`TimeGenerated` with the collector's receipt time, so this measures only +the post-receipt hop; leave that setting off to measure source-to-table: ```kusto WindowsEvent @@ -174,13 +193,13 @@ WindowsEvent ```kusto WindowsEvent -| where TimeGenerated > ago(7d) +| where TimeGenerated > ago(7d) and _IsBillable == true | summarize GB = sum(_BilledSize) / 1e9 by bin(TimeGenerated, 1d) ``` ```kusto WindowsEvent -| where TimeGenerated > ago(24h) +| where TimeGenerated > ago(24h) and _IsBillable == true | summarize GB = sum(_BilledSize) / 1e9 by Computer, Channel | order by GB desc ``` @@ -207,5 +226,6 @@ none of the lists comes from a status page: 2. the collector's registered Active sources (`wecutil gr`), 3. distinct `Computer` values in `WindowsEvent` over 24 hours. -Equal counts and matching names = the chain works. Every gap has exactly -one broken hop to find, and the queries above locate which. +Equal counts and matching names show the chain is delivering for those +machines. Every gap has at least one broken hop behind it, and the +queries above narrow down which. diff --git a/docs/wec.md b/docs/wec.md index df2fb10..b53f54c 100644 --- a/docs/wec.md +++ b/docs/wec.md @@ -8,7 +8,7 @@ safe on a production collector. Where this sits in the chain: -``` +```text Member server WEC collector SIEM [audit policy + channels] --push--> [subscription -> ForwardedEvents] --agent--> [workspace] Gate 1 Gate 2 Gate 3 @@ -53,19 +53,22 @@ XML is what you keep. Command reference: | `` | `SourceInitiated` (sources push to the collector over WinRM, the model that scales) or `CollectorInitiated` (the collector pulls; account-heavy, usually legacy) | | `` | The authoritative "what is forwarded" filter - one `