Criar conectores de dados pull sem código usando polling API aninhado

Algumas APIs REST exigem chamadas sequenciais, onde a resposta de um endpoint fornece input que deve ser passado para outro endpoint. O Microsoft Sentinel Codeless Connector Framework (CCF) suporta este padrão por meio de sondagem aninhada da API para conectores RestApiPoller.

Importante

A consulta aninhada à API está atualmente em versão prévia pública. Os Termos Suplementares do Azure Preview incluem mais termos legais que se aplicam a funcionalidades do Azure em beta, pré-visualização ou de outra forma ainda não lançadas em disponibilidade geral.

Utilize a sondagem aninhada da API quando uma chamada à API principal retorna identificadores, cursores ou outros valores necessários para uma ou mais chamadas à API subordinadas. O CCF extrai os valores necessários da resposta do pai, substitui-os no pedido filho e envia as respostas do filho para a tabela de destinos configurada.

Configure a sondagem aninhada da API num conector pull do CCF, definindo a lógica de aninhamento nas regras de conexão RestApiPoller.

Para o processo de ponta a ponta de criação e empacotamento de um conector CCF, veja Criar um conector sem código para Microsoft Sentinel. Também pode usar a extensão Microsoft Sentinel para Visual Studio Code para implementar e testar fluxos de trabalho de sondagem de API aninhados. Para informações de configuração e uso, consulte Construir conectores personalizados com IA no Microsoft Sentinel. Para as propriedades padrão RestApiPoller de pedido, resposta, autenticação, paginação e DCR, consulte a referência das regras de ligação do conector de dados RestApiPoller.

Note

Se é um Fornecedor Independente de Software (ISV) a construir uma integração com o Microsoft Sentinel usando o Codeless Connector Framework, a equipa do Microsoft App Assure poderá ajudar. Para envolver a equipe do App Assure, envie um e-mail para azuresentinelpartner@microsoft.com.

Pré-requisitos

Antes de configurar o questionamento de API aninhado, certifique-se de que compreende:

  • Os endpoints da API que o conector tem de chamar.
  • Que valores de resposta da chamada à API principal são necessários para a chamada à API subordinada.
  • O esquema de saída para a tabela de destino.
  • Como criar um conector CCF RestApiPoller padrão.

Um conector CCF completo inclui os seguintes componentes:

  • Tabela: A tabela personalizada do Log Analytics onde os dados ingeridos são armazenados.
  • DCR: A regra de recolha de dados que define a transformação da ingestão.
  • Interface do Conector: A definição de conector de dados que aparece no hub de conteúdos do Microsoft Sentinel.
  • Regras de ligação de dados: A configuração do conector que recolhe dados da API de origem.

A sondagem aninhada da API é configurada nas regras de ligação de dados para um conector RestApiPoller.

O que é a consulta aninhada da API?

A sondagem encadeada de API é um padrão de sondagem CCF que encadeia chamadas à API REST. A primeira chamada à API, designada por etapa pai, devolve os valores necessários para chamadas posteriores à API, designadas por etapas filhas.

Por exemplo, uma API pode usar este padrão:

  1. GET /incidents devolve uma lista de IDs de incidentes.
  2. GET /incidents/{incidentId}/details retorna o registo completo do incidente para cada ID.

Uma única chamada à API não retorna os dados completos. O conector deve chamar o endpoint da lista, extrair cada incidentId, e depois chamar o endpoint de detalhes uma vez para cada ID.

Utilize o sondamento de API aninhado quando:

  • Um endpoint de lista devolve IDs de recursos, e um endpoint de detalhes exige cada ID no caminho do URL ou na string de consulta.
  • Uma resposta principal retorna um cursor, um token de sessão, o identificador da consulta ou o identificador de referência, de que um pedido subordinado necessita.
  • Uma resposta contém um array de valores que devem ser passados individualmente para outro endpoint.
  • A resposta principal contém os campos que pretende manter, e a resposta subordinada adiciona dados de enriquecimento que devem ser combinados numa única linha de saída.

Se uma única chamada de API devolver todos os dados de que precisas, não é necessário sondamento de API aninhado. Use a propriedade padrão eventsJsonPaths para extrair registos da resposta.

Como funciona o polling aninhado de APIs

A consulta periódica aninhada da API é configurada com estas secções:

Seção Location Purpose
request Passo principal Define o pedido principal da API. As propriedades da janela temporal estão configuradas aqui.
response Passo principal Define como os registos são extraídos da resposta principal.
stepInfo Passo principal Permite a sondagem encadeada e define as etapas subordinadas a executar em seguida.
stepCollectorConfigs Passo principal Define cada etapa do filho, incluindo o pedido e o tratamento da resposta do filho.
shouldJoinNestedData Passo de criança Determina se a resposta do filho substitui a saída do pai ou é associada ao registo do pai.

O fluxo de sondagem aninhada funciona da seguinte forma:

  1. O pedido principal é executado.
  2. A resposta dos pais é dividida em registos usando response.eventsJsonPaths.
  3. stepPlaceholdersParsingKql extrai valores de marcador de posição de cada registo principal.
  4. O CCF substitui os marcadores de posição na configuração da etapa subordinada.
  5. A CCF trata dos pedidos de crianças.
  6. A resposta do filho é enviada como linha de saída ou juntada ao registo pai, dependendo do valor de shouldJoinNestedData.

Esqueleto de configuração de sondagem encaixada

O exemplo seguinte mostra a estrutura de um conector RestApiPoller aninhado. As propriedades CCF padrão são abreviadas por ....

{
  "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"
        }
      }
    }
  }
}

Propriedades de votação aninhada

Property Location Description
stepInfo.stepType Passo principal Deve estar definido como Nested para permitir a sondagem aninhada da API.
stepInfo.nextSteps[].stepId Passo principal O nome do padrasto da criança. Este valor deve corresponder a uma chave em stepCollectorConfigs.
stepInfo.nextSteps[].stepPlaceholdersParsingKql Passo principal KQL que extrai valores da resposta principal. A consulta é executada em source, na qual a coluna data contém cada registo principal como uma cadeia JSON em bruto. Cada coluna projetada passa a ser um marcador de posição.
stepCollectorConfigs Passo principal Um mapa de definições de passos subordinados, indexado pelos valores stepId declarados em stepInfo.nextSteps.
shouldJoinNestedData Passo de criança Controla como a resposta da criança é entregue ao fluxo. Definido para false quando a resposta do filho contém o registo de saída completo. Define para true quando precisares de campos tanto das respostas do pai como do filho na mesma linha de saída.
joinedDataStepName Passo de criança O nome da coluna dynamic que armazena a resposta do elemento filho associada quando shouldJoinNestedData é true. Não usado quando shouldJoinNestedData é false.

Substituição provisória

Os marcadores de posição são extraídos por stepPlaceholdersParsingKql e referenciados com a sintaxe $placeholderName$.

Por exemplo, este KQL cria um marcador de posição chamado incidentId:

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

O passo filho pode então referenciar o marcador como $incidentId$:

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

A substituição de marcadores de posição é suportada em toda a configuração das etapas subordinadas, incluindo o apiEndpointpedido subordinadoheaders, queryParameters e queryParametersTemplate.

Configurar propriedades de pedidos filhos

O request bloco dentro de um passo filho suporta as propriedades comuns de pedido usadas para o sondamento de APIs.

Propriedades comuns de pedido de filhos incluem:

Property Description
apiEndpoint A extremidade filha da API. Pode incluir marcadores de posição, como $incidentId$.
httpMethod O método HTTP para o pedido filho, como GET ou POST.
headers Cabeçalhos do pedido para a chamada à API subordinada. A substituição provisória é suportada.
queryParameters Consultar parâmetros de string para a chamada API filha. A substituição provisória é suportada.
queryParametersTemplate Modelo usado para cenários de corpo do pedido ou payload de consulta. A substituição provisória é suportada.
isPostPayloadJson Definido para true quando o payload POST deve ser enviado como JSON.
rateLimitQPS O número máximo de pedidos por segundo.
rateLimitConfig Configuração de limite de taxa que pode usar cabeçalhos de limite de taxa devolvidos pela API.
retryCount Número de tentativas de repetição. Padrão: 3. Alcance suportado: 1 até 6.
timeoutInSeconds Tempo limite de solicitação em segundos. Padrão: 20. Alcance suportado: 1 até 180.

O pedido principal controla a janela temporal de sondagem. Configure propriedades da janela temporal como queryWindowInMin, queryTimeFormat, startTimeAttributeName, e endTimeAttributeName apenas no pedido pai. Os passos filhos são geralmente orientados por valores provisórios extraídos da resposta do pai.

Configurar paralelismo de pedidos filho

maxParallelism Controla quantas chamadas filhas podem ser executadas simultaneamente. O valor predefinido é 15.

maxParallelism não faz parte da configuração padrão do conector pai e não pode ser definido na etapa principal. Como as etapas subordinadas são transmitidas sem tradução dos nomes dos campos, pode definir maxParallelism no bloco request de uma etapa subordinada, se for necessário fazer um ajuste.

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

Escolha se quer juntar os dados pais e filhos

Use shouldJoinNestedData para controlar como as respostas da criança são entregues ao fluxo.

Utilize shouldJoinNestedData: false

Defina shouldJoinNestedData como false quando a resposta principal inclui apenas os valores necessários para o pedido subordinado, e a resposta subordinada contém o registo completo que pretende importar.

Por exemplo, use false quando:

  • A chamada principal retorna apenas os IDs dos incidentes.
  • A chamada da criança devolve os registos completos do incidente.
  • Não é necessário manter quaisquer campos pai na linha de destino.
"stepCollectorConfigs": {
  "fetchIncidentDetails": {
    "shouldJoinNestedData": false,
    "request": {
      "httpMethod": "GET",
      "apiEndpoint": "https://api.contoso.com/incidents/$incidentId$/details"
    },
    "response": {
      "eventsJsonPaths": [ "$" ],
      "format": "json"
    }
  }
}

Utilize shouldJoinNestedData: true

Defina shouldJoinNestedData como true quando precisar de campos tanto da resposta principal como da resposta secundária na mesma linha de destino.

Por exemplo, use true quando:

  • A chamada principal devolve campos de alerta como ID do alerta, gravidade e tempo de deteção.
  • A chamada subordinada retorna campos de enriquecimento, como o utilizador afetado, o IP de origem ou a geolocalização.
  • A transformação DCR precisa de mapear tanto os campos pai como filho na tabela de destino.

Quando shouldJoinNestedData for true, defina joinedDataStepName como o nome da coluna dynamic que armazena a resposta subordinada.

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

Exemplo: pedido de criança GET

Este exemplo utiliza uma API incidental Contoso em dois passos:

  1. O pedido principal chama GET /incidents e recebe uma lista de IDs de incidente.
  2. stepPlaceholdersParsingKql extrai incidentId de cada registo principal.
  3. O pedido da criança liga GET /incidents/$incidentId$/details uma vez por cada identificação do incidente.
  4. As respostas das crianças são enviadas para o fluxo como registos planos.

Resposta dos pais

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

Resposta da criança

{
  "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"
}

Configuração de sondagem

{
  "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"
        }
      }
    }
  }
}

Exemplo: pedido de criança POST com corpo JSON

Algumas APIs exigem que os identificadores da resposta principal sejam enviados no corpo de um pedido POST em vez de no caminho do URL ou na cadeia de consulta. Utilize queryParametersTemplate com isPostPayloadJson para este padrão.

Neste exemplo, a resposta principal retorna um incidentId, e o pedido dependente envia esse valor num corpo POST em 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"
    }
  }
}

Exemplo: Associar dados de enriquecimento do registo filho ao registo pai

Este exemplo utiliza uma API de alerta Contoso onde a resposta principal contém campos que devem ser preservados, e a resposta filha contém dados de enriquecimento.

Resposta dos pais

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

Resposta da criança

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

Configuração de sondagem

{
  "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"
        }
      }
    }
  }
}

A transformação DCR pode então projetar campos tanto do registo pai como da resposta filha associada.

Por exemplo:

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)

Nomes dos campos de autenticação por passos e paginação dos filhos

A tradução de nomes de campo aplica-se apenas à etapa principal. Cada entrada em stepCollectorConfigs é transmitida textualmente e não é remapeada. Como resultado, qualquer bloco auth ou paging dentro de um passo filho deve usar os nomes dos campos do passo filho mostrados nas tabelas seguintes.

Campos de autenticação

Nome do campo da etapa principal Nome do campo da subetapa
type AuthType
apiKey APIKey
apiKeyName APIKeyName
redirectUri para OAuth2 RedirectionEndpoint
isCredentialsInHeaders para OAuth2 ou JWT IsClientSecretInHeader
grantType para OAuth2 FlowName
queryParameters para JWT TokenEndpointQueryParameters
isJsonRequest para JWT IsTokenEndpointPostPayloadJson
userName / password pares chave-valor para JWT ou autenticação de sessão UsernameAttributeNamee UsernameAttributeValue / PasswordAttributeNamePasswordAttributeValue

Para OAuth2, o FlowName valor também é transformado. Por exemplo, use ClientCredentials em vez de client_credentials, e AuthCode em vez de authorization_code.

Campos de paginação

Nome do campo da etapa principal Nome do campo da subetapa
pageSizeParameterName pageSizeParaName

Todos os outros nomes dos campos de paginação e resposta são iguais para os passos pais e filhos.

Limits

A consulta encadeada da API tem os seguintes limites:

Limit Description
A criança intervém stepCollectorConfigs Uma configuração aninhada suporta até quatro entradas em stepCollectorConfigs.
Entradas em stepInfo.nextSteps stepInfo.nextSteps suporta até três entradas.
Referências circulares As referências circulares por passos não são suportadas e são rejeitadas durante a validação.

Conclua o conector

Depois de configurar as regras de ligação aninhada RestApiPoller , complete os restantes componentes do conector CCF:

  • Crie ou atualize a tabela de destinos.
  • Crie o DCR e a transformação.
  • Crie a definição da interface de utilizador do conector.
  • Empacota o conector num modelo de implementação ARM.
  • Desdobrar e testar o conector.

Para o processo de ponta a ponta, veja Criar um conector sem código para o Microsoft Sentinel.