Automation

Microsoft Sentinel : diagnostiquer une détection planifiée silencieuse avant d’élargir sa fenêtre

Un runbook de production pour séparer collecte, latence d’ingestion, exécution de règle, fenêtres KQL et création d’incident avant de modifier une détection Microsoft Sentinel.

17 sept. 2026 azuremicrosoft-sentinelsecurityanalytics-ruleskqlingestionobservabilityincident-responseautomationrunbookrollbackproduction

Une règle planifiée Microsoft Sentinel ne crée plus d’incident depuis la veille. La plateforme source affiche toujours des événements de sécurité, le connecteur est indiqué comme connecté et une requête KQL large retourne bien des lignes. Élargir la fenêtre de la règle de quinze minutes à plusieurs heures paraît être le raccourci logique.

Cette modification peut masquer la panne réelle et rejouer des événements anciens sous forme d’alertes en double. L’incident manquant peut venir de la collecte, d’un retard d’ingestion, d’un changement de schéma, d’un échec d’exécution, d’un filtre KQL, de l’entity mapping, du regroupement des alertes ou d’une automation rule qui clôt les incidents. Ce runbook isole chaque frontière avant de modifier la logique de détection en production.

Figer un événement qui aurait dû être détecté

Choisissez un événement précis qui devait correspondre à la règle. Relevez son horodatage source, son identifiant stable et l’entité attendue. Figez aussi l’ID de ressource de la règle, la requête déployée, la fréquence, la fenêtre, le seuil, le regroupement et les paramètres de création d’incident. Sans événement témoin, une requête manuelle qui retourne quelques lignes ne prouve presque rien.

yaml sentinel-detection-contract.yml
workspace: law-sec-prod
rule: detect-admin-group-change
rule_kind: scheduled
source_table: CommonSecurityLog

expected_event:
source_time_utc: 2026-09-17T06:18:00Z
source_event_id: evt-78421
entity: admin@example.com

schedule:
frequency: 5m
lookback: 15m
threshold: greater-than-0

capture_before_change:
- rule-resource-id-and-etag
- deployed-query-and-parameters
- connector-and-rule-health
- alert-grouping-and-incident-settings

rollback_on:
- duplicate-alerts
- query-cost-or-runtime-regression
- loss-of-entity-mapping
- unexplained-volume-increase

Exportez la définition réellement déployée ou conservez la version IaC relue. La vue du portail est une preuve de l’état courant, pas l’unique copie de rollback.

Prouver séparément la source, l’arrivée et la ligne interrogeable

Vérifiez d’abord que la source a émis l’événement témoin. Retrouvez ensuite le même identifiant dans la table Sentinel cible, sans reprendre tous les filtres de la règle. Comparez enfin l’heure source, TimeGenerated et ingestion_time(). Ce sont trois horloges différentes.

kusto 01-prove-event-arrival-and-latency.kql
let EventId = "evt-78421";
let WindowStart = datetime(2026-09-17T05:45:00Z);
let WindowEnd = datetime(2026-09-17T07:00:00Z);
CommonSecurityLog
| where TimeGenerated between (WindowStart .. WindowEnd)
| where tostring(ExternalID) == EventId
| extend IngestedAt = ingestion_time()
| extend IngestionDelay = IngestedAt - TimeGenerated
| project TimeGenerated, IngestedAt, IngestionDelay,
        ExternalID, DeviceVendor, DeviceProduct,
        SourceUserName, Activity
| order by TimeGenerated asc

Adaptez l’identifiant stable et les colonnes à la table réelle. Si la ligne est absente, arrêtez de modifier l’analytics rule : la panne est en amont ou dans l’ingestion. Si ingestion_time() est nul, n’inventez pas de conclusion sur la latence; la fonction dépend de la stratégie d’ingestion-time. Si la ligne arrive après la fenêtre de la règle, mesurez la distribution des retards au lieu de raisonner sur un seul événement.

Mesurer l’écart avant d’élargir la fenêtre

Comparez volume et retard d’ingestion avant et après la dernière détection réussie. Les percentiles sont plus utiles qu’une moyenne : une petite traîne tardive peut contenir précisément l’événement important.

kusto 02-measure-ingestion-delay.kql
let Horizon = 24h;
CommonSecurityLog
| where TimeGenerated >= ago(Horizon)
| extend IngestedAt = ingestion_time()
| where isnotnull(IngestedAt)
| extend DelaySeconds = datetime_diff("second", IngestedAt, TimeGenerated)
| summarize Events = count(),
          P50 = percentile(DelaySeconds, 50),
          P95 = percentile(DelaySeconds, 95),
          P99 = percentile(DelaySeconds, 99),
          LastEvent = max(TimeGenerated),
          LastIngestion = max(IngestedAt)
by bin(TimeGenerated, 15m)
| order by TimeGenerated asc

Une fenêtre plus large peut être justifiée lorsque la latence observée dépasse la fenêtre courante. Elle doit alors être associée à une stratégie qui empêche deux exécutions chevauchantes de produire deux alertes. Pour une règle planifiée, un filtre sur l’ingestion récente peut attribuer les événements tardifs à une seule exécution pendant que la plage TimeGenerated couvre le retard mesuré. Testez ce comportement sur la table concernée : les caractéristiques d’ingestion varient selon les sources.

Lire la santé du connecteur sans la prendre pour une preuve complète

Activez l’audit et le health monitoring Microsoft Sentinel s’ils font partie du modèle d’exploitation du workspace. Interrogez _SentinelHealth() et le workbook Data collection health monitoring, tout en gardant à l’esprit que tous les connecteurs n’ont pas la même couverture. Un statut vert ne prouve pas qu’une table, un tenant ou un équipement précis produit les lignes attendues.

kusto 03-read-sentinel-health.kql
let Start = ago(24h);
_SentinelHealth()
| where TimeGenerated >= Start
| where SentinelResourceType in ("Data connector", "Analytics rule")
| where SentinelResourceName has_any ("detect-admin-group-change", "CommonSecurityLog")
| project TimeGenerated, SentinelResourceType, SentinelResourceKind,
        SentinelResourceName, Status, Description, ExtendedProperties
| order by TimeGenerated desc

Utilisez la santé comme une couche de preuve. Corrélez-la avec le backlog source, les diagnostics de l’agent ou de l’API, la fraîcheur de la table et les changements Azure Activity. SentinelAudit peut montrer que la règle a changé; il ne prouve pas que la nouvelle requête respecte le contrat de détection.

Prouver que la règle a exécuté la bonne fenêtre

Une requête correcte peut tout de même échouer une fois planifiée. Une fonction peut manquer, une référence cross-workspace être indisponible, la requête dépasser les ressources ou toutes les tentatives échouer. Regroupez les événements de santé par début de fenêtre afin d’identifier une exécution qui n’a jamais abouti.

kusto 04-find-skipped-rule-windows.kql
_SentinelHealth()
| where TimeGenerated >= ago(24h)
| where SentinelResourceType == "Analytics rule"
| where SentinelResourceKind == "Scheduled"
| where SentinelResourceName == "detect-admin-group-change"
| extend QueryStart = todatetime(ExtendedProperties["QueryStartTimeUTC"]),
       ExecutionStart = todatetime(ExtendedProperties["executionStart"])
| summarize Attempts = count(),
          SuccessfulAttempts = countif(Status == "Success"),
          LastSeen = max(TimeGenerated)
by QueryStart, SentinelResourceId
| where SuccessfulAttempts == 0
| order by QueryStart desc

Ne concluez pas à partir d’une seule tentative en échec : les règles planifiées peuvent rejouer la même fenêtre. Conservez ExtendedProperties dans le dossier d’incident avant toute modification de la règle.

Rejouer la requête déployée par étapes

Exécutez exactement la requête déployée avec des bornes temporelles fixes qui incluent l’événement témoin. Retirez ensuite une couche à la fois : agrégation du seuil, joins, watchlists, fonctions, puis filtres métier. La première étape qui perd l’événement localise la frontière à réparer.

text sentinel-query-replay-matrix.txt
Etape 1 - ligne source
L'identifiant stable existe dans la table attendue

Etape 2 - contrat temporel
Une fenetre TimeGenerated fixe inclut l'evenement

Etape 3 - enrichissement
Joins, watchlists et fonctions conservent l'evenement

Etape 4 - logique de detection
Filtres metier et agregation produisent le resultat attendu

Etape 5 - execution planifiee
La sante de la regle montre un succes sur la fenetre cible

Etape 6 - alerte et incident
Entity mapping, regroupement et parametres creent un seul incident canari

Etape 7 - automatisation aval
Automation rules et playbooks conservent l'etat d'incident attendu

Cet ordre évite de « corriger » une panne de collecte avec du KQL et d’attribuer à l’ingestion un problème de création d’incident.

Valider avec un canari borné

Clonez la règle sous un nom canari ou déployez une variante de test paramétrée sur un marqueur contrôlé. Gardez toute remédiation automatique désactivée. Rejouez un événement bénin depuis une source approuvée, puis capturez une chaîne unique : ID source, ligne de table, heure d’ingestion, exécution de règle, ID d’alerte et ID d’incident.

La validation réussit uniquement si l’événement produit exactement une alerte et un incident dans le budget de latence mesuré, avec les entités attendues et sans automatisation imprévue. Exécutez aussi un cas négatif qui ne doit pas alerter. Un test positif seul ne détecte pas une réparation devenue trop large.

Décider réparation, attente ou rollback

Réparez le connecteur ou la source si la ligne n’arrive jamais. Ajustez la fenêtre uniquement si la latence mesurée dépasse le contrat, avec un garde-fou testé sur l’heure d’ingestion pour maîtriser les doublons. Corrigez le KQL lorsque le rejeu fixe montre la disparition de l’événement à un filtre ou enrichissement identifié. Ne touchez aux paramètres d’incident ou à l’automatisation aval qu’après avoir prouvé l’alerte.

Bloquez le changement si le health monitoring est absent, si l’événement source n’est pas identifiable ou si la règle déployée diffère de la version relue. Rollbackez si le canari crée des doublons, élargit le résultat sans explication, perd l’entity mapping ou dépasse le budget d’exécution. Restaurez la définition capturée, désactivez le canari, puis rejouez les cas positif et négatif.

Conclusion

Une détection Microsoft Sentinel silencieuse n’est pas automatiquement un problème de fenêtre trop courte. C’est une chaîne qui va de l’émission source à l’ingestion, l’exécution KQL, la création d’alerte, le regroupement d’incident et l’automatisation. Prouvez un événement attendu à chaque frontière avant d’éditer la règle.

N’élargissez la fenêtre qu’à partir d’une latence mesurée et avec un contrôle des doublons. Sinon, réparez la couche en défaut, validez avec un canari borné et gardez la définition initiale prête pour le rollback.