Detección de agentes de inteligencia artificial locales con Microsoft Defender para punto de conexión (versión preliminar)

Importante

Parte de la información contenida en este artículo se refiere a un producto preliminar que puede sufrir modificaciones sustanciales antes de su lanzamiento comercial. Microsoft no otorga garantías, expresas o implícitas, con respecto a la información que aquí se proporciona.

Microsoft Defender detecta automáticamente los agentes de IA locales compatibles y los servidores MCP configurados en los dispositivos incorporados, y los muestra en el inventario de agentes de IA y en la búsqueda avanzada. Para más información sobre la detección de agentes de inteligencia artificial local, consulte Detección de agentes de inteligencia artificial local con Microsoft Defender para punto de conexión.

En este artículo, aprendes a ver los agentes descubiertos en el inventario, revisar su configuración y riesgo, explorar sus relaciones con dispositivos e identidades, e investigar la presencia de agentes utilizando caza avanzada.

Requisitos previos

Para poder detectar agentes de inteligencia artificial locales en puntos de conexión, asegúrese de cumplir los siguientes requisitos:

  • El entorno está en la nube comercial. No se admiten nubes soberanas ni nacionales.
  • Su organización tiene una licencia de Microsoft Defender para punto de conexión Plan 2. Para obtener más información, consulte Licencias.
  • Los dispositivos se incorporan a Microsoft Defender para punto de conexión.
  • Los dispositivos ejecutan una versión compatible de Windows o macOS, y Microsoft Defender Antivirus se actualiza con las actualizaciones actuales de la plataforma mensual y del motor.
  • El antivirus Microsoft Defender está funcionando en modo activo en tus dispositivos, con la protección en tiempo real activada.

No necesita scripts, configuración o implementación adicionales más allá de los requisitos de incorporación de dispositivos. Si el dispositivo cumple todos los requisitos previos, la detección de agentes comienza automáticamente.

Licencias

Microsoft Defender para punto de conexión Plan 2 es la licencia mínima para el descubrimiento de agentes de IA locales. Las capacidades de postura de seguridad para los agentes que Defender descubre requieren otra licencia, como se describe en la siguiente tabla.

Capacidad Licencia necesaria
Descubre agentes de IA locales y míralos en el inventario de agentes de IA, incluyendo los datos del agente, el dispositivo y la cuenta, y los servidores MCP configurados Microsoft Defender para punto de conexión Plan 2
Consultar agentes de IA locales en búsqueda avanzada, incluida la tabla AgentsInfo Microsoft Defender para punto de conexión Plan 2
Consulta el nivel de riesgo, los indicadores de riesgo y las recomendaciones de seguridad para los agentes de IA locales descubiertos Microsoft 365 E7, o Microsoft Agent 365 junto con Microsoft Defender para punto de conexión Plan 2

Microsoft 365 E5 y Microsoft 365 E7 incluyen ambos Microsoft Defender para punto de conexión Plan 2. Si tu organización tiene Microsoft Defender para punto de conexión Plan 2 sin Microsoft 365 E7 o Microsoft Agent 365, puedes descubrir agentes de IA locales, ver el inventario y consultar agentes en búsqueda avanzada, pero el nivel de riesgo, los indicadores de riesgo ni las recomendaciones de seguridad no están disponibles.

Visualización de agentes de inteligencia artificial locales en el inventario

  1. Inicie sesión en el portal de Microsoft Defender.

  2. En el panel de navegación de la izquierda, seleccione Recursos>agentes de IA.

  3. Selecciona la pestaña de agentes locales para ver los agentes de IA locales descubiertos en tus dispositivos.

    Captura de pantalla que muestra el inventario de agentes de inteligencia artificial local en el portal de Microsoft Defender con los agentes detectados enumerados.

Las tarjetas de información de Agentes resumen el número total de agentes monitorizados, el número de agentes con alto riesgo y el número de agentes críticos.

La lista muestra una entrada por cada instalación de agente, por lo que un agente que se ejecuta en varios dispositivos, o bajo varias cuentas en el mismo dispositivo, aparece más de una vez. Usa los filtros para reducir la lista, selecciona Personalizar columnas para cambiar qué columnas aparecen o selecciona Exportar para descargar la lista.

Columna Description
Nombre del agente, versión El agente descubierto y la versión instalada.
Nombre del dispositivo, ID del dispositivo, plataforma del sistema operativo, tipo de dispositivo El dispositivo donde se descubrió al agente.
Nombre de cuenta, dominio de cuenta La cuenta bajo la que opera el agente.
Nivel de riesgo El nivel de riesgo global del agente.
Indicadores de riesgo Por qué el agente se considera arriesgado, como ejecutarse en un dispositivo crítico o ser usado por un usuario crítico.
Recomendaciones Las recomendaciones de seguridad que se aplican al agente.
Servidores MCP, MCPs locales El número de servidores MCP remotos y de servidores MCP locales configurados para el agente.
Primero visto Cuando el agente fue descubierto por primera vez.

Nota:

Los niveles de riesgo, indicadores de riesgo y recomendaciones de seguridad requieren Microsoft 365 E7 o Microsoft Agent 365. Sin una de estas licencias, aún puedes ver el inventario y los detalles de cada agente. Para obtener más información, consulte Licencias.

Consulta los detalles de un agente local de IA

Desde la lista de agentes locales , selecciona un agente para abrir su panel de detalles:

  • Detalles muestra el nombre del agente, el proveedor, el modelo, el proceso relacionado, si el proceso anfitrión es de confianza, si el agente aprueba automáticamente sus propias acciones y el ID del agente de origen.
  • El riesgo muestra el nivel de riesgo, los indicadores de riesgo y las recomendaciones de seguridad que se aplican al agente.
  • Los detalles del dispositivo muestran el dispositivo donde se descubrió el agente, incluyendo la plataforma y versión del sistema operativo, el tipo y funciones del dispositivo, y el ID del dispositivo Microsoft Entra.
  • Los datos de usuario muestran el ID, nombre y dominio de la cuenta bajo la que opera el agente.
  • Los servidores MCP listan los servidores MCP configurados para el agente, con el nombre, tipo y punto final de cada servidor.
  • Los servidores MCP locales listan las entradas MCP locales descubiertas en el dispositivo.

Para investigar más a fondo, selecciona Ir a buscar para consultar al agente en caza avanzada, Ver en el mapa para ver al agente en el mapa de la superficie de ataque, o Abrir la página de Agente para abrir la página completa del agente.

Revisa la superficie de ataque y las recomendaciones de un agente

La página del agente tiene una pestaña de Resumen y una pestaña de recomendaciones de seguridad . El mapa de la superficie de ataque en la pestaña Resumen muestra los otros agentes, dispositivos, identidades y recursos asociados al agente.

Para obtener más información sobre el uso del inventario de agentes de inteligencia artificial, consulte Detección de agentes de inteligencia artificial y evaluación de la posición de seguridad mediante Microsoft Defender.

Consultar agentes locales de IA con búsqueda avanzada

Utiliza la caza avanzada para investigar proactivamente la presencia local de agentes de IA, entender cómo están configurados los agentes e identificar a los agentes y usuarios que conllevan el mayor riesgo. Estas consultas te ayudan a inventariar agentes, revisar los servidores MCP a los que se conectan y rastrear el acceso a activos críticos o sensibles.

Entiende las tablas

Tres tablas avanzadas de búsqueda describen los agentes de IA locales. Cada uno responde a una pregunta diferente, y son más útiles juntos.

Tabla Qué contiene Úsalo para responder
AgentsInfo Un registro de perfil para cada agente de IA que Microsoft Defender descubra, en todas las plataformas de agentes. Para los agentes de IA locales, el registro incluye el proveedor, la versión, el proceso host, la configuración de confianza y de aprobación automática, los servidores MCP configurados y el dispositivo y la cuenta donde se detectó el agente. ¿Qué es este agente y cómo está configurado?
ExposureGraphNodes Cada entidad en tu organización como nodo, incluyendo agentes de IA, dispositivos, identidades y recursos en la nube, junto con propiedades como la criticidad de activos y si la entidad posee datos sensibles. ¿Qué es esta entidad y cuánto importa?
ExposureGraphEdges Las relaciones entre nodos, como el dispositivo en el que se ejecuta un agente, o los recursos a los que puede acceder una identidad. ¿A qué puede llegar este agente?

En resumen, AgentsInfo describe qué es un agente, y el gráfico de exposición describe hasta dónde puede llegar un agente.

Ambas fuentes de datos contienen agentes de todas las plataformas, incluidos los agentes en la nube, por lo que cada una necesita su propio filtro.

En AgentsInfo, filtra por la columna Platform:

AgentsInfo
| where Platform == "LocalAgents"

En ExposureGraphNodes, los agentes de IA utilizan la ai-agent etiqueta del nodo. Filtrar por la plataforma indicada en las propiedades del nodo:

ExposureGraphNodes
| where NodeLabel == "ai-agent"
| where tostring(NodeProperties.rawData.aiAgentMetadata.platform) == "LocalAgents"

Importante

La ExposureGraphEdges tabla no incluye una propiedad que identifique a los agentes de IA locales. Filtrar solo por SourceNodeLabel == "ai-agent" devuelve bordes para todos los agentes de IA de tu inquilino, incluidos los agentes en la nube. Obtén siempre primero el conjunto de agentes locales a partir de ExposureGraphNodes y, a continuación, haz una unión con ExposureGraphEdges mediante el ID del nodo, como se muestra en las siguientes consultas.

Combina las tablas

Un agente tiene dos identificadores, y las consultas que combinan las tablas necesitan ambos:

  • AgentId identifica el perfil del agente en AgentsInfo. El grafo de exposición almacena el mismo valor en el nodo agente, que NodeProperties.rawData.aiAgentMetadata.id.
  • NodeId identifica el nodo del agente en el gráfico de exposición. Este es el valor que ExposureGraphEdges se refiere a, en SourceNodeId y TargetNodeId.
Partir de En Partido en marcha
AgentsInfo ExposureGraphNodes tostring(AgentId) y tostring(NodeProperties.rawData.aiAgentMetadata.id)
ExposureGraphNodes ExposureGraphEdges NodeId y SourceNodeId o TargetNodeId

Nota:

AgentId es una guid columna en AgentsInfo, pero el gráfico de exposición almacena el mismo valor que una cadena. Conviértelo con tostring() en ambos lados de la unión. Una unión entre columnas de diferentes tipos no coincide con ninguna fila.

Los nodos agentes de IA locales se conectan al resto del grafo de exposición a través de los siguientes bordes:

Etiqueta de borde Etiqueta de nodo de destino Description
runs on device, ec2.instance, microsoft.compute/virtualmachines El dispositivo donde se descubrió al agente.
uses mcp/server Un servidor MCP configurado para el agente.
used by user La identidad que usa el agente.

Los agentes de IA en la nube también utilizan un can authenticate as borde que apunta a una entidad de servicio o a una aplicación OAuth de Microsoft Entra. Los agentes de IA locales no usan ese edge, así que usa used by para resolver la identidad de un agente de IA local.

La criticidad de activos se almacena en ExposureGraphNodes, en NodeProperties.rawData.criticalityLevel.criticalityLevel, donde 0 es el nivel más alto (muy alto) y 3 es el más bajo (bajo). La ruleNames propiedad enumera las reglas de clasificación que hicieron que el activo fuera fundamental.

Trabajar con perfiles locales de agentes de IA

AgentsInfo Añade un registro cada vez que se actualiza el perfil de un agente, por lo que un solo agente suele tener varios registros. Para devolver solo el registro más reciente de cada agente, haz un resumen con arg_max en Timestamp:

AgentsInfo
| where Platform == "LocalAgents"
| summarize arg_max(Timestamp, Name, Version, LifecycleStatus, RawAgentInfo) by AgentId

LifecycleStatus Informa si el agente sigue presente en el dispositivo. Un agente que se ha reinstalado también puede volver a emitirse con un nuevo AgentId, lo que deja marcado el registro anterior como Deleted, así que filtra los registros Deleted y Uninstalled si quieres un inventario actualizado.

Muchas AgentsInfo columnas describen agentes en la nube y están vacías para agentes de IA locales. Las columnas que contienen datos del agente de IA local son AgentId, Name, Version, PublishedStatus, LifecycleStatus, LastUpdatedDateTime, McpServers, DeclaredTools y RawAgentInfo.

La postura local del agente de IA está anidada en la RawAgentInfo columna, bajo localAgentMetadata:

Propiedad Description
vendor El publicador del agente, como Anthropic, Google u OpenAI.
relatedProcess El proceso que aloja al agente, como code.exe.
trustedProcess Si el proceso anfitrión es de confianza. Reportado como la cadena "true" o "false".
autoApprove Si el agente actúa sin pedir la aprobación al usuario. Reportado como la cadena "true" o "false".
deviceName, aadDeviceId El dispositivo donde se descubrió al agente.
accountName, accountDomain, accountSid La cuenta bajo la que gestionaba el agente.
localMcps Servidores MCP que se ejecutan localmente en el dispositivo, incluyendo el comando que inicia cada uno.

Nota:

trustedProcess y autoApprove se reportan como cadenas, no como valores booleanos. Compáralos con "true" o "false" en lugar de usar tobool().

Cada una de las siguientes consultas es autónoma. Las consultas que utilizan el gráfico de exposición comienzan con un conjunto de let sentencias que resuelven agentes de IA locales y los dispositivos en los que se ejecutan.

Haz un inventario de agentes locales de IA

Esta consulta enumera los agentes de IA locales descubiertos en tu organización, junto con el editor, el proceso anfitrión, las versiones en uso y la extensión de cada agente:

AgentsInfo
| where Platform == "LocalAgents"
| summarize arg_max(Timestamp, Name, Version, LifecycleStatus, RawAgentInfo)
    by AgentId
| where LifecycleStatus !in~ ("Deleted", "Uninstalled")
| extend AgentMetadata = RawAgentInfo.localAgentMetadata
| extend Vendor = tostring(AgentMetadata.vendor),
         Process = tostring(AgentMetadata.relatedProcess),
         Device = tostring(AgentMetadata.deviceName),
         Account = tostring(AgentMetadata.accountName)
| summarize Installations = count(),
            DeviceCount = dcount(Device),
            Devices = make_set(Device, 100),
            Versions = make_set(Version, 20),
            Accounts = make_set_if(Account, isnotempty(Account), 50)
    by Agent = Name, Vendor, Process
| sort by DeviceCount desc, Installations desc

Cada uno AgentId representa un perfil de agente, que es un solo agente en un solo dispositivo para una sola cuenta. Installations cuenta esos perfiles, así que un agente utilizado por dos personas en el mismo dispositivo cuenta dos veces, mientras que DeviceCount solo cuenta el dispositivo una vez.

Revisa los servidores MCP y las herramientas que utilizan los agentes de IA locales

Esta consulta enumera los servidores y herramientas MCP configurados para agentes de IA locales, los agentes y dispositivos que los utilizan, y dónde se ejecuta cada uno:

let localAgentProfiles =
    AgentsInfo
    | where Platform == "LocalAgents"
    | extend AgentMetadata = RawAgentInfo.localAgentMetadata
    | project Timestamp,
              Agent = Name,
              Device = tostring(AgentMetadata.deviceName),
              Account = tostring(AgentMetadata.accountName),
              McpServers,
              DeclaredTools,
              LocalServers = AgentMetadata.localMcps;
let remoteMcpServers =
    localAgentProfiles
    | mv-expand Server = McpServers
    | project Timestamp, Agent, Device, Account,
              McpServer = tostring(Server.name),
              Origin = "Remote MCP server",
              Transport = tostring(Server.type),
              Location = tostring(Server.endpoint);
let agentDeclaredTools =
    localAgentProfiles
    | mv-expand Tool = DeclaredTools
    | project Timestamp, Agent, Device, Account,
              McpServer = tostring(Tool.name),
              Origin = "Declared tool",
              Transport = tostring(Tool.type),
              Location = tostring(Tool.endpoint);
let localMcpServers =
    localAgentProfiles
    | mv-expand Server = LocalServers
    | project Timestamp, Agent, Device, Account,
              McpServer = tostring(Server.name),
              Origin = "Local MCP server",
              Transport = tostring(Server.transportType),
              Location = tostring(Server.commandName);
remoteMcpServers
| union agentDeclaredTools, localMcpServers
| where isnotempty(McpServer)
| summarize LastSeen = max(Timestamp),
            Agents = make_set(Agent, 20),
            Devices = make_set(Device, 20),
            Accounts = make_set_if(Account, isnotempty(Account), 20)
    by McpServer, Origin, Transport, Location
| sort by McpServer asc, Origin asc

La Origin columna distingue las tres formas en que se informa de un servidor o herramienta:

  • Un servidor MCP remoto se accede a través de la red y Location contiene su punto final.
  • Un servidor MCP local se ejecuta como un proceso en el dispositivo y Location mantiene el comando que lo inicia. Los servidores MCP locales solo se reportan en AgentsInfo.
  • Una herramienta declarada es una herramienta que el agente anuncia. Las herramientas declaradas suelen estar respaldadas por un servidor MCP, pero no siempre reportan un endpoint.

A diferencia de las otras consultas, esta lee todos los registros de perfil de cada agente, de modo que los servidores MCP reportados en cualquier momento están incluidos. La LastSeen columna muestra cuándo se reportó por última vez cada servidor.

Encuentra agentes de IA locales con configuraciones arriesgadas

Esta consulta devuelve agentes de IA locales que actúan sin pedir aprobación al usuario, o que se ejecutan en un proceso host que no es de confianza:

AgentsInfo
| where Platform == "LocalAgents"
| summarize arg_max(Timestamp, Name, Version, LifecycleStatus, RawAgentInfo)
    by AgentId
| where LifecycleStatus !in~ ("Deleted", "Uninstalled")
| extend AgentMetadata = RawAgentInfo.localAgentMetadata
| extend AutoApprove = tostring(AgentMetadata.autoApprove),
         TrustedProcess = tostring(AgentMetadata.trustedProcess),
         Vendor = tostring(AgentMetadata.vendor),
         Process = tostring(AgentMetadata.relatedProcess),
         Device = tostring(AgentMetadata.deviceName),
         Account = tostring(AgentMetadata.accountName)
| where AutoApprove =~ "true" or TrustedProcess =~ "false"
| extend RiskReason = case(
    AutoApprove =~ "true" and TrustedProcess =~ "false",
        "Acts without approval, and the host process isn't trusted",
    AutoApprove =~ "true",
        "Acts without approval",
    "The host process isn't trusted")
| project Agent = Name, Vendor, Version, Process, Device, Account,
          AutoApprove, TrustedProcess, RiskReason, LastSeen = Timestamp
| sort by Device asc, Agent asc

Un agente que aprueba automáticamente sus propias acciones ejecuta herramientas y llega a recursos sin que una persona confirme cada paso, por lo que la cuenta y el dispositivo en el que se ejecuta el agente definen lo que puede hacer sin supervisión.

Encuentra agentes de IA locales arriesgados en dispositivos críticos

Esta consulta combina la configuración de agentes desde AgentsInfo con la criticidad de activos del gráfico de exposición, por lo que puedes empezar con los agentes que actúan sin supervisión y se ejecutan en dispositivos críticos para el negocio:

let deviceLabels = dynamic(["device", "ec2.instance",
                            "microsoft.compute/virtualmachines"]);
let riskyAgentProfiles =
    AgentsInfo
    | where Platform == "LocalAgents"
    | summarize arg_max(Timestamp, Name, Version, LifecycleStatus, RawAgentInfo)
        by AgentId
    | where LifecycleStatus !in~ ("Deleted", "Uninstalled")
    | extend AgentMetadata = RawAgentInfo.localAgentMetadata
    | extend AutoApprove = tostring(AgentMetadata.autoApprove),
             TrustedProcess = tostring(AgentMetadata.trustedProcess)
    | where AutoApprove =~ "true" or TrustedProcess =~ "false"
    | project AgentId = tostring(AgentId),
              Agent = Name,
              Version,
              Vendor = tostring(AgentMetadata.vendor),
              Account = tostring(AgentMetadata.accountName),
              AutoApprove,
              TrustedProcess;
let localAgentNodes =
    ExposureGraphNodes
    | where NodeLabel == "ai-agent"
    | where tostring(NodeProperties.rawData.aiAgentMetadata.platform) == "LocalAgents"
    | project AgentNodeId = NodeId,
              AgentId = tostring(NodeProperties.rawData.aiAgentMetadata.id);
let agentDeviceEdges =
    ExposureGraphEdges
    | where SourceNodeLabel == "ai-agent"
    | where EdgeLabel =~ "runs on"
    | where TargetNodeLabel in (deviceLabels)
    | project AgentNodeId = SourceNodeId, DeviceId = TargetNodeId,
              Device = TargetNodeName, DeviceType = TargetNodeLabel;
let criticalDevices =
    ExposureGraphNodes
    | where NodeLabel in (deviceLabels)
    | where NodeProperties has "criticalityLevel"
    | extend CriticalityLevel =
        toint(NodeProperties.rawData.criticalityLevel.criticalityLevel)
    | where CriticalityLevel between (0 .. 3)
    | extend Criticality = case(
        CriticalityLevel == 0, "Very high",
        CriticalityLevel == 1, "High",
        CriticalityLevel == 2, "Medium",
        "Low")
    | project DeviceId = NodeId, CriticalityLevel, Criticality,
              CriticalityReason =
                  tostring(NodeProperties.rawData.criticalityLevel.ruleNames);
riskyAgentProfiles
| join kind=inner localAgentNodes on AgentId
| join kind=inner agentDeviceEdges on AgentNodeId
| join kind=inner criticalDevices on DeviceId
| project Device, DeviceType, Criticality, CriticalityReason,
          Agent, Vendor, Version, Account, AutoApprove, TrustedProcess,
          CriticalityLevel
| sort by CriticalityLevel asc, Device asc, Agent asc
| project-away CriticalityLevel

Para revisar cada agente de IA local en un dispositivo crítico en lugar de solo los más arriesgados, elimina la where AutoApprove =~ "true" or TrustedProcess =~ "false" línea.

Clasifica los usuarios cuyos agentes de IA locales acceden a activos críticos o sensibles

Esta consulta clasifica las identidades que usan agentes de IA locales según la cantidad de recursos críticos o sensibles a los que pueden acceder, para que puedas priorizar las identidades con el mayor alcance potencial. La UserCriticality columna muestra si la identidad se clasifica como un activo crítico, como un Administrador Global:

let deviceLabels = dynamic(["device", "ec2.instance",
                            "microsoft.compute/virtualmachines"]);
let localAgents =
    ExposureGraphNodes
    | where NodeLabel == "ai-agent"
    | where tostring(NodeProperties.rawData.aiAgentMetadata.platform) == "LocalAgents"
    | project AgentNodeId = NodeId, AIAgent = NodeName;
let agentDeviceEdges =
    ExposureGraphEdges
    | where SourceNodeLabel == "ai-agent"
    | where EdgeLabel =~ "runs on"
    | where TargetNodeLabel in (deviceLabels)
    | project AgentNodeId = SourceNodeId, DeviceId = TargetNodeId,
              Device = TargetNodeName;
let agentUserEdges =
    ExposureGraphEdges
    | where SourceNodeLabel == "ai-agent"
    | where EdgeLabel =~ "used by"
    | project AgentNodeId = SourceNodeId, UserId = TargetNodeId,
              User = TargetNodeName;
let userReach =
    ExposureGraphEdges
    | where EdgeLabel in~ ("has permissions to", "has role on")
    | project UserId = SourceNodeId, AssetId = TargetNodeId
    | union (
        ExposureGraphNodes
        | where NodeLabel == "user"
        | project UserId = NodeId, AssetId = NodeId
    );
let sensitiveAssets =
    ExposureGraphNodes
    | where NodeProperties has "criticalityLevel"
         or NodeProperties has "containsSensitiveData"
    | extend
        CriticalityLevel =
            toint(NodeProperties.rawData.criticalityLevel.criticalityLevel),
        SensitiveDataRaw = tostring(NodeProperties.rawData.containsSensitiveData)
    | extend HasSensitiveData =
        iff(isnotempty(SensitiveDataRaw) and SensitiveDataRaw !~ "false", "Yes", "No")
    | where CriticalityLevel between (0 .. 3) or HasSensitiveData == "Yes"
    | extend CriticalityRank =
        iff(CriticalityLevel between (0 .. 3), CriticalityLevel, 4)
    | project AssetId = NodeId, AssetName = NodeName,
              CriticalityRank, HasSensitiveData;
let agentUsers =
    localAgents
    | join kind=inner agentDeviceEdges on AgentNodeId
    | join kind=inner agentUserEdges on AgentNodeId
    | project AIAgent, Device, UserId, User;
agentUsers
| join kind=inner userReach on UserId
| join kind=inner sensitiveAssets on AssetId
| summarize AIAgents = make_set(AIAgent, 20),
            Devices = make_set(Device, 20),
            ReachableAssets = dcountif(AssetId, AssetId != UserId),
            SensitiveAssets = dcountif(AssetId,
                AssetId != UserId and HasSensitiveData == "Yes"),
            Assets = make_set_if(AssetName, AssetId != UserId, 50),
            UserRank = minif(CriticalityRank, AssetId == UserId),
            AssetRank = minif(CriticalityRank, AssetId != UserId)
    by User
| extend UserCriticality = case(
             UserRank == 0, "Very high",
             UserRank == 1, "High",
             UserRank == 2, "Medium",
             UserRank == 3, "Low",
             "Not classified"),
         HighestAssetCriticality = case(
             AssetRank == 0, "Very high",
             AssetRank == 1, "High",
             AssetRank == 2, "Medium",
             AssetRank == 3, "Low",
             AssetRank == 4, "Sensitive data",
             "None")
| extend SortRank = coalesce(AssetRank, 99)
| project User, UserCriticality, ReachableAssets, SensitiveAssets,
          HighestAssetCriticality, AIAgents, Devices, Assets, SortRank
| sort by SortRank asc, ReachableAssets desc
| project-away SortRank