Back
Idbb616d82-108f-47d3-9dec-9652ea0d3bf6
RulenameAccount Created and Deleted in Short Timeframe
DescriptionSearch for user principal name (UPN) events. Look for accounts created and then deleted within 7 days. Attackers may create an account for their use, and then remove the account when no longer needed.

Ref : https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-user-accounts#short-lived-account
SeverityHigh
TacticsInitialAccess
TechniquesT1078.004
Required data connectorsAzureActiveDirectory
KindScheduled
Query frequency1h
Query period7d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft%20Entra%20ID/Analytic%20Rules/AccountCreatedandDeletedinShortTimeframe.yaml
Version1.1.2
Arm templatebb616d82-108f-47d3-9dec-9652ea0d3bf6.json
Deploy To Azure
let queryfrequency = 1h;
let queryperiod = 7d; // Increased queryperiod to 7 days to mitigate timing-based bypasses
AuditLogs
| where TimeGenerated > ago(queryfrequency)
| where OperationName =~ "Delete user"
| mv-apply TargetResource = TargetResources on 
  (
      where TargetResource.type == "User"
      // Normalize TargetUserPrincipalName by removing optional 32-char hex prefixes and underscores
      // Making extraction case-insensitive, and converting the result to lowercase for consistency
      | extend TargetUserPrincipalName = tolower(extract(@"(?i)([a-f0-9]{32})?_?(.*)", 2, tostring(TargetResource.userPrincipalName)))
      // Standardize UserId to ensure consistent, immutable identification
      | extend UserId = tostring(TargetResource.id)
  )
| extend DeletedByApp = tostring(InitiatedBy.app.displayName),
DeletedByAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId),
DeletedByUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName),
DeletedByAadUserId = tostring(InitiatedBy.user.id),
DeletedByIPAddress = tostring(InitiatedBy.user.ipAddress)
| project Deletion_TimeGenerated = TimeGenerated, TargetUserPrincipalName, UserId, DeletedByApp, DeletedByAppServicePrincipalId, DeletedByUserPrincipalName, DeletedByAadUserId, DeletedByIPAddress, 
Deletion_AdditionalDetails = AdditionalDetails, Deletion_InitiatedBy = InitiatedBy, Deletion_TargetResources = TargetResources
| join kind=inner (
    AuditLogs
    | where TimeGenerated > ago(queryperiod)
    | where OperationName =~ "Add user"      
    | mv-apply TargetResource = TargetResources on 
      (
          where TargetResource.type == "User"
          // Normalize TargetUserPrincipalName by removing optional 32-char hex prefixes and underscores
          // Making extraction case-insensitive, and converting the result to lowercase for consistency
          | extend TargetUserPrincipalName = tolower(extract(@"(?i)([a-f0-9]{32})?_?(.*)", 2, tostring(TargetResource.userPrincipalName)))
          // Standardize UserId to ensure consistent, immutable identification
          | extend UserId = tostring(TargetResource.id)
      )
    | project-rename Creation_TimeGenerated = TimeGenerated
) on UserId
| extend TimeDelta = Deletion_TimeGenerated - Creation_TimeGenerated
| where  TimeDelta between (time(0s) .. queryperiod)
| extend CreatedByApp = tostring(InitiatedBy.app.displayName),
CreatedByAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId),
CreatedByUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName),
CreatedByAadUserId = tostring(InitiatedBy.user.id),
CreatedByIPAddress = tostring(InitiatedBy.user.ipAddress)
| project Creation_TimeGenerated, Deletion_TimeGenerated, TimeDelta, UserId, TargetUserPrincipalName, DeletedByApp, DeletedByAppServicePrincipalId, DeletedByUserPrincipalName, DeletedByAadUserId, DeletedByIPAddress, 
CreatedByApp, CreatedByAppServicePrincipalId, CreatedByUserPrincipalName, CreatedByAadUserId, CreatedByIPAddress, Creation_AdditionalDetails = AdditionalDetails, Creation_InitiatedBy = InitiatedBy, Creation_TargetResources = TargetResources, Deletion_AdditionalDetails, Deletion_InitiatedBy, Deletion_TargetResources
| extend TargetName = tostring(split(TargetUserPrincipalName,'@',0)[0]), TargetUPNSuffix = tostring(split(TargetUserPrincipalName,'@',1)[0])
| extend CreatedByName = tostring(split(CreatedByUserPrincipalName,'@',0)[0]), CreatedByUPNSuffix = tostring(split(CreatedByUserPrincipalName,'@',1)[0])
| extend DeletedByName = tostring(split(DeletedByUserPrincipalName,'@',0)[0]), DeletedByUPNSuffix = tostring(split(DeletedByUserPrincipalName,'@',1)[0])
entityMappings:
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: TargetUserPrincipalName
  - identifier: Name
    columnName: TargetName
  - identifier: UPNSuffix
    columnName: TargetUPNSuffix
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: CreatedByUserPrincipalName
  - identifier: Name
    columnName: CreatedByName
  - identifier: UPNSuffix
    columnName: CreatedByUPNSuffix
- entityType: Account
  fieldMappings:
  - identifier: AadUserId
    columnName: CreatedByAadUserId
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: DeletedByUserPrincipalName
  - identifier: Name
    columnName: DeletedByName
  - identifier: UPNSuffix
    columnName: DeletedByUPNSuffix
- entityType: Account
  fieldMappings:
  - identifier: AadUserId
    columnName: DeletedByAadUserId
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: CreatedByIPAddress
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: DeletedByIPAddress
name: Account Created and Deleted in Short Timeframe
kind: Scheduled
triggerOperator: gt
status: Available
queryPeriod: 7d
requiredDataConnectors:
- connectorId: AzureActiveDirectory
  dataTypes:
  - SigninLogs
tactics:
- InitialAccess
query: |
  let queryfrequency = 1h;
  let queryperiod = 7d; // Increased queryperiod to 7 days to mitigate timing-based bypasses
  AuditLogs
  | where TimeGenerated > ago(queryfrequency)
  | where OperationName =~ "Delete user"
  | mv-apply TargetResource = TargetResources on 
    (
        where TargetResource.type == "User"
        // Normalize TargetUserPrincipalName by removing optional 32-char hex prefixes and underscores
        // Making extraction case-insensitive, and converting the result to lowercase for consistency
        | extend TargetUserPrincipalName = tolower(extract(@"(?i)([a-f0-9]{32})?_?(.*)", 2, tostring(TargetResource.userPrincipalName)))
        // Standardize UserId to ensure consistent, immutable identification
        | extend UserId = tostring(TargetResource.id)
    )
  | extend DeletedByApp = tostring(InitiatedBy.app.displayName),
  DeletedByAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId),
  DeletedByUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName),
  DeletedByAadUserId = tostring(InitiatedBy.user.id),
  DeletedByIPAddress = tostring(InitiatedBy.user.ipAddress)
  | project Deletion_TimeGenerated = TimeGenerated, TargetUserPrincipalName, UserId, DeletedByApp, DeletedByAppServicePrincipalId, DeletedByUserPrincipalName, DeletedByAadUserId, DeletedByIPAddress, 
  Deletion_AdditionalDetails = AdditionalDetails, Deletion_InitiatedBy = InitiatedBy, Deletion_TargetResources = TargetResources
  | join kind=inner (
      AuditLogs
      | where TimeGenerated > ago(queryperiod)
      | where OperationName =~ "Add user"      
      | mv-apply TargetResource = TargetResources on 
        (
            where TargetResource.type == "User"
            // Normalize TargetUserPrincipalName by removing optional 32-char hex prefixes and underscores
            // Making extraction case-insensitive, and converting the result to lowercase for consistency
            | extend TargetUserPrincipalName = tolower(extract(@"(?i)([a-f0-9]{32})?_?(.*)", 2, tostring(TargetResource.userPrincipalName)))
            // Standardize UserId to ensure consistent, immutable identification
            | extend UserId = tostring(TargetResource.id)
        )
      | project-rename Creation_TimeGenerated = TimeGenerated
  ) on UserId
  | extend TimeDelta = Deletion_TimeGenerated - Creation_TimeGenerated
  | where  TimeDelta between (time(0s) .. queryperiod)
  | extend CreatedByApp = tostring(InitiatedBy.app.displayName),
  CreatedByAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId),
  CreatedByUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName),
  CreatedByAadUserId = tostring(InitiatedBy.user.id),
  CreatedByIPAddress = tostring(InitiatedBy.user.ipAddress)
  | project Creation_TimeGenerated, Deletion_TimeGenerated, TimeDelta, UserId, TargetUserPrincipalName, DeletedByApp, DeletedByAppServicePrincipalId, DeletedByUserPrincipalName, DeletedByAadUserId, DeletedByIPAddress, 
  CreatedByApp, CreatedByAppServicePrincipalId, CreatedByUserPrincipalName, CreatedByAadUserId, CreatedByIPAddress, Creation_AdditionalDetails = AdditionalDetails, Creation_InitiatedBy = InitiatedBy, Creation_TargetResources = TargetResources, Deletion_AdditionalDetails, Deletion_InitiatedBy, Deletion_TargetResources
  | extend TargetName = tostring(split(TargetUserPrincipalName,'@',0)[0]), TargetUPNSuffix = tostring(split(TargetUserPrincipalName,'@',1)[0])
  | extend CreatedByName = tostring(split(CreatedByUserPrincipalName,'@',0)[0]), CreatedByUPNSuffix = tostring(split(CreatedByUserPrincipalName,'@',1)[0])
  | extend DeletedByName = tostring(split(DeletedByUserPrincipalName,'@',0)[0]), DeletedByUPNSuffix = tostring(split(DeletedByUserPrincipalName,'@',1)[0])
description: |
  'Search for user principal name (UPN) events. Look for accounts created and then deleted within 7 days. Attackers may create an account for their use, and then remove the account when no longer needed.
  Ref : https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-user-accounts#short-lived-account'
severity: High
tags:
- AADSecOpsGuide
id: bb616d82-108f-47d3-9dec-9652ea0d3bf6
triggerThreshold: 0
queryFrequency: 1h
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft%20Entra%20ID/Analytic%20Rules/AccountCreatedandDeletedinShortTimeframe.yaml
version: 1.1.2
relevantTechniques:
- T1078.004
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "apiVersion": "2024-01-01-preview",
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/bb616d82-108f-47d3-9dec-9652ea0d3bf6')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/bb616d82-108f-47d3-9dec-9652ea0d3bf6')]",
      "properties": {
        "alertRuleTemplateName": "bb616d82-108f-47d3-9dec-9652ea0d3bf6",
        "customDetails": null,
        "description": "'Search for user principal name (UPN) events. Look for accounts created and then deleted within 7 days. Attackers may create an account for their use, and then remove the account when no longer needed.\nRef : https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-user-accounts#short-lived-account'\n",
        "displayName": "Account Created and Deleted in Short Timeframe",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "TargetUserPrincipalName",
                "identifier": "FullName"
              },
              {
                "columnName": "TargetName",
                "identifier": "Name"
              },
              {
                "columnName": "TargetUPNSuffix",
                "identifier": "UPNSuffix"
              }
            ]
          },
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "CreatedByUserPrincipalName",
                "identifier": "FullName"
              },
              {
                "columnName": "CreatedByName",
                "identifier": "Name"
              },
              {
                "columnName": "CreatedByUPNSuffix",
                "identifier": "UPNSuffix"
              }
            ]
          },
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "CreatedByAadUserId",
                "identifier": "AadUserId"
              }
            ]
          },
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "DeletedByUserPrincipalName",
                "identifier": "FullName"
              },
              {
                "columnName": "DeletedByName",
                "identifier": "Name"
              },
              {
                "columnName": "DeletedByUPNSuffix",
                "identifier": "UPNSuffix"
              }
            ]
          },
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "DeletedByAadUserId",
                "identifier": "AadUserId"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "CreatedByIPAddress",
                "identifier": "Address"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "DeletedByIPAddress",
                "identifier": "Address"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft%20Entra%20ID/Analytic%20Rules/AccountCreatedandDeletedinShortTimeframe.yaml",
        "query": "let queryfrequency = 1h;\nlet queryperiod = 7d; // Increased queryperiod to 7 days to mitigate timing-based bypasses\nAuditLogs\n| where TimeGenerated > ago(queryfrequency)\n| where OperationName =~ \"Delete user\"\n| mv-apply TargetResource = TargetResources on \n  (\n      where TargetResource.type == \"User\"\n      // Normalize TargetUserPrincipalName by removing optional 32-char hex prefixes and underscores\n      // Making extraction case-insensitive, and converting the result to lowercase for consistency\n      | extend TargetUserPrincipalName = tolower(extract(@\"(?i)([a-f0-9]{32})?_?(.*)\", 2, tostring(TargetResource.userPrincipalName)))\n      // Standardize UserId to ensure consistent, immutable identification\n      | extend UserId = tostring(TargetResource.id)\n  )\n| extend DeletedByApp = tostring(InitiatedBy.app.displayName),\nDeletedByAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId),\nDeletedByUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName),\nDeletedByAadUserId = tostring(InitiatedBy.user.id),\nDeletedByIPAddress = tostring(InitiatedBy.user.ipAddress)\n| project Deletion_TimeGenerated = TimeGenerated, TargetUserPrincipalName, UserId, DeletedByApp, DeletedByAppServicePrincipalId, DeletedByUserPrincipalName, DeletedByAadUserId, DeletedByIPAddress, \nDeletion_AdditionalDetails = AdditionalDetails, Deletion_InitiatedBy = InitiatedBy, Deletion_TargetResources = TargetResources\n| join kind=inner (\n    AuditLogs\n    | where TimeGenerated > ago(queryperiod)\n    | where OperationName =~ \"Add user\"      \n    | mv-apply TargetResource = TargetResources on \n      (\n          where TargetResource.type == \"User\"\n          // Normalize TargetUserPrincipalName by removing optional 32-char hex prefixes and underscores\n          // Making extraction case-insensitive, and converting the result to lowercase for consistency\n          | extend TargetUserPrincipalName = tolower(extract(@\"(?i)([a-f0-9]{32})?_?(.*)\", 2, tostring(TargetResource.userPrincipalName)))\n          // Standardize UserId to ensure consistent, immutable identification\n          | extend UserId = tostring(TargetResource.id)\n      )\n    | project-rename Creation_TimeGenerated = TimeGenerated\n) on UserId\n| extend TimeDelta = Deletion_TimeGenerated - Creation_TimeGenerated\n| where  TimeDelta between (time(0s) .. queryperiod)\n| extend CreatedByApp = tostring(InitiatedBy.app.displayName),\nCreatedByAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId),\nCreatedByUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName),\nCreatedByAadUserId = tostring(InitiatedBy.user.id),\nCreatedByIPAddress = tostring(InitiatedBy.user.ipAddress)\n| project Creation_TimeGenerated, Deletion_TimeGenerated, TimeDelta, UserId, TargetUserPrincipalName, DeletedByApp, DeletedByAppServicePrincipalId, DeletedByUserPrincipalName, DeletedByAadUserId, DeletedByIPAddress, \nCreatedByApp, CreatedByAppServicePrincipalId, CreatedByUserPrincipalName, CreatedByAadUserId, CreatedByIPAddress, Creation_AdditionalDetails = AdditionalDetails, Creation_InitiatedBy = InitiatedBy, Creation_TargetResources = TargetResources, Deletion_AdditionalDetails, Deletion_InitiatedBy, Deletion_TargetResources\n| extend TargetName = tostring(split(TargetUserPrincipalName,'@',0)[0]), TargetUPNSuffix = tostring(split(TargetUserPrincipalName,'@',1)[0])\n| extend CreatedByName = tostring(split(CreatedByUserPrincipalName,'@',0)[0]), CreatedByUPNSuffix = tostring(split(CreatedByUserPrincipalName,'@',1)[0])\n| extend DeletedByName = tostring(split(DeletedByUserPrincipalName,'@',0)[0]), DeletedByUPNSuffix = tostring(split(DeletedByUserPrincipalName,'@',1)[0])\n",
        "queryFrequency": "PT1H",
        "queryPeriod": "P7D",
        "severity": "High",
        "status": "Available",
        "subTechniques": [
          "T1078.004"
        ],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "InitialAccess"
        ],
        "tags": [
          "AADSecOpsGuide"
        ],
        "techniques": [
          "T1078"
        ],
        "templateVersion": "1.1.2",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}