Microsoft Sentinel Analytic Rules
cloudbrothers.infoAzure Sentinel RepoToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

Power Apps - Bulk sharing of Power Apps to newly created guest users

Back
Id943acfa0-9285-4eb0-a9c0-42e36177ef19
RulenamePower Apps - Bulk sharing of Power Apps to newly created guest users
DescriptionIdentifies unusual bulk sharing, based on a predefined threshold in the query, of Power Apps to newly created Microsoft Entra guest users.
SeverityMedium
TacticsResourceDevelopment
InitialAccess
LateralMovement
TechniquesT1587
T1566
T1534
Required data connectorsAzureActiveDirectory
PowerPlatformAdmin
KindScheduled
Query frequency1h
Query period14d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Business Applications/Analytic Rules/Power Apps - Bulk sharing of Power Apps to newly created guest users.yaml
Version3.2.0
Arm template943acfa0-9285-4eb0-a9c0-42e36177ef19.json
Deploy To Azure
////////////
// threshold = If the number of unique accounts that a power app is shared with is greater than
// threshold than it'll trigger an alert. A threshold of 5 is good to start with.
// However, if this is giving too many false positives, please adjust the threshold.
////////////
let threshold = 5;
////////////
// Please replace the allowed_domains with a list of domains of your partners/sibling orgs
// with whom you generally share power apps with. This will allow us to filter
// legitimate bulk sharing attempts. Avoid using domains such as gmail, outlook, etc.
///////////
let allowed_domains = pack_array("contoso.com");
let query_frequency = 1h;
let query_lookback = 14d;
PowerPlatformAdminActivity
| where TimeGenerated >= ago(query_frequency)
| where EventOriginalType == "PowerAppPermissionEdited"
| extend Properties = tostring(PropertyCollection)
| extend AppId = extract(@'"powerplatform.analytics.resource.power_app.id","Value":"([^"]+)"', 1, Properties)
| extend AppId = tolower(replace_string(AppId, '/providers/Microsoft.PowerApps/apps/', ''))
| extend TargetPrincipalId = extract(@'"targetuser.id","Value":"([^"]+)"', 1, Properties)
| join kind=leftouter (
    AuditLogs
    | where ActivityDateTime >= ago(query_lookback)
    | where SourceSystem =~ "Azure AD" and OperationName == "Invite external user"
    | where Result =~ "success"
    | extend InvitedOrgEmail = tostring(parse_json(AdditionalDetails[5])['value'])
    | extend InvitedOrgDomain = tostring(split(InvitedOrgEmail, "@")[1])
    | where not(InvitedOrgDomain has_any(allowed_domains))
    | extend
        InvitedById = tostring(parse_json(InitiatedBy)['user']['id']),
        InvitedByUPN = tostring(parse_json(InitiatedBy)['user']['userPrincipalName']),
        InvitedEmail = tostring(parse_json(TargetResources[0])['userPrincipalName']),
        InvitedId = tostring(parse_json(TargetResources[0])['id'])
    | summarize by InvitedById, InvitedByUPN, InvitedEmail, InvitedId, InvitedOrgDomain)
    on $left.TargetPrincipalId == $right.InvitedId
| where isnotempty(InvitedId)
| summarize
    StartTime = min(TimeGenerated),
    EndTime = max(TimeGenerated),
    TargetedUsersCount=dcount(TargetPrincipalId),
    TargetedObjectIds = make_set(TargetPrincipalId, 1000),
    InvitedDomains = make_set(InvitedOrgDomain, 1000),
    InvitedEmailAddresses = make_set(InvitedEmail, 1000)
    by AppId, InvitedById, InvitedByUPN
| extend
    PowerAppsEntityId = 27593,
    AccountName = tostring(split(InvitedByUPN, '@')[0]),
    UPNSuffix = tostring(split(InvitedByUPN, '@')[1])
| project
    StartTime,
    EndTime,
    InvitedByUPN,
    InvitedById,
    InvitedDomains,
    InvitedEmailAddresses,
    TargetedUsersCount,
    TargetedObjectIds,
    AppId,
    PowerAppsEntityId,
    AccountName,
    UPNSuffix
customDetails:
  PowerAppsApp: AppId
queryPeriod: 14d
id: 943acfa0-9285-4eb0-a9c0-42e36177ef19
relevantTechniques:
- T1587
- T1566
- T1534
triggerOperator: gt
entityMappings:
- fieldMappings:
  - columnName: AccountName
    identifier: Name
  - columnName: UPNSuffix
    identifier: UPNSuffix
  entityType: Account
- fieldMappings:
  - columnName: PowerAppsEntityId
    identifier: AppId
  - columnName: AppId
    identifier: InstanceName
  entityType: CloudApplication
query: |
  ////////////
  // threshold = If the number of unique accounts that a power app is shared with is greater than
  // threshold than it'll trigger an alert. A threshold of 5 is good to start with.
  // However, if this is giving too many false positives, please adjust the threshold.
  ////////////
  let threshold = 5;
  ////////////
  // Please replace the allowed_domains with a list of domains of your partners/sibling orgs
  // with whom you generally share power apps with. This will allow us to filter
  // legitimate bulk sharing attempts. Avoid using domains such as gmail, outlook, etc.
  ///////////
  let allowed_domains = pack_array("contoso.com");
  let query_frequency = 1h;
  let query_lookback = 14d;
  PowerPlatformAdminActivity
  | where TimeGenerated >= ago(query_frequency)
  | where EventOriginalType == "PowerAppPermissionEdited"
  | extend Properties = tostring(PropertyCollection)
  | extend AppId = extract(@'"powerplatform.analytics.resource.power_app.id","Value":"([^"]+)"', 1, Properties)
  | extend AppId = tolower(replace_string(AppId, '/providers/Microsoft.PowerApps/apps/', ''))
  | extend TargetPrincipalId = extract(@'"targetuser.id","Value":"([^"]+)"', 1, Properties)
  | join kind=leftouter (
      AuditLogs
      | where ActivityDateTime >= ago(query_lookback)
      | where SourceSystem =~ "Azure AD" and OperationName == "Invite external user"
      | where Result =~ "success"
      | extend InvitedOrgEmail = tostring(parse_json(AdditionalDetails[5])['value'])
      | extend InvitedOrgDomain = tostring(split(InvitedOrgEmail, "@")[1])
      | where not(InvitedOrgDomain has_any(allowed_domains))
      | extend
          InvitedById = tostring(parse_json(InitiatedBy)['user']['id']),
          InvitedByUPN = tostring(parse_json(InitiatedBy)['user']['userPrincipalName']),
          InvitedEmail = tostring(parse_json(TargetResources[0])['userPrincipalName']),
          InvitedId = tostring(parse_json(TargetResources[0])['id'])
      | summarize by InvitedById, InvitedByUPN, InvitedEmail, InvitedId, InvitedOrgDomain)
      on $left.TargetPrincipalId == $right.InvitedId
  | where isnotempty(InvitedId)
  | summarize
      StartTime = min(TimeGenerated),
      EndTime = max(TimeGenerated),
      TargetedUsersCount=dcount(TargetPrincipalId),
      TargetedObjectIds = make_set(TargetPrincipalId, 1000),
      InvitedDomains = make_set(InvitedOrgDomain, 1000),
      InvitedEmailAddresses = make_set(InvitedEmail, 1000)
      by AppId, InvitedById, InvitedByUPN
  | extend
      PowerAppsEntityId = 27593,
      AccountName = tostring(split(InvitedByUPN, '@')[0]),
      UPNSuffix = tostring(split(InvitedByUPN, '@')[1])
  | project
      StartTime,
      EndTime,
      InvitedByUPN,
      InvitedById,
      InvitedDomains,
      InvitedEmailAddresses,
      TargetedUsersCount,
      TargetedObjectIds,
      AppId,
      PowerAppsEntityId,
      AccountName,
      UPNSuffix  
kind: Scheduled
triggerThreshold: 0
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Business Applications/Analytic Rules/Power Apps - Bulk sharing of Power Apps to newly created guest users.yaml
queryFrequency: 1h
requiredDataConnectors:
- dataTypes:
  - PowerPlatformAdminActivity
  connectorId: PowerPlatformAdmin
- dataTypes:
  - AuditLogs
  connectorId: AzureActiveDirectory
eventGroupingSettings:
  aggregationKind: SingleAlert
name: Power Apps - Bulk sharing of Power Apps to newly created guest users
version: 3.2.0
description: Identifies unusual bulk sharing, based on a predefined threshold in the query, of Power Apps to newly created Microsoft Entra guest users.
alertDetailsOverride:
  alertDisplayNameFormat: Power Apps - app shared with recently created external guest accounts
  alertDescriptionFormat: '{{InvitedByUPN}}  shared an app with {{TargetedUsersCount}} recently added guest user accounts that are not on the list of allowed partner domains. List of domain s {{InvitedDomains}}'
tactics:
- ResourceDevelopment
- InitialAccess
- LateralMovement
severity: Medium
status: Available
{
  "$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/943acfa0-9285-4eb0-a9c0-42e36177ef19')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/943acfa0-9285-4eb0-a9c0-42e36177ef19')]",
      "properties": {
        "alertDetailsOverride": {
          "alertDescriptionFormat": "{{InvitedByUPN}}  shared an app with {{TargetedUsersCount}} recently added guest user accounts that are not on the list of allowed partner domains. List of domain s {{InvitedDomains}}",
          "alertDisplayNameFormat": "Power Apps - app shared with recently created external guest accounts"
        },
        "alertRuleTemplateName": "943acfa0-9285-4eb0-a9c0-42e36177ef19",
        "customDetails": {
          "PowerAppsApp": "AppId"
        },
        "description": "Identifies unusual bulk sharing, based on a predefined threshold in the query, of Power Apps to newly created Microsoft Entra guest users.",
        "displayName": "Power Apps - Bulk sharing of Power Apps to newly created guest users",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "AccountName",
                "identifier": "Name"
              },
              {
                "columnName": "UPNSuffix",
                "identifier": "UPNSuffix"
              }
            ]
          },
          {
            "entityType": "CloudApplication",
            "fieldMappings": [
              {
                "columnName": "PowerAppsEntityId",
                "identifier": "AppId"
              },
              {
                "columnName": "AppId",
                "identifier": "InstanceName"
              }
            ]
          }
        ],
        "eventGroupingSettings": {
          "aggregationKind": "SingleAlert"
        },
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Business Applications/Analytic Rules/Power Apps - Bulk sharing of Power Apps to newly created guest users.yaml",
        "query": "////////////\n// threshold = If the number of unique accounts that a power app is shared with is greater than\n// threshold than it'll trigger an alert. A threshold of 5 is good to start with.\n// However, if this is giving too many false positives, please adjust the threshold.\n////////////\nlet threshold = 5;\n////////////\n// Please replace the allowed_domains with a list of domains of your partners/sibling orgs\n// with whom you generally share power apps with. This will allow us to filter\n// legitimate bulk sharing attempts. Avoid using domains such as gmail, outlook, etc.\n///////////\nlet allowed_domains = pack_array(\"contoso.com\");\nlet query_frequency = 1h;\nlet query_lookback = 14d;\nPowerPlatformAdminActivity\n| where TimeGenerated >= ago(query_frequency)\n| where EventOriginalType == \"PowerAppPermissionEdited\"\n| extend Properties = tostring(PropertyCollection)\n| extend AppId = extract(@'\"powerplatform.analytics.resource.power_app.id\",\"Value\":\"([^\"]+)\"', 1, Properties)\n| extend AppId = tolower(replace_string(AppId, '/providers/Microsoft.PowerApps/apps/', ''))\n| extend TargetPrincipalId = extract(@'\"targetuser.id\",\"Value\":\"([^\"]+)\"', 1, Properties)\n| join kind=leftouter (\n    AuditLogs\n    | where ActivityDateTime >= ago(query_lookback)\n    | where SourceSystem =~ \"Azure AD\" and OperationName == \"Invite external user\"\n    | where Result =~ \"success\"\n    | extend InvitedOrgEmail = tostring(parse_json(AdditionalDetails[5])['value'])\n    | extend InvitedOrgDomain = tostring(split(InvitedOrgEmail, \"@\")[1])\n    | where not(InvitedOrgDomain has_any(allowed_domains))\n    | extend\n        InvitedById = tostring(parse_json(InitiatedBy)['user']['id']),\n        InvitedByUPN = tostring(parse_json(InitiatedBy)['user']['userPrincipalName']),\n        InvitedEmail = tostring(parse_json(TargetResources[0])['userPrincipalName']),\n        InvitedId = tostring(parse_json(TargetResources[0])['id'])\n    | summarize by InvitedById, InvitedByUPN, InvitedEmail, InvitedId, InvitedOrgDomain)\n    on $left.TargetPrincipalId == $right.InvitedId\n| where isnotempty(InvitedId)\n| summarize\n    StartTime = min(TimeGenerated),\n    EndTime = max(TimeGenerated),\n    TargetedUsersCount=dcount(TargetPrincipalId),\n    TargetedObjectIds = make_set(TargetPrincipalId, 1000),\n    InvitedDomains = make_set(InvitedOrgDomain, 1000),\n    InvitedEmailAddresses = make_set(InvitedEmail, 1000)\n    by AppId, InvitedById, InvitedByUPN\n| extend\n    PowerAppsEntityId = 27593,\n    AccountName = tostring(split(InvitedByUPN, '@')[0]),\n    UPNSuffix = tostring(split(InvitedByUPN, '@')[1])\n| project\n    StartTime,\n    EndTime,\n    InvitedByUPN,\n    InvitedById,\n    InvitedDomains,\n    InvitedEmailAddresses,\n    TargetedUsersCount,\n    TargetedObjectIds,\n    AppId,\n    PowerAppsEntityId,\n    AccountName,\n    UPNSuffix\n",
        "queryFrequency": "PT1H",
        "queryPeriod": "P14D",
        "severity": "Medium",
        "status": "Available",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "InitialAccess",
          "LateralMovement",
          "ResourceDevelopment"
        ],
        "techniques": [
          "T1534",
          "T1566",
          "T1587"
        ],
        "templateVersion": "3.2.0",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}