Gegevensconnectors zonder code voor pull maken met geneste API-polling

Voor sommige REST-API's zijn sequentiële aanroepen vereist, waarbij het antwoord van het ene eindpunt invoer biedt die moet worden doorgegeven aan een ander eindpunt. De Microsoft Sentinel CCF (Codeless Connector Framework) ondersteunt dit patroon via geneste API-polling voor RestApiPoller connectors.

Important

Nested API-polling is momenteel in de publieke preview. De Azure Preview Supplemental Terms bevatten meer juridische termen die van toepassing zijn op Azure-functies in bèta, preview of anderszins nog niet algemeen beschikbaar zijn.

Gebruik geneste API-polling wanneer een bovenliggende API-aanroep id's, cursors of andere waarden retourneert die vereist zijn voor een of meer onderliggende API-aanroepen. CCF extraheert de vereiste waarden uit het bovenliggende antwoord, vervangt deze in de onderliggende aanvraag en verzendt de onderliggende antwoorden naar de geconfigureerde doeltabel.

Configureer geneste API-polling in een CCF pull connector door de logica voor de nesting in de RestApiPoller verbindingsregels te definiëren.

Raadpleeg Een connector zonder code maken voor Microsoft Sentinel voor het volledige end-to-endproces voor het maken en verpakken van een CCF-connector. Je kunt ook de Microsoft Sentinel-extensie voor Visual Studio Code gebruiken om geneste API-pollingworkflows te implementeren en te testen. Voor informatie over installatie en gebruik raadpleegt u Build custom connectors with AI in Microsoft Sentinel. Zie RestApiPoller voor de standaard-aanvraag-, antwoord-, authenticatie-, paging- en DCR-eigenschappen.

Opmerking

Als u een onafhankelijke softwareleverancier (ISV) bent die een Microsoft Sentinel-integratie bouwt met behulp van het Codeless Connector Framework, kan het Microsoft App Assure-team mogelijk helpen. Stuur een e-mail naar het App Assure-team om contact op te nemen.

Prerequisites

Voordat u geneste API-polling configureert, moet u het volgende begrijpen:

  • De API-eindpunten die de connector moet aanroepen.
  • Welke antwoordwaarden van de bovenliggende API-aanroep zijn vereist voor de onderliggende API-aanroep.
  • Het uitvoerschema voor de doeltabel.
  • Een standaard CCF-connector RestApiPoller maken.

Een volledige CCF-connector bevat de volgende onderdelen:

  • Tabel: De Log Analytics aangepaste tabel waarin opgenomen gegevens worden opgeslagen.
  • DCR: De regel voor gegevensverzameling waarmee de opnametransformatie wordt gedefinieerd.
  • Connectorinterface: de definitie van de gegevensconnector die wordt weergegeven in de Microsoft Sentinel-contenthub.
  • Regels voor gegevensverbinding: de connectorconfiguratie waarmee gegevens worden opgehaald uit de bron-API.

Geneste API-polling wordt geconfigureerd in de regels voor gegevensverbinding voor een RestApiPoller connector.

Wat is geneste API-polling?

Geneste API-polling is een CCF-pollingpatroon dat REST API-aanroepen koppelt. De eerste API-aanroep, de bovenliggende stap genoemd, retourneert waarden die nodig zijn voor latere API-aanroepen, die onderliggende stappen worden genoemd.

Een API kan bijvoorbeeld gebruikmaken van dit patroon:

  1. GET /incidents retourneert een lijst met incident-id's.
  2. GET /incidents/{incidentId}/details retourneert de volledige incidentrecord voor elke id.

Eén API-aanroep retourneert de volledige gegevens niet. De connector moet het lijsteindpunt aanroepen, elk incidentIdextraheren en vervolgens het detaileindpunt één keer aanroepen voor elke id.

Gebruik geneste API-polling wanneer:

  • Een lijsteindpunt retourneert resource-id's en voor een detaileindpunt is elke id in het URL-pad of de queryreeks vereist.
  • Een bovenliggende respons retourneert een cursor, sessietoken, query-ID of referentie-ID die een onderliggend verzoek nodig heeft.
  • Een antwoord bevat een matrix met waarden die afzonderlijk moeten worden doorgegeven aan een ander eindpunt.
  • Het bovenliggende antwoord bevat velden die u wilt behouden, en het onderliggende antwoord voegt verrijkingsgegevens toe die in dezelfde uitvoerrij moeten worden samengevoegd.

Als één API-aanroep alle benodigde gegevens retourneert, is geneste API-polling niet vereist. Gebruik de standaardeigenschap eventsJsonPaths om records uit het antwoord te extraheren.

Hoe geneste API-polling werkt

Geneste API-polling wordt geconfigureerd met de volgende secties:

Afdeling Locatie Purpose
request Bovenliggende stap Definieert de bovenliggende API-aanvraag. Eigenschappen van tijdvensters worden hier geconfigureerd.
response Bovenliggende stap Definieert hoe records worden geëxtraheerd uit het bovenliggende antwoord.
stepInfo Bovenliggende stap Maakt geneste polling mogelijk en definieert welke onderliggende stappen vervolgens worden uitgevoerd.
stepCollectorConfigs Bovenliggende stap Definieert elke onderliggende stap, inclusief de verwerking van het onderliggende verzoek en de respons.
shouldJoinNestedData Onderliggende stap Hiermee bepaalt u of het onderliggend antwoord de bovenliggende uitvoer vervangt of aan het bovenliggend record wordt toegevoegd.

Het geneste pollingproces werkt als volgt:

  1. De bovenliggende aanvraag wordt uitgevoerd.
  2. Het bovenliggende antwoord wordt gesplitst in records met behulp van response.eventsJsonPaths.
  3. stepPlaceholdersParsingKql extraheert tijdelijke aanduidingen uit elke bovenliggende record.
  4. CCF vervangt de plaatsaanduidingen in de configuratie van de onderliggende stap.
  5. CCF voert de onderliggende aanvragen uit.
  6. Het onderliggende antwoord wordt ofwel weergegeven als de uitvoerrij ofwel samengevoegd met de bovenliggende gegevensrecord, afhankelijk van de waarde van shouldJoinNestedData.

Sjabloon voor geneste pollingconfiguratie

In het volgende voorbeeld ziet u de structuur van een geneste RestApiPoller verbindingslijn. Standaard CCF-eigenschappen worden afgekort tot ....

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

Geneste polling-eigenschappen

Vastgoed Locatie Description
stepInfo.stepType Bovenliggende stap Moet op Nested zijn ingesteld om geneste API-polling in te schakelen.
stepInfo.nextSteps[].stepId Bovenliggende stap De naam van de onderliggende stap. Deze waarde moet overeenkomen met een sleutel in stepCollectorConfigs.
stepInfo.nextSteps[].stepPlaceholdersParsingKql Bovenliggende stap KQL waarmee waarden uit het bovenliggende antwoord worden geëxtraheerd. De query wordt uitgevoerd op source, waarbij de kolom data elk bovenliggend record bevat in de vorm van een ruwe JSON-tekenreeks. Elke geprojecteerde kolom wordt een tijdelijke aanduiding.
stepCollectorConfigs Bovenliggende stap Een kaart met onderliggende stapdefinities, gesleuteld door de stepId waarden die zijn gedeclareerd in stepInfo.nextSteps.
shouldJoinNestedData Onderliggende stap Hiermee bepaalt u hoe het onderliggende antwoord naar de stream wordt verzonden. Ingesteld op false wanneer het onderliggende antwoord de volledige uitvoerrecord bevat. Ingesteld op true wanneer u velden van zowel de bovenliggende als de onderliggende antwoorden in dezelfde uitvoerrij nodig hebt.
joinedDataStepName Onderliggende stap De naam van de dynamic kolom waarin het gekoppelde onderliggende antwoord wordt opgeslagen wanneer shouldJoinNestedData dit is true. Niet gebruikt wanneer shouldJoinNestedData is false.

Vervanging van plaatsaanduiding

Placeholders worden geëxtraheerd met stepPlaceholdersParsingKql en er wordt naar verwezen met de syntaxis $placeholderName$.

Met deze KQL maakt u bijvoorbeeld een tijdelijke aanduiding met de naam incidentId:

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

De onderliggende stap kan vervolgens naar de placeholder verwijzen als $incidentId$:

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

Vervanging van placeholders wordt ondersteund in de configuratie van de onderliggende stap, inclusief in de onderliggende aanvraag apiEndpoint, headers, queryParameters en queryParametersTemplate.

Eigenschappen van onderliggende aanvragen configureren

Het request-blok binnen een onderliggende stap ondersteunt de algemene aanvraag-eigenschappen die voor API-polling worden gebruikt.

Veelvoorkomende eigenschappen van onderliggende aanvragen zijn onder andere:

Vastgoed Description
apiEndpoint Het onderliggende API-eindpunt. U kunt tijdelijke aanduidingen opnemen, zoals $incidentId$.
httpMethod De HTTP-methode voor het onderliggende verzoek, zoals GET of POST.
headers Aanvraagheaders voor de onderliggende API-aanroep. Vervanging van tijdelijke aanduidingen wordt ondersteund.
queryParameters Queryreeksparameters voor de onderliggende API-aanroep. Vervanging van tijdelijke aanduidingen wordt ondersteund.
queryParametersTemplate Sjabloon die wordt gebruikt voor aanvraagbody- of query-payloadscenario's. Vervanging van tijdelijke aanduidingen wordt ondersteund.
isPostPayloadJson Ingesteld op true wanneer de POST-nettolading moet worden verzonden als JSON.
rateLimitQPS Het maximum aantal aanvragen per seconde.
rateLimitConfig Snelheidslimietconfiguratie die gebruik kan maken van headers voor frequentielimieten die door de API worden geretourneerd.
retryCount Aantal nieuwe pogingen. Standaard: 3. Ondersteund bereik: 1 tot 6.
timeoutInSeconds Time-out aanvragen in seconden. Standaard: 20. Ondersteund bereik: 1 tot 180.

De bovenliggende aanvraag bepaalt het tijdvenster voor polling. Configureer tijdvenstereigenschappen zoals queryWindowInMin, queryTimeFormat, startTimeAttributeName en endTimeAttributeName alleen voor de bovenliggende aanvraag. Onderliggende stappen worden meestal aangestuurd door placeholderwaarden die uit de bovenliggende respons zijn geëxtraheerd.

Parallelisme van onderliggende verzoeken configureren

maxParallelism bepaalt hoeveel onderliggende aanroepen gelijktijdig kunnen worden uitgevoerd. De standaardwaarde is 15.

maxParallelism maakt geen deel uit van de standaardconfiguratie van de bovenliggende connector en kan niet worden ingesteld op de bovenliggende stap. Omdat onderliggende stappen worden doorgegeven zonder omzetting van veldnamen, kunt u maxParallelism instellen binnen het request-blok van een onderliggende stap als een aanpassing nodig is.

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

Kies of u ouder- en kindgegevens wilt samenvoegen

Gebruik shouldJoinNestedData om te bepalen hoe onderliggende reacties naar de stream worden gestuurd.

Gebruik shouldJoinNestedData: false

Stel shouldJoinNestedData in op false wanneer de bovenliggende respons alleen de waarden bevat die vereist zijn voor de onderliggende aanvraag en de onderliggende respons het volledige gegevensrecord bevat dat u wilt importeren.

Gebruik bijvoorbeeld false wanneer:

  • De bovenliggende oproep geeft alleen incident-id's terug.
  • De onderliggende oproep geeft de volledige incidentrecords terug.
  • U hoeft in de doelrij geen bovenliggende velden te behouden.
"stepCollectorConfigs": {
  "fetchIncidentDetails": {
    "shouldJoinNestedData": false,
    "request": {
      "httpMethod": "GET",
      "apiEndpoint": "https://api.contoso.com/incidents/$incidentId$/details"
    },
    "response": {
      "eventsJsonPaths": [ "$" ],
      "format": "json"
    }
  }
}

Gebruik shouldJoinNestedData: true

Stel shouldJoinNestedData in op true wanneer u velden van zowel het hoofdantwoord als het subantwoord in dezelfde doelrij nodig hebt.

Gebruik bijvoorbeeld true wanneer:

  • De bovenliggende aanroep retourneert waarschuwingsvelden, zoals waarschuwings-id, ernst en detectietijd.
  • De onderliggende oproep retourneert verrijkingsvelden zoals betreffende gebruiker, bron-IP of geolocatie.
  • De DCR-transformatie moet zowel bovenliggende als onderliggende velden toewijzen aan de doeltabel.

Wanneer shouldJoinNestedData is ingesteld op true, stelt u joinedDataStepName in op de naam van de kolom dynamic waarin de onderliggende respons is opgeslagen.

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

Voorbeeld: GET child request

In dit voorbeeld wordt gebruikgemaakt van een API voor contoso-incidenten in twee stappen:

  1. De bovenliggende aanvraag roept GET /incidents aan en ontvangt een lijst met incident-ID's.
  2. stepPlaceholdersParsingKql extraheert incidentId uit elke bovenliggende record.
  3. De onderliggende aanroep roept GET /incidents/$incidentId$/details één keer per incident-id aan.
  4. De onderliggende antwoorden worden als platte records naar de stream verzonden.

Bovenliggend antwoord

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

Subantwoord

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

Polling-configuratie

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

Voorbeeld: POST-onderliggend verzoek met een JSON-inhoud

Voor sommige API's moeten identificatoren uit de bovenliggende respons in een POST-body worden verzonden in plaats van in het URL-pad of in de querystring. Gebruik queryParametersTemplate met isPostPayloadJson voor dit patroon.

In dit voorbeeld retourneert de bovenliggende respons een incidentId, en stuurt de onderliggende aanvraag die waarde mee in een JSON POST-body.

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

Voorbeeld: Verrijkingsgegevens van onderliggende records koppelen aan het bovenliggende record

In dit voorbeeld wordt een Contoso-waarschuwings-API gebruikt waarin het bovenliggende antwoord velden bevat die moeten worden bewaard en het onderliggende antwoord verrijkingsgegevens bevat.

Bovenliggend antwoord

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

Subantwoord

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

Polling-configuratie

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

De DCR-transformatie kan vervolgens velden van zowel de bovenliggende record als het gekoppelde onderliggende antwoord projecteert.

Voorbeeld:

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)

Authenticatie van onderliggende stappen en veldnamen voor paginering

De vertaling van veldnamen is alleen van toepassing op de bovenliggende stap. Elke invoer in stepCollectorConfigs wordt ongewijzigd doorgegeven en niet opnieuw gekoppeld. Daarom moet elk auth- of paging-blok binnen een onderliggende stap de veldnamen van die onderliggende stap gebruiken, zoals weergegeven in de volgende tabellen.

Authenticatievelden

Veldnaam van bovenliggende stap Veldnaam van onderliggende stap
type AuthType
apiKey APIKey
apiKeyName APIKeyName
redirectUri voor OAuth2 RedirectionEndpoint
isCredentialsInHeaders voor OAuth2 of JWT IsClientSecretInHeader
grantType voor OAuth2 FlowName
queryParameters voor JWT TokenEndpointQueryParameters
isJsonRequest voor JWT IsTokenEndpointPostPayloadJson
userName / password sleutel-waardeparen voor JWT of sessieverificatie UsernameAttributeName en UsernameAttributeValue / PasswordAttributeName en PasswordAttributeValue

Voor OAuth2 wordt de FlowName waarde ook getransformeerd. Gebruik bijvoorbeeld ClientCredentials in plaats van client_credentials, en AuthCode in plaats van authorization_code.

Pagingsvelden

Veldnaam van bovenliggende stap Veldnaam van onderliggende stap
pageSizeParameterName pageSizeParaName

Alle andere namen van paginerings- en responsvelden zijn gelijk voor bovenliggende en onderliggende stappen.

Limits

Geneste API-polling heeft de volgende limieten:

Limit Description
Onderliggende stappen in stepCollectorConfigs Een geneste configuratie ondersteunt maximaal vier vermeldingen in stepCollectorConfigs.
Vermeldingen in stepInfo.nextSteps stepInfo.nextSteps ondersteunt maximaal drie vermeldingen.
Kringverwijzingen Verwijzingen naar kringstappen worden niet ondersteund en worden geweigerd tijdens de validatie.

De connector voltooien

Nadat u de geneste RestApiPoller verbindingsregels hebt geconfigureerd, voltooi de resterende onderdelen van de CCF-connector:

  • Maak de doeltabel of werk deze bij.
  • Maak de DCR en de transformatie aan.
  • Maak de definitie van de gebruikersinterface van de connector.
  • De connector inpakken in een ARM-implementatiesjabloon.
  • Implementeer en test de connector.

Zie Een connector zonder code maken voor Microsoft Sentinel voor het end-to-endproces.