In this section

DE1.1 Sentinel Analytics Rule Types

6-8 hours · Module 1 · Free
What you already know

You understand why detection engineering exists, what causes the coverage gap, and how the four metrics measure program health. You've seen CHAIN-HARVEST walk through five phases undetected. This section teaches the platform-specific foundation: the five types of analytics rules in Microsoft Sentinel, what each one does architecturally, when you choose each type, and what each type cannot do. You need to understand these rule types before you configure, test, or deploy anything.

Scenario

You open the Analytics blade in your Sentinel workspace. Thirty-two rules are listed as active. Your SOC lead reports "comprehensive detection coverage." But when you filter by rule type, you find 24 scheduled rules (all enabled from templates), 7 Microsoft Security pass-through rules, and 1 Fusion rule. Zero NRT rules for time-critical threats. Zero custom scheduled rules designed for your environment. Zero scheduled rules that consume the Anomalies table, despite anomaly rules generating hundreds of behavioral signals per week that nobody reads. Thirty-two active rules, and the detection program is operating on defaults. This section teaches you what each rule type does — so you can evaluate which types your program is actually using and which gaps exist.

Microsoft Sentinel provides five types of analytics rules. Each type serves a different purpose in the detection lifecycle, each has different architectural characteristics, and each produces different outputs. Understanding what each type does — and more importantly, what each type cannot do — determines which type you choose for every detection hypothesis you develop throughout this course.

This matters because most Sentinel deployments use only two of the five types: scheduled rules enabled from templates, and Microsoft Security rules enabled by default when data connectors are configured. The other three — NRT, anomaly, and Fusion — either go unused or get enabled without understanding what they produce. A detection engineer uses all five deliberately, choosing the type that matches the detection hypothesis rather than the type the template wizard defaults to.

Estimated time: 45 minutes.

FIVE ANALYTICS RULE TYPES — WHAT EACH PRODUCES SCHEDULED Full KQL 5 min – 14 day interval Cross-table joins Aggregation, time-series 90%+ of your rules NRT Simple filters only ~1 min interval (fixed) No joins, no let, no union Max 50 rules, 30 alerts/run 3–5 critical rules MICROSOFT SECURITY No custom query Product alert pass-through MDE, MDO, MCAS, Entra Severity filter only Enable and configure ANOMALY ML behavioral baselines UEBA + ML templates Score 0–1 per deviation Flighting/Production mode Signal generator FUSION Microsoft-managed ML Multi-stage correlation Cross-product signals Single rule, not editable Complementary layer SecurityAlert table → Incident pipeline Alerts with entities, severity, ATT&CK tags → grouped into investigable incidents SecurityIncident Direct to incident (no alert) Anomalies table Signals only — no alerts TWO-STAGE: Anomaly signals consumed by scheduled rules → alerts

Figure DE1.1 — The five analytics rule types and their output paths. Scheduled, NRT, and Microsoft Security rules produce SecurityAlert records that feed the incident pipeline. Fusion produces SecurityIncident records directly. Anomaly rules produce signals in the Anomalies table that scheduled rules consume through two-stage detection — the dashed path shows how anomaly signals flow back into the scheduled rule pipeline.

Scheduled rules

Scheduled rules are the detection engineer's primary tool. Over 90% of the custom rules you build in this course are scheduled rules, because they offer something no other rule type provides: full KQL flexibility combined with configurable execution timing.

A scheduled rule is a KQL query that Sentinel runs on a repeating timer. You define the query, how often it runs (the query interval), and how far back in time it examines (the lookback period). If the query returns results that exceed the trigger threshold you set, Sentinel generates an alert. That alert gets enriched with the entities you mapped, tagged with the ATT&CK techniques you specified, assigned the severity you chose, and routed into the incident pipeline according to the grouping rules you configured.

The query interval ranges from 5 minutes to 14 days. The lookback period ranges from 5 minutes to 14 days. Sentinel enforces a critical constraint: the query interval must be shorter than or equal to the lookback period. You literally cannot create a rule with a 30-minute interval and a 15-minute lookback — the portal validation rejects it. This is Sentinel preventing you from creating detection gaps where events fall between executions and are never scanned.

There's a subtlety that most documentation doesn't emphasize enough. Sentinel runs all scheduled rules on a built-in 5-minute delay from their scheduled time. If your rule is configured to run every 5 minutes, it actually executes 5 minutes after each scheduled time. This delay exists to account for ingestion latency — the gap between when an event occurs at the source and when it arrives in the workspace. For most M365 data sources, this 5-minute buffer is sufficient. For data sources with longer ingestion latency — some third-party connectors, or cloud app telemetry during peak periods — events can arrive after the rule has already executed and the lookback window has closed.

The production fix for ingestion delay uses the ingestion_time() function. Instead of relying solely on TimeGenerated (when the event occurred at the source), you extend the lookback period by the expected ingestion delay and then filter on ingestion_time() (when the event actually arrived in the workspace) to associate each event with the correct execution window. This prevents both missed events and duplicate alerts from overlapping lookback periods. Microsoft documents this as the recommended architecture for production rules, not a workaround. You'll implement this pattern for every scheduled rule you build in DE3 through DE8.

Scheduled rules support the full KQL language: join, union, let statements, summarize with aggregation, make-series for time-series analysis, subqueries, and user-defined functions. This is what makes cross-family detection possible — the ability to correlate events across different data sources in a single query. The CHAIN-HARVEST detection from DE0, which joins a risky authentication event in SigninLogs with an inbox rule creation in OfficeActivity within a 30-minute window, is a scheduled rule. That cross-table join is something no other rule type can execute.

Here is what that rule actually looks like:

KQL
// Scheduled rule — inbox rule after risky sign-in (CHAIN-HARVEST Phase 3)
// Runs every 15 minutes, lookback 20 minutes
let lookback = 20m;
let riskySignins = materialize(
    SigninLogs
    | where TimeGenerated > ago(lookback)
    | where RiskLevelDuringSignIn in ("medium", "high")
    | where ResultType == 0
    | project RiskTime = TimeGenerated, UserPrincipalName, IPAddress,
        RiskLevel = RiskLevelDuringSignIn
);
OfficeActivity
| where TimeGenerated > ago(lookback)
| where Operation == "New-InboxRule"
| join kind=inner riskySignins on UserPrincipalName
| where TimeGenerated between (RiskTime .. RiskTime + 60m)
| project TimeGenerated, UserPrincipalName, IPAddress, RiskLevel,
    Operation, Parameters = tostring(Parameters)

This query uses let to define a variable, materialize() to cache an intermediate result, a cross-table join between SigninLogs and OfficeActivity, and a between clause for temporal correlation. None of these operations are possible in NRT rules. The query runs every 15 minutes with a 20-minute lookback — the extra 5 minutes of overlap ensures that events with slight ingestion delay are still caught. Sentinel's built-in deduplication suppresses duplicate alerts from the overlap.

The query starts by materializing all medium- and high-risk successful sign-ins from the last 20 minutes into a cached table. It then queries OfficeActivity for inbox rule creation events in the same time window, joins the two tables on UserPrincipalName, and filters for cases where the inbox rule was created within 60 minutes after the risky sign-in. The result — if any — is a high-confidence indicator that someone established email persistence immediately after a compromised authentication.

This is the detection you build and deploy in Section 8 of this module. You'll configure every parameter: the query, the 15-minute interval, the 20-minute lookback, the entity mapping, the severity, the ATT&CK technique tag, and the alert grouping strategy. Right now, the point is that this detection requires a scheduled rule because of the cross-table join. No other rule type can do it.

NRT rules

Near-Real-Time rules exist for a single purpose: reducing detection latency from minutes to seconds for threats where every minute of delay increases the damage.

NRT rules are hardcoded to run once every minute with a one-minute lookback. You cannot change the frequency or the lookback — they are fixed by design. Unlike scheduled rules, which filter events using TimeGenerated (when the event occurred at the source), NRT rules use ingestion_time() — they query events based on when the data arrived in the workspace, not when it was generated. This architectural difference eliminates the ingestion delay problem entirely. NRT rules run on a 2-minute built-in delay (compared to 5 minutes for scheduled rules), because the ingestion-time approach needs less buffer.

The trade-off for this speed is query capability. NRT rules support entity mapping, custom details, and dynamic alert content — the same alert enrichment features as scheduled rules. But the query language is restricted. No join. No let statements. No union. No summarize for cross-event aggregation. No make-series for time-series analysis. The query must be a filter chain: where, extend, project. You can reference multiple tables and watchlists, and queries can run across workspaces, but the core detection logic must work on individual events or very small batches.

Here is what an NRT rule looks like — a ransomware pre-encryption indicator:

KQL
// NRT rule — volume shadow copy deletion (ransomware pre-encryption)
// Runs every ~1 minute, lookback = events since last execution
DeviceProcessEvents
| where FileName =~ "vssadmin.exe"
| where ProcessCommandLine has "delete shadows"
| where InitiatingProcessFileName !in~ ("msiexec.exe", "setup.exe")
| project TimeGenerated, DeviceName, AccountName,
    FileName, ProcessCommandLine, InitiatingProcessFileName

Compare this to the scheduled rule above. No let, no materialize(), no join, no between. A straight filter chain: start with DeviceProcessEvents, filter for vssadmin.exe executing shadow copy deletion, exclude legitimate installer processes that reset shadow copies during updates, and project the fields the analyst needs. This query works on a per-event basis — each row in DeviceProcessEvents is evaluated independently. That's why it can run at NRT speed.

The detection is high-confidence despite its simplicity. vssadmin.exe delete shadows from a non-installer parent process is a near-certain indicator of ransomware preparation. The few legitimate scenarios (disaster recovery testing, backup software reconfiguration) are rare enough that the false positive rate stays under 5% in most environments. The !in~ exclusion handles the known legitimate cases.

There are additional operational constraints. NRT rules produce up to 30 single-event alerts per execution. If a query returns more than 30 results in a single minute, Sentinel creates individual alerts for the first 29 events and a 30th summary alert aggregating the rest. A workspace can run a maximum of 50 NRT rules total. These limits exist because NRT rules consume substantially more compute than scheduled rules — they execute 1,440 times per day (once per minute) compared to 288 times for a 5-minute scheduled rule or 24 times for an hourly one.

The decision is straightforward: can the attacker complete their objective in the time between scheduled rule executions? If a ransomware operator encrypts thousands of files in the 5 minutes between scheduled rule runs, that detection needs NRT. If CHAIN-HARVEST unfolds over 4 hours, a 5-minute scheduled rule provides adequate latency — and the cross-table join that the detection requires is impossible in NRT anyway.

Most production detection programs run 3 to 5 NRT rules alongside 30 to 50 scheduled rules. NRT covers the time-critical indicators: ransomware pre-encryption (shadow copy deletion, mass file renames, boot configuration changes), credential dumping from LSASS, active C2 beaconing to confirmed infrastructure, and security tool tampering (Defender service stops, event log clearing). Everything else stays scheduled.

Microsoft Security rules

Microsoft Security rules are the simplest rule type. They don't execute a KQL query. They import alerts from other Microsoft security products — Defender for Endpoint, Defender for Office 365, Defender for Cloud Apps, Entra ID Protection, Defender for Cloud — into Sentinel as incidents. You configure which products to import from, optionally filter by alert severity, and Sentinel creates incidents from those alerts automatically.

These rules exist because Microsoft's security products run their own detection engines. Defender for Endpoint detects malicious process execution using endpoint behavioral models. Defender for Office 365 detonates URLs and attachments in sandboxes to detect phishing. Entra ID Protection evaluates sign-in risk using ML models trained on Microsoft's global authentication telemetry. Those product-level alerts are valuable signals, but they generate in separate consoles with separate incident queues. Microsoft Security rules bring them into Sentinel's unified queue so the SOC sees everything in one place.

What a Microsoft Security alert looks like when it arrives in Sentinel: Defender for Office 365 detects a phishing email containing a link to a credential harvesting page. The product generates an alert with severity "Medium", entities populated automatically (the recipient mailbox, the sender address, the URL), and an alert name like "Phishing email detected by Microsoft Defender for Office 365." The Microsoft Security rule in Sentinel picks up this alert and creates an incident. The SOC analyst sees it in the Sentinel incident queue alongside alerts from your custom scheduled rules.

You don't design the detection logic — Microsoft's product team does. You don't tune the sensitivity — the product's ML models handle that. You don't control which entities are mapped — the product determines entity extraction. Your configuration choices are limited to which products and severity levels pass through.

The detection engineer's role with Microsoft Security rules is operational: confirm they're configured for every connected product, verify the severity filter isn't silently dropping alerts you need, and treat their output as one signal source among many. The real value comes from cross-referencing product alerts with your custom detections through entity correlation. A Defender for Office 365 alert that flags a phishing email, combined with your custom scheduled rule that detects an inbox rule creation within 30 minutes of a risky sign-in, produces a correlated multi-alert incident that neither detection alone could create.

One architectural change to be aware of: Microsoft is migrating all Sentinel customers to the Defender portal by March 2027. If you've already onboarded Sentinel to the Defender portal, the Defender XDR correlation engine creates incidents instead of Sentinel's native incident creation. Any Microsoft Security rules you previously configured are automatically disabled — the Defender portal handles the correlation natively. Your custom scheduled and NRT rules continue to operate regardless of which portal you use.

Anomaly rules

Anomaly rules are architecturally different from everything above. Scheduled rules, NRT rules, and Microsoft Security rules all produce alerts that feed the incident pipeline. Anomaly rules don't produce alerts at all. They write records to the Anomalies table — behavioral signals that indicate deviation from a learned baseline. These are not detections. They are inputs to your detections.

Sentinel provides two categories of anomaly detection, both writing to the same Anomalies table. ML-based anomaly rules are analytics rule templates that you find on the Anomalies tab of the Analytics page. They use machine learning models trained on your workspace data to establish behavioral baselines for specific activities — login patterns, data access volumes, Azure operations, network connections — and flag deviations that exceed a configurable threshold. UEBA (User and Entity Behavior Analytics) anomalies come from a separate engine that builds per-entity behavioral profiles based on each user's or host's own historical activity, their peer group's behavior, and the organization-wide baseline. Both produce records with the same structure.

This is what an anomaly record looks like in the Anomalies table:

KQL
// Query the Anomalies table to see what behavioral signals exist
Anomalies
| where TimeGenerated > ago(7d)
| project TimeGenerated, AnomalyTemplateName, UserName, Score,
    Tactics, Techniques, AnomalyReasons
| sort by Score desc
| take 10

A typical result might show:

KQL
TimeGenerated            AnomalyTemplateName              UserName           Score
2026-05-14 10:42:00     Anomalous Data Access            s.chen@corp.com    0.87
2026-05-14 09:15:00     Anomalous Sign-in                j.park@corp.com    0.72
2026-05-13 22:30:00     Anomalous Privilege Granted       admin@corp.com     0.91
2026-05-13 16:45:00     Anomalous Data Access            b.taylor@corp.com  0.65
Tactics: Collection, CredentialAccess, PrivilegeEscalation
AnomalyReasons (expanded for s.chen):
  "FirstTimeUserAccessedResource": true,
  "UnusualVolumeOfDataAccess": true,
  "ActivityFromUnusualCountry": false,
  "PeerGroupDeviation": true

The Score field ranges from 0 to 1 — higher values indicate greater deviation from the baseline. The AnomalyReasons field is a JSON object that explains exactly which behavioral features triggered the flag. For s.chen, the anomaly fired because it was the first time the user accessed a particular resource, the volume of data access was unusual for this user, and the behavior deviated from the user's peer group. The country was normal.

The critical architectural point: this anomaly record does not create an alert. It does not create an incident. It sits in the Anomalies table waiting for something to consume it. By itself, an anomaly score of 0.87 for "Anomalous Data Access" could be a compromised account exfiltrating data — or it could be a finance manager preparing for a board meeting by reviewing three months of financial reports. The anomaly model can't distinguish between those scenarios. That distinction requires context.

The two-stage detection pattern provides the context. Stage 1: the anomaly rule generates the behavioral signal and writes it to the Anomalies table. Stage 2: a custom scheduled rule that you write queries the Anomalies table and joins it with other data — SigninLogs risk signals, OfficeActivity patterns, device compliance status — to determine whether the behavioral anomaly is part of a compromise or a legitimate activity spike. The scheduled rule is where the detection confidence comes from. The anomaly rule provides the behavioral deviation. Together, they detect threats that don't have a fixed signature.

You'll build two-stage detections starting in DE4 (credential attacks), where behavioral anomalies from the Anomalies table serve as the first signal in cross-family correlation rules. The anomaly rules generate the behavioral baseline deviations. Your scheduled rules turn those deviations into actionable alerts.

One operational detail: the built-in anomaly rule templates can't be edited directly. To tune a template's threshold or parameters, you duplicate the rule and modify the copy. The duplicate runs in Flighting mode (staging) while the original continues in Production mode. You compare results from both, and when the tuned version produces better signal quality, you promote it to Production with a single click. The original automatically switches to Flighting. This design ensures you never break a working anomaly detection while tuning — you always have the baseline running.

Fusion

Fusion is Microsoft's ML correlation engine for multi-stage attack detection. It runs as a single analytics rule called "Advanced Multistage Attack Detection," enabled by default. You cannot create additional Fusion rules, and you cannot customize the correlation logic — Microsoft manages the ML models, the scenario definitions, and the pattern library.

Fusion correlates low-fidelity signals across multiple Microsoft products and your custom scheduled rules. A suspicious sign-in from Entra ID Protection plus an unusual file download flagged by Defender for Cloud Apps plus an anomalous outbound connection detected by Defender for Endpoint — individually, each signal is ambiguous. Fusion's ML evaluates whether the combination, considering timing and shared entities, constitutes a multi-stage attack.

Here is a concrete example of what Fusion produces. Entra ID Protection detects a sign-in from an atypical location for a user — a medium-risk alert on its own, and one that fires regularly for users who travel. Separately, Defender for Cloud Apps detects a mass file download from SharePoint by the same user — unusual volume, but the user is in a department that occasionally does bulk downloads. Neither alert alone would be prioritized by the SOC. Fusion correlates the two: the same user, within 2 hours, had a geographically atypical sign-in followed by a mass file download that deviated from their behavioral baseline. Fusion creates a single high-severity incident titled "Impossible travel followed by mass file download" with both alerts included, both entity sets merged, and both ATT&CK technique tags applied.

Fusion incidents are stored in the SecurityIncident table, not the SecurityAlert table. They are by design low-volume, high-fidelity, and high-severity. If your Fusion rule generates more than 5 incidents per week, something is misconfigured in the upstream signals. For most organizations, Fusion produces 1 to 3 incidents per month. When it fires, the multi-product correlation usually indicates a genuine multi-stage compromise worth immediate investigation.

Fusion can also correlate alerts from your custom scheduled rules into its multi-stage scenarios — but only if those rules have proper entity mapping and ATT&CK technique mapping configured. This is one more reason why every rule you build in this course includes complete entity mapping and technique tagging: it feeds Fusion's correlation engine as well as your own investigation workflow.

The detection engineer's relationship with Fusion mirrors the relationship with Microsoft Security rules. Your custom scheduled rules detect what you've specifically engineered them to detect. Fusion detects multi-stage combinations you didn't explicitly anticipate. Both run in parallel. Neither replaces the other.

Inventory your rules

Open your Sentinel workspace. Navigate to Analytics → Active rules. Sort or filter by rule type. Count how many rules you have in each category. The distribution tells a story before you read a single rule configuration.

What we see in 90% of deployments

The Sentinel project team enabled 25 template rules from the Content Hub, configured Microsoft Security rules for every connected product, and handed the workspace to the SOC. The deployment closed. Nobody evaluated whether those 25 templates detect the techniques that matter for the organization's threat landscape. Nobody assessed whether any threats require NRT latency. Nobody checked whether anomaly rules are generating signals that a scheduled rule consumes. The workspace has 32 active rules, the SOC reports "detection capability," and the actual ATT&CK coverage is 10% — the same gap it had on deployment day, because nothing was engineered after the deployment project ended.

The rule type distribution is the first diagnostic. If it shows zero custom scheduled rules, zero NRT, and zero anomaly-consuming rules — the detection program is running on deployment defaults, not on engineering.

A workspace with 25 scheduled rules (all templates), 6 Microsoft Security rules, 1 Fusion rule, and zero NRT rules is a workspace where detection was configured during deployment and never engineered afterward. Every rule was enabled because the wizard recommended it or the data connector suggested it. Nobody evaluated whether those 25 templates detect the techniques that actually matter for the organization. Nobody wrote a custom rule. Nobody evaluated whether the time-critical threats need NRT. Nobody checked whether the anomaly rules are generating signals that any scheduled rule consumes.

Run this query in the Logs blade to see the distribution from the alerts perspective — which rule types have actually produced alerts in the last 90 days:

KQL
// Rule type audit — which types are producing alerts?
let rules = SecurityAlert
    | where TimeGenerated > ago(90d)
    | where ProviderName startswith "ASI"
    | extend RuleType = case(
        ProviderName == "ASI Scheduled Alerts", "Scheduled",
        ProviderName == "ASI NRT Alerts", "NRT",
        ProviderName has "Fusion", "Fusion",
        "Microsoft Security"
    )
    | distinct AlertName, RuleType;
rules
| summarize RuleCount = count() by RuleType
| extend Percentage = round(RuleCount * 100.0 / toscalar(rules | count), 1)
| sort by RuleCount desc

The query classifies alerts by the ProviderName field in the SecurityAlert table. "ASI" stands for Azure Sentinel Intelligence — the provider prefix for all Sentinel-generated alerts. The case statement maps each provider string to a rule type category: ASI Scheduled Alerts for scheduled rules, ASI NRT Alerts for NRT, Fusion for the correlation engine, and everything else as Microsoft Security pass-through.

If you're running a developer tenant with no historical data, the query returns empty results. That's expected — you'll generate data as you build and deploy rules in the coming sections. If you're running against a production workspace, note your distribution. A mature detection program shows roughly 70-80% scheduled (a mix of template and custom), 5-8% NRT, 8-10% Microsoft Security, and occasional Fusion incidents. If your workspace shows 100% scheduled with zero custom rules, zero NRT, and no anomaly consumption — that's the gap this module teaches you to close.

Check the Anomalies table separately. Anomaly rules don't produce SecurityAlert records, so the query above doesn't show them:

KQL
// Anomaly signal inventory — what behavioral signals exist?
Anomalies
| where TimeGenerated > ago(30d)
| summarize SignalCount = count() by AnomalyTemplateName
| sort by SignalCount desc

If this returns results, your anomaly rules are generating behavioral signals. The follow-up question is whether any scheduled rule queries the Anomalies table and joins those signals with environmental context to produce actionable detections. In most workspaces, the answer is no — the anomaly rules run, the table fills with signals, and nobody consumes them. That two-stage gap is what you close starting in DE4.

Export your complete rule inventory with type classification. This PowerShell produces the audit document you'll work from throughout Module 1:

PowerShell
# Export rule inventory by type for Module 1 audit
Get-AzSentinelAlertRule -ResourceGroupName "rg-sentinel" `
    -WorkspaceName "law-sentinel" |
    Group-Object Kind |
    Select-Object @{N='RuleType';E={$_.Name}},
        @{N='Count';E={$_.Count}},
        @{N='Enabled';E={($_.Group | Where-Object Enabled).Count}} |
    Sort-Object Count -Descending |
    Format-Table -AutoSize
Expected Output
RuleType            Count  Enabled
──────────────────  ─────  ───────
Scheduled           14     14
MicrosoftSecurity   5      5
NRT                 2      2
Anomaly             2      2
Fusion              1      1

NE has 24 rules across all types, 23 enabled (one Scheduled rule was disabled after a misconfiguration and never re-enabled). The type distribution reveals the detection strategy: 14 scheduled rules do the heavy lifting, 5 MicrosoftSecurity rules pass through Defender alerts, 2 NRT rules provide fast detection for credential attacks, and 2 Anomaly rules generate behavioral signals that nobody consumes. Your output tells you whether your detection program is built on scheduled rules (customizable) or pass-through rules (vendor-dependent).

Detection Engineering Principle

The five rule types serve five different purposes. Scheduled rules provide full KQL flexibility with configurable timing — they're the workhorse for 90% of your detection program. NRT rules trade flexibility for speed on the handful of threats where sub-minute latency changes the outcome. Anomaly rules generate behavioral signals that your scheduled rules consume through two-stage correlation. Fusion correlates multi-stage attack patterns across Microsoft products and your custom rules. Microsoft Security rules import product-level alerts into the unified queue. A mature detection program uses all five deliberately — not by default, but by design.

Next

Section 2 takes the scheduled rule apart. You'll configure a test rule from scratch — the query, the frequency, the lookback, the trigger threshold, the event grouping — and see it fire in your workspace. The goal is to understand every parameter that controls how a scheduled rule executes, because every parameter is a design decision that affects detection quality.

Unlock the Full Course See Full Course Agenda