Query Details

RULE 02 Diagnostic Settings Deleted Without Recreation

Query

// Rule    : Azure - Diagnostic Settings Permanently Deleted Without Recreation
// Severity: High
// Tactics : DefenseEvasion
// MITRE   : T1562
// Freq    : PT1H   Period: PT2H
//==========================================================================================

let ExcludedCallerPatterns = dynamic(["terraform", "bicep", "pipeline", "github", "pulumi", "arm-"]);
let Deletions = AzureActivity
    | where TimeGenerated > ago(2h)
    | where OperationNameValue =~ "MICROSOFT.INSIGHTS/DIAGNOSTICSETTINGS/DELETE"
    | where ActivityStatusValue =~ "Success"
    | where not(tolower(Caller) has_any (ExcludedCallerPatterns))
    | where isnotempty(CallerIpAddress)
    | project DeletionTime = TimeGenerated, Caller, ResourceId, CallerIpAddress, SubscriptionId, ResourceGroup;
let Recreations = AzureActivity
    | where TimeGenerated > ago(2h)
    | where OperationNameValue =~ "MICROSOFT.INSIGHTS/DIAGNOSTICSETTINGS/WRITE"
    | where ActivityStatusValue =~ "Success"
    | summarize EarliestRecreation = min(TimeGenerated) by ResourceId;
Deletions
| join kind=leftouter Recreations on ResourceId
| where isnull(EarliestRecreation) or EarliestRecreation > DeletionTime + 30m
| summarize
    DeletionCount = count(),
    DeletedSettings = make_set(ResourceId, 10),
    SourceIPs = make_set(CallerIpAddress, 5),
    CallerIP = any(CallerIpAddress),
    FirstDeletion = min(DeletionTime),
    LastDeletion = max(DeletionTime)
    by Caller, SubscriptionId, ResourceGroup
| where DeletionCount >= 1
| extend
    AccountName = tostring(split(Caller, "@")[0]),
    AccountUPNSuffix = tostring(split(Caller, "@")[1])

Explanation

This query is designed to detect and report on instances where diagnostic settings in Azure are permanently deleted without being recreated within a specified timeframe. Here's a simplified breakdown of what the query does:

  1. Excluded Callers: It defines a list of caller patterns (e.g., "terraform", "bicep") that should be ignored because they are typically associated with automated processes.

  2. Identify Deletions: It looks for successful deletion operations of diagnostic settings within the last 2 hours, excluding those initiated by the excluded callers. It also ensures that the caller's IP address is available.

  3. Identify Recreations: It searches for successful recreation (write) operations of diagnostic settings within the same timeframe and notes the earliest recreation time for each resource.

  4. Join and Filter: It performs a left outer join between deletions and recreations based on the resource ID. It filters out cases where no recreation occurred or where the recreation happened more than 30 minutes after the deletion.

  5. Summarize Results: For each caller, subscription, and resource group, it summarizes the data by counting the number of deletions, listing the deleted settings, source IPs, and noting the first and last deletion times.

  6. Filter and Extend: It only includes cases where at least one deletion occurred and extracts the account name and UPN suffix from the caller's email address.

In essence, this query helps identify potentially suspicious activities where diagnostic settings are deleted and not promptly recreated, which could indicate an attempt to evade defenses.

Details

David Alonso profile picture

David Alonso

Released: March 12, 2026

Tables

AzureActivity

Keywords

AzureActivityCallerResourceIdSubscriptionIdResourceGroupCallerIpAddress

Operators

letdynamicago=~has_anytolowerisnotemptyprojectsummarizeminjoinkind=leftouteronwhereisnull>+30mcountmake_setanyminmaxby>=extendtostringsplit

Actions