Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Alcune API REST richiedono chiamate sequenziali, in cui la risposta da un endpoint fornisce l'input che deve essere passato a un altro endpoint. Il framework CCF (Codeless Connector Framework) Microsoft Sentinel supporta questo modello tramite il polling dell'API annidata per RestApiPoller i connettori.
Importante
Il polling dell'API annidata è attualmente in anteprima pubblica. I Termini Supplementari di Azure Preview includono termini legali più validi che si applicano alle funzionalità di Azure in beta, anteprima o in altro modo non ancora rilasciate in disponibilità generale.
Usare il polling dell'API annidata quando una chiamata API padre restituisce identificatori, cursori o altri valori richiesti da una o più chiamate API figlio. CCF estrae i valori necessari dalla risposta padre, li inserisce nella richiesta figlia e invia le risposte figlie alla tabella di destinazione configurata.
Configurare il polling dell'API annidata in un connettore di pull CCF definendo la logica di annidamento nelle regole di connessione RestApiPoller.
Per la procedura end-to-end per creare e pacchettizzare un connettore CCF, vedi Creare un connettore senza codice per Microsoft Sentinel. Puoi anche utilizzare l'estensione Microsoft Sentinel per Visual Studio Code per implementare e testare flussi di lavoro di polling API annidati. Per informazioni sulla configurazione e sull'utilizzo, vedere Build custom connectors with AI in Microsoft Sentinel. Per le proprietà standard RestApiPoller di richiesta, risposta, autenticazione, paging e DCR, vedere Informazioni di riferimento sulle regole di connessione del connettore dati RestApiPoller.
Annotazioni
Se si è un fornitore di software indipendente (ISV) che crea un'integrazione Microsoft Sentinel usando il framework del connettore codeless, il team di Microsoft App Assure può essere in grado di assistere. Per coinvolgere il team di App Assure, inviare un messaggio di posta elettronica a azuresentinelpartner@microsoft.com.
Prerequisiti
Prima di configurare il polling dell'API annidata, assicurarsi di comprendere:
- Gli endpoint API che il connettore deve chiamare.
- Quali valori di risposta della chiamata API padre sono richiesti dalla chiamata API figlio.
- Schema di output per la tabella di destinazione.
- Come creare un connettore CCF
RestApiPollerstandard.
Un connettore CCF completo include i componenti seguenti:
- Tabella: Log Analytics tabella personalizzata in cui vengono archiviati i dati inseriti.
- DCR: regola di raccolta dati che definisce la trasformazione di inserimento.
- Interfaccia utente del connettore: la definizione del connettore dati visualizzata nell'hub contenuti di Microsoft Sentinel.
- Regole di connessione dati: configurazione del connettore che recupera i dati dall'API di origine.
Il polling dell'API annidata è configurato nelle regole di connessione dati per un connettore RestApiPoller.
Che cos'è il polling dell'API annidata?
Il polling dell'API annidata è un modello di polling CCF che concatena le chiamate API REST. La prima chiamata API, denominata passaggio padre, restituisce i valori richiesti dalle chiamate API successive, denominati passaggi figlio.
Ad esempio, un'API potrebbe usare questo modello:
-
GET /incidentsrestituisce un elenco di ID incidente. -
GET /incidents/{incidentId}/detailsrestituisce il record completo dell'incidente per ogni ID.
Una singola chiamata API non restituisce i dati completi. Il connettore deve chiamare l'endpoint dell'elenco, estrarre ogni incidentId e quindi chiamare l'endpoint dei dettagli una volta per ogni ID.
Utilizza il polling API annidato quando:
- Un endpoint di elenco restituisce gli ID delle risorse e un endpoint di dettaglio richiede ciascun ID nel percorso dell'URL o nella stringa di query.
- Una risposta padre restituisce un cursore, un token di sessione, un ID di query o un ID di riferimento di cui una richiesta figlio ha bisogno.
- Una risposta contiene una matrice di valori che devono essere passati singolarmente a un altro endpoint.
- La risposta padre contiene i campi da mantenere e la risposta figlio aggiunge i dati di arricchimento che devono essere uniti nella stessa riga di output.
Se una singola chiamata API restituisce tutti i dati necessari, il polling dell'API annidato non è obbligatorio. Utilizzare la proprietà standard eventsJsonPaths per estrarre i record dalla risposta.
Funzionamento del polling dell'API annidata
Il polling dell'API annidata è configurato con queste sezioni:
| Sezione | Posizione | Purpose |
|---|---|---|
request |
Passaggio padre | Definisce la richiesta API principale. Le proprietà della finestra temporale si configurano qui. |
response |
Passaggio padre | Definisce come i record vengono estratti dalla risposta principale. |
stepInfo |
Passaggio padre | Abilita il polling annidato e definisce i passaggi figlio da eseguire in seguito. |
stepCollectorConfigs |
Passaggio padre | Definisce ogni passaggio figlio, inclusa la gestione delle richieste e delle risposte figlio. |
shouldJoinNestedData |
Passaggio figlio | Definisce se la risposta del figlio sostituisce l'output del padre o viene aggiunta al record del padre. |
Il flusso di polling annidato funziona come segue:
- La richiesta principale viene eseguita.
- La risposta principale viene suddivisa in record utilizzando
response.eventsJsonPaths. -
stepPlaceholdersParsingKqlestrae i valori dei segnaposto da ogni record principale. - CCF sostituisce i segnaposto nella configurazione del passaggio figlio.
- CCF esegue le richieste figlio.
- La risposta figlio viene inviata come riga di output o unita al record padre, a seconda del valore di
shouldJoinNestedData.
Modello di configurazione del polling annidato
Nell'esempio seguente viene illustrata la struttura di un connettore annidato RestApiPoller . Le proprietà CCF standard sono abbreviate 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"
}
}
}
}
}
Proprietà di polling annidate
| Proprietà | Posizione | Descrizione |
|---|---|---|
stepInfo.stepType |
Passaggio padre | Deve essere impostato su Nested per abilitare il polling API annidato. |
stepInfo.nextSteps[].stepId |
Passaggio padre | Nome del passaggio figlio. Questo valore deve corrispondere a una chiave in stepCollectorConfigs. |
stepInfo.nextSteps[].stepPlaceholdersParsingKql |
Passaggio padre | KQL che estrae i valori dalla risposta principale. La query viene eseguita su source, dove la colonna data contiene ogni record padre come stringa JSON non elaborata. Ogni colonna proiettata diventa un segnaposto. |
stepCollectorConfigs |
Passaggio padre | Mappa delle definizioni dei passaggi figlio, con chiave in base ai stepId valori dichiarati in stepInfo.nextSteps. |
shouldJoinNestedData |
Passaggio figlio | Controllare la modalità di recapito della risposta figlio al flusso. Impostare su false quando la risposta figlio contiene il record di output completo. Impostare su true quando sono necessari campi delle risposte padre e figlio nella stessa riga di output. |
joinedDataStepName |
Passaggio figlio | Nome della colonna dynamic che memorizza la risposta figlia combinata quando shouldJoinNestedData è true. Non usato quando shouldJoinNestedData è false. |
Sostituzione dei segnaposto
I segnaposto vengono estratti tramite stepPlaceholdersParsingKql e a essi si fa riferimento con la sintassi $placeholderName$.
Ad esempio, questo KQL crea un segnaposto denominato incidentId:
source
| project res = parse_json(data)
| project incidentId = res.incidentId
Il passaggio figlio può quindi fare riferimento al segnaposto come segue: $incidentId$
"apiEndpoint": "https://api.example.com/incidents/$incidentId$/details"
La sostituzione dei segnaposto è supportata in tutta la configurazione del passaggio figlio, inclusa la richiesta figlio apiEndpoint, headers, queryParameters e queryParametersTemplate.
Configurare le proprietà della richiesta figlio
Il blocco request all'interno di un passaggio figlio supporta le proprietà comuni della richiesta usate per il polling delle API.
Le proprietà comuni delle richieste figlio includono:
| Proprietà | Descrizione |
|---|---|
apiEndpoint |
L'endpoint figlio dell'API. È possibile includere segnaposto come $incidentId$. |
httpMethod |
Il metodo HTTP per la richiesta figlia, ad esempio GET o POST. |
headers |
Intestazioni di richiesta per la chiamata API figlio. La sostituzione segnaposto è supportata. |
queryParameters |
Parametri della stringa di query per la chiamata all’API figlia. La sostituzione segnaposto è supportata. |
queryParametersTemplate |
Modello usato negli scenari di payload del corpo della richiesta o della query. La sostituzione segnaposto è supportata. |
isPostPayloadJson |
Impostare su true quando il payload POST deve essere inviato come JSON. |
rateLimitQPS |
Numero massimo di richieste al secondo. |
rateLimitConfig |
Configurazione del limite di frequenza che può usare le intestazioni del limite di frequenza restituite dall'API. |
retryCount |
Numero di tentativi di ripetizione. Impostazione predefinita: 3. Intervallo supportato: 1 da a 6. |
timeoutInSeconds |
Periodo di timeout, espresso in secondi, della richiesta. Impostazione predefinita: 20. Intervallo supportato: 1 da a 180. |
La richiesta principale determina la finestra temporale di polling. Configurare le proprietà della finestra temporale, quali queryWindowInMin, queryTimeFormat, startTimeAttributeName e endTimeAttributeName, solo sulla richiesta padre. I passaggi secondari sono in genere basati sui valori di segnaposto estratti dalla risposta principale.
Configurare il parallelismo delle richieste figlie
maxParallelism determina quante chiamate figlie possono essere eseguite in parallelo. Il valore predefinito è 15.
maxParallelism non fa parte della configurazione del connettore padre standard e non può essere impostata nel passaggio padre. Poiché i passaggi secondari vengono trasferiti senza traduzione dei nomi dei campi, è possibile impostare maxParallelism all'interno del blocco request di un passaggio secondario, se è necessaria una modifica.
"stepCollectorConfigs": {
"fetchIncidentDetails": {
"shouldJoinNestedData": false,
"request": {
"httpMethod": "GET",
"apiEndpoint": "https://api.contoso.com/incidents/$incidentId$/details",
"maxParallelism": 15
},
"response": {
"eventsJsonPaths": [ "$" ],
"format": "json"
}
}
}
Scegliere se aggiungere dati padre e figlio
Usare shouldJoinNestedData per controllare il modo in cui le risposte figlio vengono recapitate al flusso.
Utilizzare shouldJoinNestedData: false.
Impostare shouldJoinNestedData su false quando la risposta padre fornisce solo i valori necessari per la richiesta figlio e la risposta figlio include il record completo che si desidera acquisire.
Ad esempio, usare false quando:
- La chiamata principale restituisce solo gli ID degli incidenti.
- La chiamata figlio restituisce i record completi degli eventi imprevisti.
- Non è necessario conservare alcun campo padre nella riga di destinazione.
"stepCollectorConfigs": {
"fetchIncidentDetails": {
"shouldJoinNestedData": false,
"request": {
"httpMethod": "GET",
"apiEndpoint": "https://api.contoso.com/incidents/$incidentId$/details"
},
"response": {
"eventsJsonPaths": [ "$" ],
"format": "json"
}
}
}
Utilizzare shouldJoinNestedData: true.
Impostare shouldJoinNestedData su true quando servono campi sia della risposta padre sia della risposta figlia nella stessa riga di destinazione.
Ad esempio, usare true quando:
- La chiamata principale restituisce campi dell'avviso come l'ID dell'avviso, la gravità e l'ora di rilevamento.
- La chiamata figlio restituisce campi di arricchimento come l'utente interessato, l'indirizzo IP di origine o la geolocalizzazione.
- La trasformazione DCR deve mappare sia i campi padre che quelli figlio nella tabella di destinazione.
Quando shouldJoinNestedData è true, impostare joinedDataStepName sul nome della colonna dynamic che contiene la risposta figlio.
"stepCollectorConfigs": {
"fetchAlertEnrichment": {
"shouldJoinNestedData": true,
"joinedDataStepName": "enrichment",
"request": {
"httpMethod": "GET",
"apiEndpoint": "https://api.contoso.com/alerts/$alertId$/enrichment"
},
"response": {
"eventsJsonPaths": [ "$" ],
"format": "json"
}
}
}
Esempio: richiesta figlio GET
Questo esempio utilizza un'API per gli incidenti Contoso in due passaggi:
- La richiesta principale chiama
GET /incidentse riceve un elenco di ID degli incidenti. -
stepPlaceholdersParsingKqlestraeincidentIdda ogni record padre. - La richiesta figlio invoca
GET /incidents/$incidentId$/detailsuna volta per ogni ID incidente. - Le risposte figlio vengono inviate al flusso come record flat.
Risposta padre
{
"incidents": [
{ "incidentId": "INC-001" },
{ "incidentId": "INC-002" },
{ "incidentId": "INC-003" }
]
}
Risposta figlio
{
"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"
}
Configurazione del polling
{
"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"
}
}
}
}
}
Esempio: richiesta figlio POST con corpo JSON
Alcune API richiedono l'invio di identificatori dalla risposta padre in un corpo POST anziché nel percorso URL o nella stringa di query. Usare queryParametersTemplate con isPostPayloadJson per questo modello.
In questo esempio, la risposta padre restituisce un incidentId e la richiesta figlio invia tale valore nel corpo JSON di una richiesta POST.
"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"
}
}
}
Esempio: associare i dati di arricchimento del record figlio al record padre
Questo esempio usa un'API di avviso Contoso in cui la risposta padre contiene campi che devono essere mantenuti e la risposta figlio contiene dati di arricchimento.
Risposta padre
{
"alerts": [
{
"alertId": "ALT-001",
"severity": "High",
"detectedAt": "2026-05-30T14:22:00Z",
"riskScore": 92
}
]
}
Risposta figlio
{
"alertId": "ALT-001",
"affectedUser": "bob@contoso.com",
"sourceIp": "198.51.100.77",
"geolocation": "US/Virginia",
"relatedIncidentId": "INC-042"
}
Configurazione del polling
{
"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"
}
}
}
}
}
La trasformazione DCR può quindi proiettare i campi sia dal record padre che dalla risposta figlio unita.
Per esempio:
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)
Autenticazione dei passaggi secondari e nomi dei campi di impaginazione
La traduzione dei nomi di campo si applica solo al passaggio padre. Ogni elemento in stepCollectorConfigs viene lasciato invariato e non viene rimappato. Di conseguenza, qualsiasi blocco auth o paging all'interno di una fase figlia deve usare i nomi dei campi della fase figlia mostrati nelle tabelle seguenti.
Campi di autenticazione
| Nome del campo del passaggio principale | Nome campo passaggio figlio |
|---|---|
type |
AuthType |
apiKey |
APIKey |
apiKeyName |
APIKeyName |
redirectUri per OAuth2 |
RedirectionEndpoint |
isCredentialsInHeaders per OAuth2 o JWT |
IsClientSecretInHeader |
grantType per OAuth2 |
FlowName |
queryParameters per JWT |
TokenEndpointQueryParameters |
isJsonRequest per JWT |
IsTokenEndpointPostPayloadJson |
userName
/
password coppie chiave-valore per JWT o autenticazione della sessione |
UsernameAttributeName e UsernameAttributeValue / PasswordAttributeName e PasswordAttributeValue |
Anche per OAuth2 il FlowName valore viene trasformato. Ad esempio, usare ClientCredentials anziché client_credentialse AuthCode anziché authorization_code.
Campi di paging
| Nome del campo del passaggio principale | Nome campo passaggio figlio |
|---|---|
pageSizeParameterName |
pageSizeParaName |
Tutti gli altri nomi dei campi di paging e risposta sono gli stessi per i passaggi padre e figlio.
Limits
Il polling dell'API annidata presenta i limiti seguenti:
| Limit | Descrizione |
|---|---|
Passaggi secondari in stepCollectorConfigs |
Una configurazione annidata supporta fino a quattro voci in stepCollectorConfigs. |
Voci in stepInfo.nextSteps |
stepInfo.nextSteps consente fino a tre voci. |
| Riferimenti circolari | I riferimenti ai passaggi circolari non sono supportati e vengono rifiutati durante la convalida. |
Completare il connettore
Dopo aver configurato le regole di connessione annidate RestApiPoller, completare i componenti rimanenti del connettore CCF:
- Creare o aggiornare la tabella di destinazione.
- Creare il DCR e la trasformazione.
- Creare la definizione dell'interfaccia utente del connettore.
- Inserisci il connettore in un modello di distribuzione ARM.
- Distribuire e testare il connettore.
Per il processo end-to-end, vedere Creare un connettore senza codice per Microsoft Sentinel.