<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Sebastian Selig · Field Notes</title><description>Practical writing about Falcon LogScale and security operations.</description><link>https://it-selig.de/</link><item><title>How to reduce alert noise in LogScale</title><link>https://it-selig.de/blog/how-to-reduce-alert-noise-in-logscale/</link><guid isPermaLink="true">https://it-selig.de/blog/how-to-reduce-alert-noise-in-logscale/</guid><description>Reduce LogScale alert noise with service-specific validation, early email notifications, and a separate Teams alert for persistent failures.</description><pubDate>Sun, 13 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;The short answer&lt;/h2&gt;
&lt;p&gt;I recommend separating early information from alerts that deserve additional attention: use email for the first notification and a separate Teams alert with a longer validation period. Define both conditions around the service, because neither one failed check nor a fixed waiting period means the same thing in every environment. Keep Teams selective so that it does not become a second inbox full of messages people stop reading.&lt;/p&gt;
&lt;p&gt;My own email inbox is the starting point for this recommendation. Repeated notifications and brief server interruptions produce messages that eventually receive less attention. A service failure that matters can then disappear among notifications that did not need an immediate response.&lt;/p&gt;
&lt;p&gt;The question is how to decide which condition deserves a message, when to send it, and which channel should carry it.&lt;/p&gt;
&lt;h2&gt;The factors&lt;/h2&gt;
&lt;h3&gt;1. What the check actually confirms&lt;/h3&gt;
&lt;p&gt;A failed check provides information, but its meaning depends on what it tests. A server briefly becoming unreachable and a service remaining unavailable are different conditions. Treating both as equally urgent makes the messages harder to distinguish.&lt;/p&gt;
&lt;p&gt;Validation must fit the environment. For one service, that might mean requiring a failure to persist for a defined period. For another, the check itself may already combine enough evidence to justify an immediate notification.&lt;/p&gt;
&lt;p&gt;Before choosing a delay, describe what the result proves. Does it show one unsuccessful connection attempt, several failed checks, or a service that users cannot access? That distinction is the basis for the notification condition.&lt;/p&gt;
&lt;h3&gt;2. How much delay the service can tolerate&lt;/h3&gt;
&lt;p&gt;A longer validation period gives a brief interruption time to clear before generating an additional message. It also delays that message when the failure continues.&lt;/p&gt;
&lt;p&gt;The responsible person must decide whether that trade-off is acceptable. For example, a temporary interruption to a service needed later in the day may allow more validation time than an unavailable service required for work happening now. These are decision examples, not default severity assignments.&lt;/p&gt;
&lt;p&gt;I would choose the acceptable delay for each service before choosing the alert settings. A convenient value in the interface should not determine how long a significant failure goes without attention.&lt;/p&gt;
&lt;h3&gt;3. What each channel means&lt;/h3&gt;
&lt;p&gt;In this strategy, email carries the earlier information. Teams carries the additional notification after the stricter condition is met.&lt;/p&gt;
&lt;p&gt;That distinction only helps if it remains consistent. Sending every email notification to Teams immediately would give recipients the same messages in two places. It would not address the original problem.&lt;/p&gt;
&lt;p&gt;The message still provides information rather than taking action on the recipient’s behalf. It should make the affected service and the detected condition clear enough for the recipient to decide what to do.&lt;/p&gt;
&lt;h3&gt;4. Whether another message adds information&lt;/h3&gt;
&lt;p&gt;Repeated notifications about the same unchanged failure deserve their own decision. A reminder may be useful, but receiving the same message again does not necessarily tell the recipient anything new.&lt;/p&gt;
&lt;p&gt;LogScale supports throttling to limit repeated actions. Its documentation distinguishes the query time window from the throttle period; these settings serve different purposes. See the &lt;a href=&quot;https://library.humio.com/logscale-gdo/automated-alerts-getting-started.html&quot;&gt;LogScale trigger documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Use validation to decide whether a condition deserves a notification. Use repetition controls to decide when another notification about that condition is useful.&lt;/p&gt;
&lt;h2&gt;Concrete recommendation by situation&lt;/h2&gt;
&lt;p&gt;For a service where brief interruptions are acceptable, start with two independent alerts. Give the email alert the earlier notification condition. Give the Teams alert a condition that requires evidence over a longer period.&lt;/p&gt;
&lt;p&gt;For illustration, a brief interruption might meet the email condition and recover before it meets the Teams condition. A continuing failure would meet both conditions, so the recipient would receive the additional Teams message. This describes the intended behavior, not a tested configuration or a universal timing recommendation.&lt;/p&gt;
&lt;p&gt;For a service where waiting is unacceptable, do not add a delay merely to preserve this two-stage pattern. Use the validation that the service requires, which may already be built into the check, and choose the channel according to its importance.&lt;/p&gt;
&lt;p&gt;For information that only needs attention during defined working hours, consider restricting notifications to those times. Keep that decision separate from failure validation: business hours determine when information is relevant to the recipient, while validation determines whether the failure condition is sufficiently established.&lt;/p&gt;
&lt;p&gt;Get the query: &lt;a href=&quot;https://mylogscale.com/queries/restrict-alerts-to-weekdays-and-business-hours&quot;&gt;Restrict Alerts to Weekdays and Business Hours&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Apply such a restriction only where the service can tolerate it. An outage that requires attention outside business hours needs a notification path that remains active then.&lt;/p&gt;
&lt;h2&gt;Edge cases and objections&lt;/h2&gt;
&lt;h3&gt;A longer search window does not prove a continuing outage&lt;/h3&gt;
&lt;p&gt;The Teams alert is independent. It is not simply a delayed copy of the first email.&lt;/p&gt;
&lt;p&gt;LogScale triggers use query results to initiate actions, as described in the &lt;a href=&quot;https://library.humio.com/logscale-gdo/automated-alerts.html&quot;&gt;official trigger overview&lt;/a&gt;. The second query must therefore express the stronger condition you intend to detect.&lt;/p&gt;
&lt;p&gt;If it only finds an earlier failure somewhere in a larger window, it may still find that event after recovery. The condition must distinguish the evidence for a continuing problem from an old failure. The right way to do that depends on the available checks and events.&lt;/p&gt;
&lt;h3&gt;Too much suppression can hide a separate failure&lt;/h3&gt;
&lt;p&gt;Reducing repetition should not prevent a different service failure from becoming visible. Consider two services failing close together: a rule intended to silence reminders for the first should not accidentally silence the first notification for the second.&lt;/p&gt;
&lt;p&gt;Before relying on the setup, check three scenarios in your own environment: a brief interruption, a continuing failure, and a separate failure while the first remains active. Confirm which messages arrive through each channel.&lt;/p&gt;
&lt;p&gt;Start with one noisy alert. Define what it proves, when a second message becomes useful, and what should reach Teams. Then verify those three scenarios before applying the same strategy more broadly.&lt;/p&gt;
</content:encoded></item><item><title>How long should you keep audit logs? A practical retention guide for small teams</title><link>https://it-selig.de/blog/audit-log-retention/</link><guid isPermaLink="true">https://it-selig.de/blog/audit-log-retention/</guid><description>Keep audit logs long enough to investigate, meet your obligations, and prove what happened — but separate fast searchable data from long-term archive data.</description><pubDate>Sun, 16 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;The short answer: keep audit logs as long as their purpose requires — and don&amp;#39;t use one retention period for every kind of log.&lt;/strong&gt; A week of searchable application logs can be enough for day-to-day troubleshooting. Authentication, administrative, and audit-relevant events often need a longer, documented retention period. The right number comes from your incident-response needs, contracts, sector rules, and privacy obligations — not from the default setting in your logging tool.&lt;/p&gt;
&lt;p&gt;This is a technical decision guide, not legal advice. If a law, regulator, customer contract, or internal policy applies to your organisation, have the responsible legal, privacy, and compliance stakeholders confirm the final retention schedule.&lt;/p&gt;
&lt;h2&gt;Start with the question the log must answer&lt;/h2&gt;
&lt;p&gt;“Keep logs for a year” sounds simple, but it hides several different jobs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Troubleshooting:&lt;/strong&gt; What changed after yesterday&amp;#39;s deployment?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security investigation:&lt;/strong&gt; When did an attacker first authenticate, and what did they do afterwards?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audit evidence:&lt;/strong&gt; Who changed a privileged setting, and can we show the sequence of events?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Capacity and service analysis:&lt;/strong&gt; Which system or endpoint produced the failures?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each job has a different time horizon, access pattern, and cost. Teams normally investigate production errors within hours or days. A security incident may be discovered weeks later. An audit request can arrive long after the systems involved have changed.&lt;/p&gt;
&lt;p&gt;NIST describes its current log-management work as a planning guide for improvements that support both recommended practices and regulatory requirements.[1] That is the useful mindset: retention is not an isolated storage setting. It is a documented operating decision tied to the risks you need to manage.&lt;/p&gt;
&lt;h2&gt;Separate hot search from long-term retention&lt;/h2&gt;
&lt;p&gt;The most expensive version of retention is keeping every event instantly searchable forever. It is rarely necessary.&lt;/p&gt;
&lt;p&gt;Instead, separate data into at least two layers:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Hot, searchable data&lt;/strong&gt; for active monitoring, incident triage, and investigations. This is the data analysts need to query quickly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Archive data&lt;/strong&gt; for longer-term evidence and exceptional investigations. It can be slower and cheaper, as long as it stays protected, retrievable, and understandable when needed.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For LogScale users, this distinction matters because repository retention controls what is available for search. In the cloud product, age-based retention uses the event&amp;#39;s &lt;code&gt;@timestamp&lt;/code&gt;; LogScale deletes data in segments and restricts searches to the configured interval.[3] Do not assume a backup, an archive, and a searchable LogScale repository are interchangeable. Decide which one fulfils which purpose.&lt;/p&gt;
&lt;h2&gt;Archive the evidence you need, not every raw event&lt;/h2&gt;
&lt;p&gt;An archive does not need to be a second copy of the whole repository. For a long-lived investigation record, I prefer to define the small set of LogScale queries that answer the questions I expect to revisit: authentication activity, privilege changes, configuration changes, and the alerts or cases that explain a response.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/ITrunsDE/logscale-archive&quot;&gt;LogScale Archive&lt;/a&gt; follows that model. It runs a scheduled, repository-scoped LogScale query and stores its results locally; the web UI searches stored results, not a live LogScale repository.[4] Retention is configured per query version, so a short-lived operational query does not force the same policy on a high-value audit query.[5]&lt;/p&gt;
&lt;p&gt;That boundary matters. A query-result archive is evidence selected for a purpose. It is not a complete backup of the original repository, and it cannot recover data a query never selected. Keep a separate backup and restore plan for the Archive database itself.&lt;/p&gt;
&lt;h2&gt;A practical starting matrix&lt;/h2&gt;
&lt;p&gt;Use this as a conversation starter, not as a compliance schedule:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Data type&lt;/th&gt;
&lt;th&gt;Primary purpose&lt;/th&gt;
&lt;th&gt;Searchable period&lt;/th&gt;
&lt;th&gt;Longer-term archive?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Application and debug logs&lt;/td&gt;
&lt;td&gt;Troubleshooting&lt;/td&gt;
&lt;td&gt;Days to a few weeks&lt;/td&gt;
&lt;td&gt;Usually no, unless needed for a specific investigation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure and service logs&lt;/td&gt;
&lt;td&gt;Operations and incident response&lt;/td&gt;
&lt;td&gt;Weeks to a few months&lt;/td&gt;
&lt;td&gt;Sometimes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authentication and privilege events&lt;/td&gt;
&lt;td&gt;Security investigation and audit trail&lt;/td&gt;
&lt;td&gt;Months, based on risk&lt;/td&gt;
&lt;td&gt;Often&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Administrative changes and access records&lt;/td&gt;
&lt;td&gt;Audit evidence and investigation&lt;/td&gt;
&lt;td&gt;Months, based on obligations&lt;/td&gt;
&lt;td&gt;Often&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security alerts and investigation cases&lt;/td&gt;
&lt;td&gt;Evidence of response and decisions&lt;/td&gt;
&lt;td&gt;Long enough to reconstruct the case&lt;/td&gt;
&lt;td&gt;Often&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Selected query results&lt;/td&gt;
&lt;td&gt;Durable evidence for one defined question&lt;/td&gt;
&lt;td&gt;Only while operationally useful&lt;/td&gt;
&lt;td&gt;Yes, when that question outlives hot search&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The value of the matrix is not the labels. It forces the questions that a single retention value avoids: What would we lose if the data vanished tomorrow? How late are incidents usually detected? Who needs the data? Can we still read and trust it after the original system is gone?&lt;/p&gt;
&lt;h2&gt;Privacy is part of the retention design&lt;/h2&gt;
&lt;p&gt;Logs frequently contain personal data: usernames, IP addresses, device identifiers, email addresses, and sometimes application payloads. Retaining logs “just in case” is therefore not a neutral option.&lt;/p&gt;
&lt;p&gt;The GDPR&amp;#39;s storage-limitation principle says that personal data should be kept in a form that identifies people for no longer than necessary for the purpose of processing.[2] That does not mean security logs must be deleted immediately. It means the purpose, necessity, access controls, and retention period should be explicit and defensible.&lt;/p&gt;
&lt;p&gt;A practical response is to document, for every important log class:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the purpose of collection;&lt;/li&gt;
&lt;li&gt;the system owner;&lt;/li&gt;
&lt;li&gt;who can search it;&lt;/li&gt;
&lt;li&gt;the searchable-retention period;&lt;/li&gt;
&lt;li&gt;the archive-retention period, if any; and&lt;/li&gt;
&lt;li&gt;how deletion or expiry is verified.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This also makes future changes easier. When someone asks why a repository holds 90 days instead of 30, or why an archive query keeps 12 months of privilege changes, the answer should not be “because that was the default.”&lt;/p&gt;
&lt;h2&gt;Design for an investigation that starts late&lt;/h2&gt;
&lt;p&gt;The retention period should reflect your detection reality, not your ideal one. If a suspicious account is discovered after 45 days but authentication events disappear after 30, the investigation begins with a blind spot.&lt;/p&gt;
&lt;p&gt;That does not automatically mean every raw event needs to remain in hot storage for a year. You can retain high-value events longer than low-value volume. Examples include successful and failed authentication, privilege changes, configuration changes, audit events, and security alerts. In contrast, highly verbose debug output may only be valuable for a short troubleshooting window.&lt;/p&gt;
&lt;p&gt;The key is to preserve enough context to reconstruct a meaningful timeline. An archived authentication event without host, source address, user identity, timestamp, and relevant action may be cheaper to keep but less useful to investigate.&lt;/p&gt;
&lt;h2&gt;Make retention enforceable, not aspirational&lt;/h2&gt;
&lt;p&gt;A policy that says “keep audit logs for 12 months” is incomplete if nobody knows where the data lives or whether deletion actually happens.&lt;/p&gt;
&lt;p&gt;For each repository or archive, test these four things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Expiry:&lt;/strong&gt; Does data disappear when the retention period ends?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Search boundary:&lt;/strong&gt; Can users query only the intended hot window?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Archive retrieval:&lt;/strong&gt; Can the team retrieve a specific period when an investigation requires it?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access control:&lt;/strong&gt; Can only authorised people access sensitive historical events?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For a query-result archive, add two checks: the query must select the fields needed to reconstruct the timeline, and the worker must have completed the scheduled run. A green connection test alone proves neither. If an investigation needs a period outside the hot window, retrieve one known archived result before declaring the policy operational.&lt;/p&gt;
&lt;p&gt;Document the test and repeat it after major platform or policy changes. Retention is a control only when the configured behaviour matches the documented intention.&lt;/p&gt;
&lt;h2&gt;A sensible next step for a small team&lt;/h2&gt;
&lt;p&gt;Do not begin by choosing a universal number of days. Pick your three highest-value data classes — for example authentication events, privileged changes, and application errors — and write down their purpose and investigation window.&lt;/p&gt;
&lt;p&gt;Then set a modest searchable period that your team can afford and operate. If a class needs to outlive that window, define an archive path before the data ages out. Start with one query for authentication or privileged changes, test its results, activate its schedule, and document why that query earns the longer retention. This creates a real retention strategy without turning a small logging project into a compliance programme.&lt;/p&gt;
&lt;h2&gt;Get the queries&lt;/h2&gt;
&lt;p&gt;Retention only helps if you can use the data while it is available. Follow the &lt;a href=&quot;https://mylogscale.com/howtos/install-logscale-archive-with-docker-compose/&quot;&gt;LogScale Archive installation walkthrough&lt;/a&gt; to create, test, and activate the first archive query. Browse &lt;a href=&quot;https://mylogscale.com/&quot;&gt;mylogscale.com&lt;/a&gt; for LogScale queries you can adapt for authentication activity, errors, and other investigation starting points. Updates land there, not in this article.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://csrc.nist.gov/Projects/log-management&quot;&gt;NIST Log Management project&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eur-lex.europa.eu/eli/reg/2016/679/oj/eng&quot;&gt;Regulation (EU) 2016/679 (GDPR)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://library.humio.com/falcon-logscale-cloud/admin-retention.html&quot;&gt;Falcon LogScale Cloud Data Retention&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ITrunsDE/logscale-archive/blob/main/README.md&quot;&gt;LogScale Archive project and scope&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ITrunsDE/logscale-archive/blob/main/docs/retention.md&quot;&gt;LogScale Archive retention controls&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>5 LogScale queries to audit Active Directory group changes</title><link>https://it-selig.de/blog/logscale-queries-ad-group-changes/</link><guid isPermaLink="true">https://it-selig.de/blog/logscale-queries-ad-group-changes/</guid><description>Five practical LogScale queries for tracking who added or removed members from privileged AD groups.</description><pubDate>Fri, 31 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Changes to membership in privileged Active Directory groups like &lt;code&gt;Domain Admins&lt;/code&gt;, &lt;code&gt;Enterprise Admins&lt;/code&gt;, and Backup Operators are usually among the first things an attacker targets after gaining access --- a foothold on a low-privilege account is far less useful than a foothold with domain-wide rights, and group membership is often the fastest path from one to the other. At the same time, these changes are some of the easiest things to miss: they&amp;#39;re a handful of specific event IDs buried among the much larger volume of everyday Windows Security logs, and unless you&amp;#39;re specifically looking for them, they tend to scroll past unnoticed until an audit or an incident forces the question.&lt;/p&gt;
&lt;p&gt;The five queries below start broad and get progressively more targeted, moving from &amp;quot;show me everything&amp;quot; to specific patterns worth alerting on to a rollup suitable for reporting. They assume Windows Security events are sent to LogScale using the standard &lt;code&gt;#windows.EventID&lt;/code&gt; field --- if you&amp;#39;re ingesting through a custom parser or a different field mapping, adjust the field names accordingly before running them.&lt;/p&gt;
&lt;p&gt;One thing worth knowing before you start: &lt;code&gt;MemberName&lt;/code&gt; is not reliably populated in these events. Depending on the source and the parser, it arrives as a full distinguished name, as a bare account name, or as nothing at all --- while &lt;code&gt;MemberSid&lt;/code&gt; is always present. Every table below therefore carries both, so an empty &lt;code&gt;member&lt;/code&gt; column is a parser detail rather than a missing result.&lt;/p&gt;
&lt;h2&gt;1. Track every group membership change, no matter which group it is&lt;/h2&gt;
&lt;p&gt;Event IDs &lt;code&gt;4728&lt;/code&gt; and &lt;code&gt;4729&lt;/code&gt; (global groups), &lt;code&gt;4732&lt;/code&gt; and &lt;code&gt;4733&lt;/code&gt; (local groups), and &lt;code&gt;4756&lt;/code&gt; and &lt;code&gt;4757&lt;/code&gt; (universal groups) fire whenever a member is added to or removed from a group, regardless of which group it is or who made the change. Combining all six into a single view gives you the full stream of membership activity across the domain, which is the right starting point before you narrow down to anything specific.&lt;/p&gt;
&lt;p&gt;This one is deliberately broad. It&amp;#39;s not meant to be alerted on directly --- with any reasonably active AD environment, group membership changes happen constantly for mundane reasons --- but it&amp;#39;s the query you reach for when you&amp;#39;re investigating an incident and need to see everything that touched group membership around a given time window, or when you want a quick sanity check that the event forwarding pipeline is actually capturing these IDs at all.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;in(field=&amp;quot;#windows.EventID&amp;quot;, values=[4732,4728,4756,4729,4733,4757])
| case {
	in(field=&amp;quot;#windows.EventID&amp;quot;, values=[4732,4728,4756]) | action:= &amp;quot;added&amp;quot;;
	in(field=&amp;quot;#windows.EventID&amp;quot;, values=[4733,4729,4757]) | action:= &amp;quot;removed&amp;quot;;
}
| rename(field=&amp;quot;windows.EventData.TargetUserName&amp;quot;, as=&amp;quot;group&amp;quot;)
| rename(field=&amp;quot;windows.EventData.MemberName&amp;quot;, as=&amp;quot;member&amp;quot;)
| rename(field=&amp;quot;windows.EventData.MemberSid&amp;quot;, as=&amp;quot;member_sid&amp;quot;)
| rename(field=&amp;quot;windows.EventData.SubjectUserName&amp;quot;, as=&amp;quot;administrator&amp;quot;)
| table([@timestamp, action, group, member, member_sid, administrator])
| sort(@timestamp, order=desc)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://mylogscale.com/queries/track-every-active-directory-group-membership-change&quot;&gt;Track every Active Directory group membership change&lt;/a&gt;&lt;/strong&gt; on mylogscale.&lt;/p&gt;
&lt;p&gt;The result is a plain event table, one row per membership change:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;@timestamp&lt;/th&gt;
&lt;th&gt;action&lt;/th&gt;
&lt;th&gt;group&lt;/th&gt;
&lt;th&gt;member&lt;/th&gt;
&lt;th&gt;member_sid&lt;/th&gt;
&lt;th&gt;administrator&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;2026-07-29 14:02:11&lt;/td&gt;
&lt;td&gt;added&lt;/td&gt;
&lt;td&gt;Helpdesk Tier 1&lt;/td&gt;
&lt;td&gt;CN=m.klein,OU=Users,DC=corp,DC=local&lt;/td&gt;
&lt;td&gt;S-1-5-21-...-4821&lt;/td&gt;
&lt;td&gt;a.weber&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-07-29 03:47:52&lt;/td&gt;
&lt;td&gt;added&lt;/td&gt;
&lt;td&gt;Domain Admins&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;S-1-5-21-...-1174&lt;/td&gt;
&lt;td&gt;svc_provision&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The second row is the kind of thing the rest of this list exists to isolate: an off-hours addition to a privileged group, made by a service account, with no resolvable &lt;code&gt;member&lt;/code&gt; name.&lt;/p&gt;
&lt;h2&gt;2. Changes to Domain Admins specifically&lt;/h2&gt;
&lt;p&gt;The broad view is useful for investigation, but day-to-day you care much more about a short list of high-privilege groups than about every group in the domain. This query narrows the same event set down to &lt;code&gt;Domain Admins&lt;/code&gt;, &lt;code&gt;Enterprise Admins&lt;/code&gt;, &lt;code&gt;Schema Admins&lt;/code&gt;, &lt;code&gt;DnsAdmins&lt;/code&gt;, &lt;code&gt;Group Policy Creator Owners&lt;/code&gt;, and a couple of infrastructure-specific groups (&lt;code&gt;VMware Admins&lt;/code&gt;, &lt;code&gt;vCenter Administrators&lt;/code&gt;), and adds a filter for changes made outside normal business hours.&lt;/p&gt;
&lt;p&gt;The after-hours filter does much of the work here. A membership change to &lt;code&gt;Domain Admins&lt;/code&gt; at 2 pm on a Tuesday might be entirely legitimate change management; the same change at 3 am is a different story. Adjust the hour range and the group list to match your own change-management windows and your own list of privileged groups --- the ones above are just a starting point.&lt;/p&gt;
&lt;p&gt;Set the &lt;code&gt;timezone&lt;/code&gt; argument on &lt;code&gt;formatTime()&lt;/code&gt; to your own zone, and do not skip it. Without it, LogScale formats the timestamp in UTC, so a 9-to-17 filter silently becomes 10-to-18 or 11-to-19 for anyone in Central Europe, depending on daylight saving time. The first and last hour of the working day then either escape the filter or get flagged as after-hours, which is the sort of error that only surfaces once someone questions an alert. The &lt;code&gt;parseInt()&lt;/code&gt; step is there for the same reason: &lt;code&gt;formatTime()&lt;/code&gt; returns a string like &lt;code&gt;&amp;quot;03&amp;quot;&lt;/code&gt;, and converting it explicitly keeps the comparison numeric instead of relying on LogScale to coerce it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;in(field=&amp;quot;#windows.EventID&amp;quot;, values=[4732,4728,4756,4729,4733,4757])
| case {
in(field=&amp;quot;#windows.EventID&amp;quot;, values=[4732,4728,4756]) | action:= &amp;quot;added&amp;quot;;
in(field=&amp;quot;#windows.EventID&amp;quot;, values=[4733,4729,4757]) | action:= &amp;quot;removed&amp;quot;;
}

// look for these groups
| in(field=&amp;quot;windows.EventData.TargetUserName&amp;quot;, values=[&amp;quot;Domain Admins&amp;quot;,
&amp;quot;Enterprise Admins&amp;quot;,
&amp;quot;Schema Admins&amp;quot;,
&amp;quot;DnsAdmins&amp;quot;,
&amp;quot;Group Policy Creator Owners&amp;quot;,
&amp;quot;VMware Admins&amp;quot;,
&amp;quot;vCenter Administrators&amp;quot;])

// only show outside office hours, in local time
| hour := formatTime(format=&amp;quot;%H&amp;quot;, field=@timestamp, timezone=&amp;quot;Europe/Berlin&amp;quot;)
| parseInt(hour)
| hour &amp;lt; 9 or hour &amp;gt; 17

// format the table
| rename(field=&amp;quot;windows.EventData.TargetUserName&amp;quot;, as=&amp;quot;group&amp;quot;)
| rename(field=&amp;quot;windows.EventData.MemberName&amp;quot;, as=&amp;quot;member&amp;quot;)
| rename(field=&amp;quot;windows.EventData.MemberSid&amp;quot;, as=&amp;quot;member_sid&amp;quot;)
| rename(field=&amp;quot;windows.EventData.SubjectUserName&amp;quot;, as=&amp;quot;administrator&amp;quot;)
| table([@timestamp, action, group, member, member_sid, administrator])
| sort(@timestamp, order=desc)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://mylogscale.com/queries/detect-after-hours-changes-to-privileged-active-directory-groups&quot;&gt;Detect after-hours changes to privileged Active Directory groups&lt;/a&gt;&lt;/strong&gt; on mylogscale.&lt;/p&gt;
&lt;h2&gt;3. Self-service additions: when an account adds itself to a group&lt;/h2&gt;
&lt;p&gt;Legitimate administrators rarely add their own accounts to a group --- normal workflows involve managing other people&amp;#39;s access, not your own. When &lt;code&gt;SubjectUserSid&lt;/code&gt; matches &lt;code&gt;MemberSid&lt;/code&gt; in the event data, the same account both requested and received the change, which strongly indicates that something other than routine administration is going on.&lt;/p&gt;
&lt;p&gt;In practice, this pattern shows up in a handful of scenarios: an attacker who has compromised a low-privilege account using a token or session with just enough rights to escalate itself, a misconfigured automation script running under a personal account instead of a dedicated service account, or --- less alarmingly --- an admin taking a shortcut during a rushed change. None of these should happen regularly, and none of them should happen silently. That&amp;#39;s what makes this one of the higher-signal, lower-noise queries in this list: the baseline rate of legitimate self-additions in most environments is close to zero, so any hit deserves a look.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;in(field=&amp;quot;#windows.EventID&amp;quot;, values=[4732,4728,4756])
| test(windows.EventData.MemberSid == windows.EventData.SubjectUserSid)

// format the table
| rename(field=&amp;quot;windows.EventData.TargetUserName&amp;quot;, as=&amp;quot;group&amp;quot;)
| rename(field=&amp;quot;windows.EventData.MemberName&amp;quot;, as=&amp;quot;member&amp;quot;)
| rename(field=&amp;quot;windows.EventData.MemberSid&amp;quot;, as=&amp;quot;member_sid&amp;quot;)
| rename(field=&amp;quot;windows.EventData.SubjectUserName&amp;quot;, as=&amp;quot;administrator&amp;quot;)
| table([@timestamp, group, member, member_sid, administrator])
| sort(@timestamp, order=desc)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://mylogscale.com/queries/detect-active-directory-group-self-additions&quot;&gt;Detect Active Directory group self-additions&lt;/a&gt;&lt;/strong&gt; on mylogscale.&lt;/p&gt;
&lt;h2&gt;4. Group membership changes performed by service accounts&lt;/h2&gt;
&lt;p&gt;Service accounts typically handle automation, not manual management. Provisioning tools, sync jobs, and identity platforms often use a service account to add or remove users from groups as part of their normal tasks. This is expected. What isn&amp;#39;t expected is a service account making changes outside its usual automation window. That usually means someone is using the service account&amp;#39;s credentials directly, instead of letting automation handle it.&lt;/p&gt;
&lt;p&gt;This query filters for &lt;code&gt;SubjectUserName&lt;/code&gt; values matching common service-account naming prefixes (&lt;code&gt;svc_&lt;/code&gt;, &lt;code&gt;sa_&lt;/code&gt;, &lt;code&gt;srv_&lt;/code&gt;) --- adjust the regex to match your own naming convention. The prefix filter on its own would surface every legitimate sync job as well, so it carries the same time-of-day filter as query 2, inverted: automation is expected overnight, and a change from the same account in the middle of the working day is the interesting case. If your sync job runs at 2 am, anything from that account at 2 pm is worth a second look. Set the hour range to the inverse of your own automation window, and use the same &lt;code&gt;timezone&lt;/code&gt; you set in query 2.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;in(field=&amp;quot;#windows.EventID&amp;quot;, values=[4732,4728,4756,4729,4733,4757])
| windows.EventData.SubjectUserName=/^(svc_|sa_|srv_)/i

// only show during office hours, when automation should be idle
| hour := formatTime(format=&amp;quot;%H&amp;quot;, field=@timestamp, timezone=&amp;quot;Europe/Berlin&amp;quot;)
| parseInt(hour)
| hour &amp;gt;= 9 and hour &amp;lt;= 17

// format the table
| rename(field=&amp;quot;windows.EventData.TargetUserName&amp;quot;, as=&amp;quot;group&amp;quot;)
| rename(field=&amp;quot;windows.EventData.MemberName&amp;quot;, as=&amp;quot;member&amp;quot;)
| rename(field=&amp;quot;windows.EventData.MemberSid&amp;quot;, as=&amp;quot;member_sid&amp;quot;)
| rename(field=&amp;quot;windows.EventData.SubjectUserName&amp;quot;, as=&amp;quot;administrator&amp;quot;)
| table([@timestamp, group, member, member_sid, administrator])
| sort(@timestamp, order=desc)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://mylogscale.com/queries/detect-active-directory-group-changes-by-service-accounts-during-office-hours&quot;&gt;Detect Active Directory group changes by service accounts during office hours&lt;/a&gt;&lt;/strong&gt; on mylogscale.&lt;/p&gt;
&lt;h2&gt;5. Daily rollup for a compliance report&lt;/h2&gt;
&lt;p&gt;Auditors checking access controls usually don&amp;#39;t want a raw event feed. They want a clear number to reference and a trend they can compare over time. This query reduces the events to a daily count for each group, making months of membership changes easy to show on a single slide or spreadsheet row.&lt;/p&gt;
&lt;p&gt;It&amp;#39;s not a detection query in the same sense as the others; it won&amp;#39;t tell you &lt;em&gt;that&lt;/em&gt; something suspicious happened, only &lt;em&gt;how much&lt;/em&gt; membership activity a group saw on a given day. But it pairs well with the others --- if the daily rollup for &lt;code&gt;Domain Admins&lt;/code&gt; suddenly jumps from its usual near-zero baseline, that&amp;#39;s a good prompt to go back to query 2 and look at what actually changed. Keep the same &lt;code&gt;timezone&lt;/code&gt; you used there, otherwise a late-evening change lands on the following day in the report and the two queries stop agreeing with each other.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;in(field=&amp;quot;#windows.EventID&amp;quot;, values=[4732,4728,4756,4729,4733,4757])
| day := formatTime(format=&amp;quot;%Y-%m-%d&amp;quot;, field=@timestamp, timezone=&amp;quot;Europe/Berlin&amp;quot;)
| rename(field=&amp;quot;windows.EventData.TargetUserName&amp;quot;, as=&amp;quot;group&amp;quot;)
| groupBy([day, group], function=count())
| sort(day, order=desc)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://mylogscale.com/queries/daily-active-directory-group-membership-changes&quot;&gt;Daily Active Directory group membership changes&lt;/a&gt;&lt;/strong&gt; on mylogscale.&lt;/p&gt;
&lt;p&gt;Which gives you exactly the shape an auditor asks for:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;day&lt;/th&gt;
&lt;th&gt;group&lt;/th&gt;
&lt;th&gt;_count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;2026-07-29&lt;/td&gt;
&lt;td&gt;Helpdesk Tier 1&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-07-29&lt;/td&gt;
&lt;td&gt;Domain Admins&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-07-28&lt;/td&gt;
&lt;td&gt;Helpdesk Tier 1&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;Wiring these into alerts&lt;/h2&gt;
&lt;p&gt;Queries 3 and 4 are the best ones to set up as scheduled searches with a notifier. Both describe behaviour that should almost never happen in a well-managed environment. A self-addition, or a service account making changes during the hours it is supposed to be idle, is rare enough that a false positive only takes a couple of minutes to check, while a real issue is important to catch right away. Queries 1, 2, and 5 work better as saved dashboards you review regularly. Running them as alerts would mostly create noise, since group membership changes and daily activity can shift for many routine reasons.&lt;/p&gt;
&lt;p&gt;For the lookback window, 5 to 15 minutes is a good place to start. However, check how your Windows event forwarding works before deciding. If events come in batches instead of a steady stream, a 5-minute window can leave gaps where an event falls outside both the previous and current search. It&amp;#39;s usually safer to make the window a bit wider and let the scheduled search handle deduplication, rather than trying to keep the interval as short as possible.&lt;/p&gt;
&lt;p&gt;Make sure the notifier payload actually includes &lt;code&gt;member&lt;/code&gt;, &lt;code&gt;member_sid&lt;/code&gt;, &lt;code&gt;group&lt;/code&gt;, and &lt;code&gt;administrator&lt;/code&gt; rather than just a generic &amp;quot;match found&amp;quot; message --- whoever receives the alert should be able to act on it without opening LogScale first. And if query 4 turns out to be noisier than expected in your environment (some shops have more interactive service-account usage than they&amp;#39;d like to admit), consider adding a minimum hit count or a short suppression window before the notifier fires, so a single automation blip doesn&amp;#39;t trigger an unnecessary alert.&lt;/p&gt;
&lt;h2&gt;All Active Directory queries&lt;/h2&gt;
&lt;p&gt;Browse all &lt;a href=&quot;https://mylogscale.com/queries?q=ActiveDirectory&quot;&gt;Active Directory queries on mylogscale&lt;/a&gt; for additional detection, investigation, and reporting use cases.&lt;/p&gt;
</content:encoded></item></channel></rss>