top of page


  • Writer's pictureRoman Guoussev-Donskoi

Use Azure AD Groups membership in Sentinel rules

Microsoft Sentinel custom analytics rules are amazingly simple yet powerful way to create security incidents based or events specific to your environment.

Microsoft provides good documentation and steps to Create custom analytics rules to detect threats

One of the use cases we have encountered is ability to monitor that changes and access to Azure resources are performed by expected groups of users.

Azure provides capabilities to log and monitor access easily (for example we can use AzureActivity and StorageBlobLogs tables in Azure Log Analytics). But cross-checking access against specific groups in Azure Active Directory requires additional steps as information about membership of Azure AD groups is not directly available in Log Analytics.

There are multiple ways to achieve this but for us the easiest to implement and maintain was to leverage Kusto "externaldata" operator.

To leverage "externaldata" for Azure AD group membership we created a SQL table with object IDs and names of groups that are authorized to access resources to be monitored. Then we created Azure function that on daily basis reads membership of those groups and logs it into a json file in Azure Storage account.

Once this is done validating access to Azure resources against Azure AD groups becomes a breeze and can be done in a single Kusto join query.

  1. use "externaldata" operator to access blob

  2. Reference blob that contains Azure AD group membership info

  3. Provide Shared Access Signature (SAS) to ensure this information remains private and secure

  4. Limit the output to the selected group of authorized users

  5. Read Azure Activity Logs in Log Analytics workspace (assume you collecting all your Azure Changes in Log Analytics of course)

  6. Specify timeframe to used to read access logs (suggest keep this low for performance reasons)

  7. "anti-join" returns only users that do not belong to the expected group

  8. join based on user principal name (of course can optimize this query to make more efficient but for illustration purposes left as-is)

The complete query is below. Just replace %your-storageaccount%, %your-SAS%, %your-group-name%, and %your-subscriptionid% according to your environment values.

let GroupMembers = externaldata(GroupObjectID:string,GroupName:string,UserPrincipalName: string)

with(format='multijson', ingestionMapping='[{"Column":"GroupObjectID","Properties":{"Path":"$.GroupObjectID"}},{"Column":"GroupName","Properties":{"Path":"$.GroupName"}},{"Column":"UserPrincipalName","Properties":{"Path":"$.UserPrincipalName"}}]') | where GroupName == '%your-group-name%';

AzureActivity | where SubscriptionId == '%your-subscriptionid%' and Caller contains '@' and TimeGenerated > ago(10d) | extend CallerLower =  tolower(Caller) |
join kind=leftanti(GroupMembers ) on $left.CallerLower == $right.UserPrincipalName

Hope you find this approach useful.

Special Thanks to Tian Zheng for working together on this scenario!

2,480 views0 comments

Recent Posts

See All

Query SQL using OpenAI and Langchain

LLMs (such as OpenAI) are good for reasoning but they lack capability interface with outside world. This is where Langchain agents step in: agents provide LLMs with tools to perform actions (for examp


Home: Blog2


Home: GetSubscribers_Widget


Your details were sent successfully!

Home: Contact
bottom of page