Query Details

Copilot Agents Sharing

Query

# Copilot - Agents - Sharing

![KQL](https://img.shields.io/badge/language-KQL-blue.svg)
![Status: Testing](https://img.shields.io/badge/status-testing-blue.svg)

## Query Information

### Description

Retrieve Copilot - Agent - Sharing Settings changes

Sharing allows defining who can share agents within your organization and how sharing works. Options include:

- Allow all users to share with anyone in the organization - All users can share their agents with others in your tenant.
- No users can share with anyone in the organization - Sharing is disabled at the org level, but users can still share directly with specific individuals.
- Allow specific groups of users to share with anyone in the organization - Restrict broad sharing permissions to designated groups.

Only agents built with Agent Builder are governed by sharing control.

#### References

- [Agent settings in Microsoft 365 admin center](https://learn.microsoft.com/en-us/microsoft-365/admin/manage/agent-settings?view=o365-worldwide)

### Author
- **Alex Verboon**

## Defender XDR

```kql
// sharing
CloudAppEvents
| where Application == "Microsoft 365"
| where ActionType == "UpdateTenantSettings"
| extend UserAccessSetting = tostring(parse_json(tostring(RawEventData.Resource)).Property)
| where UserAccessSetting == "AllowOrgWideSharing"
| extend ForAllUsers = tostring(RawEventData.ForAllUsers)
| extend NewValue = tostring(parse_json(tostring(parse_json(tostring(RawEventData.Resource)).NewValue)))
| extend OriginalValue = tostring(parse_json(tostring(parse_json(tostring(RawEventData.Resource)).OriginalValue)))
| extend RemovedIdentities = parse_json(RawEventData.RemovedIdentities)
| extend AddedIdentities = parse_json(RawEventData.AddedIdentities)
| extend Configuration = "Users who can share agents with anyone in the organization"
| extend ConfigurationState = case(
    // No Users: was All Users, now explicitly disabled
    ForAllUsers == "true"  and NewValue == "false", "No Users",
    // All Users: enabled for everyone
    ForAllUsers == "true"  and array_length(AddedIdentities) == 0 and NewValue == "true",  "All Users",
    // Specific Users or Groups: scoped to identities
    ForAllUsers == "false" and array_length(AddedIdentities) > 0,   "Specific Users or Groups",
    ForAllUsers == "false" and array_length(RemovedIdentities) > 0, "Specific Users or Groups",
    "Unknown"
)
| project TimeGenerated, UserAccessSetting, Configuration,ConfigurationState, ForAllUsers, AddedIdentities, RemovedIdentities, OriginalValue, NewValue, AccountDisplayName
| sort by TimeGenerated
```

Query with enriched added and removed user entities

```kql
// sharing
CloudAppEvents
| where Application == "Microsoft 365"
| where ActionType == "UpdateTenantSettings"
| extend UserAccessSetting = tostring(parse_json(tostring(RawEventData.Resource)).Property)
| where UserAccessSetting == "AllowOrgWideSharing"
| extend ForAllUsers = tostring(RawEventData.ForAllUsers)
| extend NewValue = tostring(parse_json(tostring(parse_json(tostring(RawEventData.Resource)).NewValue)))
| extend OriginalValue = tostring(parse_json(tostring(parse_json(tostring(RawEventData.Resource)).OriginalValue)))
| extend RemovedIdentities = parse_json(RawEventData.RemovedIdentities)
| extend AddedIdentities = parse_json(RawEventData.AddedIdentities)
| extend Configuration = "Users who can share agents with anyone in the organization"
| extend ConfigurationState = case(
    ForAllUsers == "true"  and NewValue == "false", "No Users",
    ForAllUsers == "true"  and array_length(AddedIdentities) == 0 and NewValue == "true", "All Users",
    ForAllUsers == "false" and array_length(AddedIdentities) > 0,   "Specific Users or Groups",
    ForAllUsers == "false" and array_length(RemovedIdentities) > 0, "Specific Users or Groups",
    "Unknown"
)
// Pad empty arrays with a placeholder so mv-expand never drops the row
| extend AddedIdentities   = iff(array_length(AddedIdentities)   == 0, dynamic([null]), AddedIdentities)
| extend RemovedIdentities = iff(array_length(RemovedIdentities) == 0, dynamic([null]), RemovedIdentities)
// Enrich AddedIdentities
| mv-expand AddedIdentity = AddedIdentities
| extend AddedObjectId = tostring(AddedIdentity)
| join kind=leftouter (
    IdentityInfo
    | where TimeGenerated > ago(21d)
    | summarize arg_max(TimeGenerated, *) by AccountObjectId
    | project AccountObjectId, AddedDisplayName = AccountDisplayName
) on $left.AddedObjectId == $right.AccountObjectId
// Only pack non-placeholder rows
| extend AddedIdentitiesDetails = iff(isnotempty(AddedObjectId), pack("ObjectId", AddedObjectId, "DisplayName", AddedDisplayName), dynamic(null))
| summarize
    AddedIdentitiesDetails = make_list(AddedIdentitiesDetails),
    arg_max(TimeGenerated, *)
    by TimeGenerated, UserAccessSetting, Configuration, ConfigurationState, ForAllUsers,
       RemovedIdentities = tostring(RemovedIdentities),
       OriginalValue, NewValue, AccountDisplayName
// Enrich RemovedIdentities
| mv-expand RemovedIdentity = parse_json(RemovedIdentities)
| extend RemovedObjectId = tostring(RemovedIdentity)
| join kind=leftouter (
    IdentityInfo
    | where TimeGenerated > ago(21d)
    | summarize arg_max(TimeGenerated, *) by AccountObjectId
    | project AccountObjectId, RemovedDisplayName = AccountDisplayName
) on $left.RemovedObjectId == $right.AccountObjectId
// Only pack non-placeholder rows
| extend RemovedIdentitiesDetails = iff(isnotempty(RemovedObjectId), pack("ObjectId", RemovedObjectId, "DisplayName", RemovedDisplayName), dynamic(null))
| summarize
    RemovedIdentitiesDetails = make_list(RemovedIdentitiesDetails),
    AddedIdentitiesDetails   = any(AddedIdentitiesDetails)
    by TimeGenerated, UserAccessSetting, Configuration, ConfigurationState, ForAllUsers, OriginalValue, NewValue, AccountDisplayName
| project TimeGenerated, AccountDisplayName, UserAccessSetting, Configuration, ConfigurationState,
    AddedIdentitiesDetails, RemovedIdentitiesDetails
| sort by TimeGenerated
```

Explanation

This KQL (Kusto Query Language) script is designed to track changes in sharing settings for agents within a Microsoft 365 environment. Here's a simplified breakdown of what the query does:

  1. Data Source: It pulls data from CloudAppEvents where the application is "Microsoft 365" and the action type is "UpdateTenantSettings". This indicates changes in the tenant settings related to sharing.

  2. Focus on Sharing Settings: The query specifically looks for changes in the "AllowOrgWideSharing" setting, which controls who can share agents across the organization.

  3. Determine Sharing Configuration:

    • It identifies whether sharing is allowed for all users, no users, or specific users/groups based on the values in the event data.
    • It uses conditions to categorize the sharing state into "No Users", "All Users", or "Specific Users or Groups".
  4. Track Changes:

    • It captures the original and new values of the sharing setting.
    • It identifies which users or groups were added or removed from the sharing permissions.
  5. Enrich User Data:

    • The query enriches the data by joining with IdentityInfo to get display names for added or removed identities, making it easier to understand who was affected by the changes.
  6. Output:

    • The results include the time of the change, the account that made the change, the configuration state, and details of any added or removed identities.
    • The data is sorted by the time the change occurred, providing a chronological view of sharing setting changes.

In essence, this query helps administrators monitor and audit changes to agent sharing settings within their organization, ensuring they can track who can share agents and how these permissions evolve over time.

Details

Alex Verboon profile picture

Alex Verboon

Released: April 20, 2026

Tables

CloudAppEventsIdentityInfo

Keywords

CloudAppEventsMicrosoft365UpdateTenantSettingsUserAccessSettingAllowOrgWideSharingForAllUsersNewValueOriginalValueRemovedIdentitiesAddedIdentitiesConfigurationConfigurationStateAccountDisplayNameIdentityInfoAccountObjectIdAddedDisplayNameRemovedDisplayName

Operators

//|where==extendtostring()parse_json()array_length()case()projectsort byiff()dynamic()mv-expandjoin kind=leftouter>ago()summarizearg_max()bypack()make_list()any()isnotempty()==!=

Actions