Crear conectores de datos sin necesidad de código mediante sondeo anidado de API

Algunas API REST requieren llamadas secuenciales, donde la respuesta de un punto de conexión proporciona una entrada que se debe pasar a otro punto de conexión. El Microsoft Sentinel Codeless Connector Framework (CCF) admite este patrón mediante sondeos de API anidados para conectores RestApiPoller.

Importante

La consulta de API anidada se encuentra actualmente en versión preliminar pública. Los Términos Suplementarios de Azure Preview incluyen más términos legales que se aplican a funciones de Azure en beta, vista previa o de otro modo que aún no se han publicado en disponibilidad general.

Use el sondeo de API anidado cuando una llamada API primaria devuelve identificadores, cursores u otros valores que requieren una o varias llamadas API secundarias. CCF extrae los valores necesarios de la respuesta primaria, los sustituye en la solicitud secundaria y envía las respuestas secundarias a la tabla de destino configurada.

Configure la consulta de API anidada en un conector de extracción CCF definiendo la lógica de anidación en las RestApiPoller reglas de conexión.

Para ver el proceso de un extremo a otro para crear y empaquetar un conector CCF, consulte Creación de un conector sin código para Microsoft Sentinel. También puedes usar la extensión Microsoft Sentinel para Visual Studio Code para implementar y probar flujos de trabajo de sondeo anidados de API. Para información sobre configuración y uso, consulta Construir conectores personalizados con IA en Microsoft Sentinel. Para obtener las propiedades estándar RestApiPoller de solicitud, respuesta, autenticación, paginación y DCR, consulte Referencia de reglas de conexión del conector de datos restApiPoller.

Note

Si es un proveedor de software independiente (ISV) que crea una integración de Microsoft Sentinel mediante codeless Connector Framework, es posible que el equipo de Microsoft App Assure pueda ayudar. Para interactuar con el equipo de App Assure, envíe un correo electrónico a azuresentinelpartner@microsoft.com.

Prerequisites

Antes de configurar el sondeo anidado de API, asegúrese de entender lo siguiente:

  • Los puntos de conexión de API a los que debe llamar el conector.
  • Qué valores de respuesta de la llamada a la API principal requiere la llamada a la API secundaria.
  • Esquema de salida de la tabla de destino.
  • Creación de un conector CCF RestApiPoller estándar.

Un conector CCF completo incluye los siguientes componentes:

  • Tabla: el Log Analytics tabla personalizada donde se almacenan los datos ingeridos.
  • DCR: La regla de recopilación de datos que define la transformación de la ingesta.
  • Interfaz de usuario del conector: la definición del conector de datos que aparece en el centro de contenido de Microsoft Sentinel.
  • Reglas de conexión de datos: la configuración del conector que captura datos de la API de origen.

El sondeo anidado de API se configura en las reglas de la conexión de datos para un conector RestApiPoller.

¿Qué son los sondeos de API anidados?

El sondeo anidado de API es un patrón de sondeo de CCF que encadena llamadas a API REST. La primera llamada API, denominada paso primario, devuelve los valores que requieren las llamadas API posteriores, denominados pasos secundarios.

Por ejemplo, una API podría usar este patrón:

  1. GET /incidents devuelve una lista de identificadores de incidente.
  2. GET /incidents/{incidentId}/details devuelve el registro completo del incidente para cada ID.

Una sola llamada API no devuelve los datos completos. El conector debe llamar al punto de conexión de lista, extraer cada incidentId y, a continuación, llamar al punto de conexión de detalles una vez por cada ID.

Utilice el sondeo anidado de la API cuando:

  • Un extremo para listar devuelve identificadores de recursos, y un extremo de detalle requiere que cada identificador se incluya en la ruta URL o en la cadena de consulta.
  • Una respuesta de elemento primario devuelve un cursor, un token de sesión, un identificador de consulta o un identificador de referencia que necesita una solicitud de elemento secundario.
  • Una respuesta contiene una matriz de valores que se deben pasar individualmente a otro punto de conexión.
  • La respuesta primaria contiene campos que desea conservar y la respuesta secundaria agrega datos de enriquecimiento que se deben unir a la misma fila de salida.

Si una sola llamada a la API devuelve todos los datos que necesitas, no hace falta el sondeo anidado de la API. Use la propiedad estándar eventsJsonPaths para extraer registros de la respuesta.

Cómo funcionan las consultas periódicas anidadas de la API

El sondeo de API anidado se configura con estas secciones:

Section Location Purpose
request Paso del elemento primario Define la solicitud de API primaria. Las propiedades de la ventana de tiempo se configuran aquí.
response Paso del elemento primario Define cómo se extraen los registros de la respuesta primaria.
stepInfo Paso del elemento primario Permite el sondeo anidado y define los pasos del elemento secundario que se ejecutarán a continuación.
stepCollectorConfigs Paso del elemento primario Define cada paso del elemento secundario, incluido el control de solicitudes y respuestas del elemento secundario.
shouldJoinNestedData Paso del elemento secundario Define si la respuesta del elemento secundario reemplaza la salida del elemento primario o está unida al registro del elemento primario.

El flujo de sondeo anidado funciona de la siguiente manera:

  1. Se ejecuta la solicitud del elemento primario.
  2. La respuesta primaria se divide en registros mediante response.eventsJsonPaths.
  3. stepPlaceholdersParsingKql extrae valores de marcador de posición de cada registro primario.
  4. CCF sustituye los marcadores de posición en la configuración del paso del elemento secundario.
  5. CCF ejecuta las solicitudes del elemento secundario.
  6. La respuesta del elemento secundario se envía como fila de salida o se une al registro del elemento primario, en función del valor de shouldJoinNestedData.

Plantilla de configuración de sondeo anidado

En el ejemplo siguiente se muestra la estructura de un conector anidado RestApiPoller . Las propiedades CCF estándar se abrevian con ....

{
  "kind": "RestApiPoller",
  "properties": {
    "connectorDefinitionName": "...",
    "dcrConfig": { },
    "dataType": "...",
    "auth": { },
    "request": {
      "apiEndpoint": "https://api.example.com/incidents",
      "httpMethod": "GET",
      "queryWindowInMin": 60,
      "queryTimeFormat": "yyyy-MM-ddTHH:mm:ssZ",
      "startTimeAttributeName": "startTime",
      "endTimeAttributeName": "endTime"
    },
    "response": {
      "eventsJsonPaths": [ "$.incidents" ],
      "format": "json"
    },
    "stepInfo": {
      "stepType": "Nested",
      "nextSteps": [
        {
          "stepId": "fetchIncidentDetails",
          "stepPlaceholdersParsingKql": "source | project res = parse_json(data) | project incidentId = res.incidentId"
        }
      ]
    },
    "stepCollectorConfigs": {
      "fetchIncidentDetails": {
        "shouldJoinNestedData": false,
        "request": {
          "httpMethod": "GET",
          "apiEndpoint": "https://api.example.com/incidents/$incidentId$/details"
        },
        "response": {
          "eventsJsonPaths": [ "$" ],
          "format": "json"
        }
      }
    }
  }
}

Propiedades de sondeo anidadas

Propiedad Location Description
stepInfo.stepType Paso del elemento primario Debe establecerse en Nested para habilitar el sondeo anidado de la API.
stepInfo.nextSteps[].stepId Paso del elemento primario El nombre del paso del elemento secundario. Este valor debe coincidir con una clave en stepCollectorConfigs.
stepInfo.nextSteps[].stepPlaceholdersParsingKql Paso del elemento primario KQL que extrae valores de la respuesta primaria. La consulta se ejecuta sobre source, donde la columna data contiene cada registro principal como una cadena JSON sin formato. Cada columna proyectada se convierte en un marcador de posición.
stepCollectorConfigs Paso del elemento primario Mapa de definiciones del paso del elemento secundario, indexadas por los valores de stepId declarados en stepInfo.nextSteps.
shouldJoinNestedData Paso del elemento secundario Controla cómo se envía la respuesta del elemento secundario a la transmisión. Establézcalo en false cuando la respuesta secundaria contenga el registro de salida completo. Establézcalo en true cuando necesite campos de las respuestas principales y secundarias en la misma fila de salida.
joinedDataStepName Paso del elemento secundario Nombre de la dynamic columna que almacena la respuesta del elemento secundario combinada cuando shouldJoinNestedData es true. No se usa cuando shouldJoinNestedData es false.

Sustitución de marcadores de posición

Los marcadores de posición se extraen mediante stepPlaceholdersParsingKql y se referencian con la sintaxis $placeholderName$.

Por ejemplo, este KQL crea un marcador de posición denominado incidentId:

source
| project res = parse_json(data)
| project incidentId = res.incidentId

A continuación, el paso del elemento secundario puede hacer referencia al marcador de posición como $incidentId$:

"apiEndpoint": "https://api.example.com/incidents/$incidentId$/details"

La sustitución de marcadores de posición es compatible en toda la configuración del paso del elemento secundario, incluida la solicitud del elemento secundario apiEndpoint, headers, queryParameters y queryParametersTemplate.

Configure las propiedades de la solicitud del elemento secundario

El bloque request dentro de un paso del elemento secundario admite las propiedades de solicitud comunes que se usan para el sondeo de API.

Entre las propiedades comunes de solicitud de elemento secundario se incluyen:

Propiedad Description
apiEndpoint Punto de conexión de API secundario. Puede incluir marcadores de posición como $incidentId$.
httpMethod Método HTTP para la solicitud secundaria, como GET o POST.
headers Encabezados de solicitud para la llamada API del elemento secundario. Se admite la sustitución de marcadores de posición.
queryParameters Parámetros de cadena de consulta para la llamada a la API secundaria. Se admite la sustitución de marcadores de posición.
queryParametersTemplate Plantilla utilizada para escenarios de cuerpo de la solicitud o carga de consulta. Se admite la sustitución de marcadores de posición.
isPostPayloadJson Establézcalo en true cuando se debe enviar la carga POST como JSON.
rateLimitQPS Número máximo de solicitudes por segundo.
rateLimitConfig Configuración de límite de velocidad que puede usar encabezados de límite de velocidad devueltos por la API.
retryCount Número de reintentos. Valor predeterminado: 3. Intervalo admitido: 1 a 6.
timeoutInSeconds Tiempo de espera de la solicitud en segundos. Valor predeterminado: 20. Intervalo admitido: 1 a 180.

La solicitud del elemento primario controla la ventana de tiempo de sondeo. Configure las propiedades del intervalo de tiempo, como queryWindowInMin, queryTimeFormat, startTimeAttributeName y endTimeAttributeName, solo en la solicitud principal. Normalmente, los pasos del elemento secundario se controlan mediante valores de marcador de posición extraídos de la respuesta del elemento primario.

Configuración del paralelismo de solicitudes del elemento secundario

maxParallelism controla el número de llamadas secundarias que se pueden ejecutar simultáneamente. El valor por defecto es 15.

maxParallelism no forma parte de la configuración del conector primario estándar y no se puede establecer en el paso primario. Dado que los pasos del elemento secundario se pasan sin traducir los nombres de campo, puede establecer maxParallelism dentro del bloque request de un paso del elemento secundario si es necesario realizar algún ajuste.

"stepCollectorConfigs": {
  "fetchIncidentDetails": {
    "shouldJoinNestedData": false,
    "request": {
      "httpMethod": "GET",
      "apiEndpoint": "https://api.contoso.com/incidents/$incidentId$/details",
      "maxParallelism": 15
    },
    "response": {
      "eventsJsonPaths": [ "$" ],
      "format": "json"
    }
  }
}

Elija si desea unir datos padre e hijo

Utilice shouldJoinNestedData para controlar cómo se envían las respuestas del elemento secundario a la transmisión.

Utilice shouldJoinNestedData: false

Establezca shouldJoinNestedData en false cuando la respuesta principal solo proporcione los valores necesarios para la solicitud secundaria y la respuesta secundaria contenga el registro completo que desea incorporar.

Por ejemplo, use false cuando:

  • La llamada del elemento primario devuelve solo los identificadores de incidente.
  • La llamada del elemento secundario devuelve los registros completos de incidentes.
  • No es necesario conservar ninguno de los campos principales en la fila de destino.
"stepCollectorConfigs": {
  "fetchIncidentDetails": {
    "shouldJoinNestedData": false,
    "request": {
      "httpMethod": "GET",
      "apiEndpoint": "https://api.contoso.com/incidents/$incidentId$/details"
    },
    "response": {
      "eventsJsonPaths": [ "$" ],
      "format": "json"
    }
  }
}

Utilice shouldJoinNestedData: true

Configure shouldJoinNestedData como true cuando necesite campos tanto de la respuesta principal como de la respuesta secundaria en la misma fila de destino.

Por ejemplo, use true cuando:

  • La llamada primaria devuelve campos de alerta, como el identificador de alerta, la gravedad y el tiempo de detección.
  • La llamada secundaria devuelve campos de enriquecimiento como el usuario afectado, la dirección IP de origen o la geolocalización.
  • La transformación DCR debe asignar tanto los campos del elemento primario como los campos del elemento secundario en la tabla de destino.

Cuando shouldJoinNestedData sea true, asigne a joinedDataStepName el nombre de la columna dynamic que almacena la respuesta secundaria.

"stepCollectorConfigs": {
  "fetchAlertEnrichment": {
    "shouldJoinNestedData": true,
    "joinedDataStepName": "enrichment",
    "request": {
      "httpMethod": "GET",
      "apiEndpoint": "https://api.contoso.com/alerts/$alertId$/enrichment"
    },
    "response": {
      "eventsJsonPaths": [ "$" ],
      "format": "json"
    }
  }
}

Ejemplo: solicitud del elemento secundario GET

En este ejemplo se usa una API de incidentes de Contoso en dos pasos:

  1. La solicitud del elemento primario llama a GET /incidents y recibe una lista de ID de incidentes.
  2. stepPlaceholdersParsingKql extrae incidentId de cada registro primario.
  3. La solicitud del elemento secundario llama a GET /incidents/$incidentId$/details una vez por identificador de incidente.
  4. Las respuestas del elemento secundario se envían a la transmisión como registros planos.

Respuesta principal

{
  "incidents": [
    { "incidentId": "INC-001" },
    { "incidentId": "INC-002" },
    { "incidentId": "INC-003" }
  ]
}

Respuesta del elemento secundario

{
  "incidentId": "INC-001",
  "title": "Suspicious login attempt",
  "severity": "High",
  "status": "Active",
  "createdAt": "2026-05-30T14:22:00Z",
  "affectedUser": "alice@contoso.com",
  "sourceIp": "198.51.100.42"
}

Configuración de sondeo

{
  "kind": "RestApiPoller",
  "properties": {
    "connectorDefinitionName": "ContosoIncidentsConnector",
    "dcrConfig": {
      "dataCollectionEndpoint": "{{dataCollectionEndpoint}}",
      "dataCollectionRuleImmutableId": "{{dataCollectionRuleImmutableId}}",
      "streamName": "Custom-ContosoIncidents_CL"
    },
    "dataType": "ContosoIncidents_CL",
    "auth": {
      "type": "APIKey",
      "ApiKey": "{{apiKey}}",
      "ApiKeyName": "x-functions-key"
    },
    "request": {
      "apiEndpoint": "https://api.contoso.com/incidents",
      "httpMethod": "GET",
      "queryWindowInMin": 60,
      "queryTimeFormat": "yyyy-MM-ddTHH:mm:ssZ",
      "startTimeAttributeName": "startTime",
      "endTimeAttributeName": "endTime",
      "headers": {
        "Accept": "application/json"
      }
    },
    "response": {
      "eventsJsonPaths": [ "$.incidents" ],
      "format": "json"
    },
    "stepInfo": {
      "stepType": "Nested",
      "nextSteps": [
        {
          "stepId": "fetchIncidentDetails",
          "stepPlaceholdersParsingKql": "source | project res = parse_json(data) | project incidentId = res.incidentId"
        }
      ]
    },
    "stepCollectorConfigs": {
      "fetchIncidentDetails": {
        "shouldJoinNestedData": false,
        "request": {
          "httpMethod": "GET",
          "apiEndpoint": "https://api.contoso.com/incidents/$incidentId$/details",
          "headers": {
            "Accept": "application/json"
          },
          "retryCount": 3,
          "timeoutInSeconds": 60
        },
        "response": {
          "eventsJsonPaths": [ "$" ],
          "format": "json"
        }
      }
    }
  }
}

Ejemplo: solicitud del elemento secundario POST con un cuerpo JSON

Algunas API requieren que los identificadores de la respuesta principal se envíen en el cuerpo de una solicitud POST en lugar de en la ruta de la URL o en la cadena de consulta. Usa queryParametersTemplate con isPostPayloadJson para este patrón.

En este ejemplo, la respuesta primaria devuelve un incidentIdy la solicitud secundaria envía ese valor en un cuerpo POST JSON.

"stepCollectorConfigs": {
  "fetchIncidentDetails": {
    "shouldJoinNestedData": false,
    "request": {
      "httpMethod": "POST",
      "apiEndpoint": "https://api.contoso.com/incidents/details:batchGet",
      "headers": {
        "Accept": "application/json",
        "Content-Type": "application/json"
      },
      "queryParametersTemplate": "{'ids': ['$incidentId$']}",
      "isPostPayloadJson": true,
      "retryCount": 3,
      "timeoutInSeconds": 60
    },
    "response": {
      "eventsJsonPaths": [ "$.items" ],
      "format": "json"
    }
  }
}

Ejemplo: unión de datos de enriquecimiento del elemento secundario al registro del elemento primario

En este ejemplo se usa una API de alerta de Contoso donde la respuesta primaria contiene campos que se deben conservar y la respuesta secundaria contiene datos de enriquecimiento.

Respuesta principal

{
  "alerts": [
    {
      "alertId": "ALT-001",
      "severity": "High",
      "detectedAt": "2026-05-30T14:22:00Z",
      "riskScore": 92
    }
  ]
}

Respuesta del elemento secundario

{
  "alertId": "ALT-001",
  "affectedUser": "bob@contoso.com",
  "sourceIp": "198.51.100.77",
  "geolocation": "US/Virginia",
  "relatedIncidentId": "INC-042"
}

Configuración de sondeo

{
  "kind": "RestApiPoller",
  "properties": {
    "connectorDefinitionName": "ContosoAlertsConnector",
    "dcrConfig": {
      "dataCollectionEndpoint": "{{dataCollectionEndpoint}}",
      "dataCollectionRuleImmutableId": "{{dataCollectionRuleImmutableId}}",
      "streamName": "Custom-ContosoAlerts_CL"
    },
    "dataType": "ContosoAlerts_CL",
    "auth": {
      "type": "APIKey",
      "ApiKey": "{{apiKey}}",
      "ApiKeyName": "x-functions-key"
    },
    "request": {
      "apiEndpoint": "https://api.contoso.com/alerts",
      "httpMethod": "GET",
      "queryWindowInMin": 60,
      "queryTimeFormat": "yyyy-MM-ddTHH:mm:ssZ",
      "startTimeAttributeName": "startTime",
      "endTimeAttributeName": "endTime"
    },
    "response": {
      "eventsJsonPaths": [ "$.alerts" ],
      "format": "json"
    },
    "stepInfo": {
      "stepType": "Nested",
      "nextSteps": [
        {
          "stepId": "fetchAlertEnrichment",
          "stepPlaceholdersParsingKql": "source | project res = parse_json(data) | project alertId = res.alertId"
        }
      ]
    },
    "stepCollectorConfigs": {
      "fetchAlertEnrichment": {
        "shouldJoinNestedData": true,
        "joinedDataStepName": "enrichment",
        "request": {
          "httpMethod": "GET",
          "apiEndpoint": "https://api.contoso.com/alerts/$alertId$/enrichment"
        },
        "response": {
          "eventsJsonPaths": [ "$" ],
          "format": "json"
        }
      }
    }
  }
}

Después, la transformación DCR puede proyectar campos tanto del registro del elemento primario como de la respuesta del elemento secundario combinada.

Por ejemplo:

source
| extend enrichment = todynamic(enrichment)
| project
    TimeGenerated = todatetime(detectedAt),
    AlertId = tostring(alertId),
    Severity = tostring(severity),
    RiskScore = toint(riskScore),
    AffectedUser = tostring(enrichment.affectedUser),
    SourceIp = tostring(enrichment.sourceIp),
    Geolocation = tostring(enrichment.geolocation),
    RelatedIncidentId = tostring(enrichment.relatedIncidentId)

Autenticación de pasos del elemento secundario y nombres de los campos de paginación

La traducción del nombre del campo solo se aplica al paso principal. Cada entrada de stepCollectorConfigs se transmite literalmente y no se reasocia. Como resultado, cualquier bloque auth o paging dentro de un paso secundario debe usar los nombres de los campos del paso secundario que se muestran en las tablas siguientes.

Campos de autenticación

Nombre del campo del paso principal Nombre del campo del paso del elemento secundario
type AuthType
apiKey APIKey
apiKeyName APIKeyName
redirectUri para OAuth2 RedirectionEndpoint
isCredentialsInHeaders para OAuth2 o JWT IsClientSecretInHeader
grantType para OAuth2 FlowName
queryParameters para JWT TokenEndpointQueryParameters
isJsonRequest para JWT IsTokenEndpointPostPayloadJson
userName / password pares clave-valor para JWT o autenticación de sesión UsernameAttributeName y UsernameAttributeValue / PasswordAttributeName y PasswordAttributeValue

Para OAuth2, el FlowName valor también se transforma. Por ejemplo, use ClientCredentials en lugar de client_credentialsy AuthCode en lugar de authorization_code.

Campos de paginación

Nombre del campo del paso principal Nombre del campo del paso del elemento secundario
pageSizeParameterName pageSizeParaName

Todos los demás nombres de campos de paginación y de respuesta coinciden en los pasos del elemento primario y secundario.

Límites

El sondeo anidado de la API tiene los siguientes límites:

Limit Description
Pasos del elemento secundario en stepCollectorConfigs Una configuración anidada admite hasta cuatro entradas en stepCollectorConfigs.
Entradas en stepInfo.nextSteps stepInfo.nextSteps admite hasta tres entradas.
Referencias circulares No se admiten las referencias a pasos circulares y se rechazan durante la validación.

Completar el conector

Después de configurar las reglas de conexión anidadas RestApiPoller , complete los componentes restantes del conector CCF:

  • Cree o actualice la tabla de destino.
  • Cree el DCR y la transformación.
  • Cree la definición de la interfaz de usuario del conector.
  • Empaquetar el conector en una plantilla de implementación de ARM.
  • Implemente y pruebe el conector.

Para ver el proceso de un extremo a otro, consulte Creación de un conector sin código para Microsoft Sentinel.