Skip to content

Add Cloudflare Security Events CCF connector (GraphQL Analytics API)#1

Draft
Tom Plant (pl4nty) wants to merge 5 commits into
feature/cloudflare-graphql-api-ccffrom
claude/gracious-franklin-INrYc
Draft

Add Cloudflare Security Events CCF connector (GraphQL Analytics API)#1
Tom Plant (pl4nty) wants to merge 5 commits into
feature/cloudflare-graphql-api-ccffrom
claude/gracious-franklin-INrYc

Conversation

@pl4nty

Copy link
Copy Markdown
Member

Summary

Adds a Codeless Connector Framework (CCF) RestApiPoller data connector that ingests Cloudflare security / firewall events into Microsoft Sentinel via the Cloudflare GraphQL Analytics API (firewallEventsAdaptive dataset, queried at account scope).

It lives alongside the existing CloudflareApi_CCF (audit logs) and CloudflareLog_CCF connectors in the Cloudflare CCF solution and mirrors their four-file structure.

New folder: Solutions/Cloudflare CCF/Data Connectors/CloudflareSecurityEvents_CCF/

File Purpose
CloudflareSecurityEvents_ConnectorDefinition.json UI / connector definition (CloudflareSecurityEventsDefinition)
CloudflareSecurityEvents_PollerConfig.json RestApiPoller polling config (GraphQL POST)
CloudflareSecurityEvents_DCR.json DCR: stream + camelCase→PascalCase transform
CloudflareSecurityEvents_Table.json Custom table CloudflareSecurityEvents_CL (90-day retention)

How it works

  • POST to https://api.cloudflare.com/client/v4/graphql with a templated GraphQL body (queryParametersTemplate + isPostPayloadJson: true).
  • The account tag is injected via parameters('accountId'); the poll window is injected via the CCF {_QueryWindowStartTime} / {_QueryWindowEndTime} tokens (queryTimeFormat: yyyy-MM-ddTHH:mm:ssZ, queryWindowInMin: 10).
  • Auth: Bearer API token (Account Analytics > Read scope).
  • The DCR maps Cloudflare's camelCase fields to PascalCase columns and derives TimeGenerated from the event datetime.

Validation

The exact GraphQL query embedded in queryParametersTemplate was tested live against the Cloudflare GraphQL API:

  • ARM concat(...) resolution simulated → produces valid JSON body with the correct accountTag and time tokens.
  • Live request returned HTTP 200, no GraphQL errors, 34 fields per row on a populated account.
  • All four files parse as valid JSON; stream name, dataType/table name, and connectorDefinitionName/definition id are all consistent.

Conventions followed (per Azure/Azure-Sentinel PR Azure#13317 feedback)

  • PascalCase column names with per-column + table descriptions.
  • httpMethod / queryWindowInMin hardcoded (not parameterized).
  • dataType matches the table name; connectorDefinitionName matches the definition id; definition file name matches id.
  • retentionInDays set; connector marked isPreview: true.

Design decisions / notes

  • Account scope (one connection covers every zone in the account), per request. ZoneTag is included so events remain attributable per-zone.
  • Field set (34 columns) intentionally limited to fields available across Cloudflare plans/entitlements. Entitlement-gated fields (Bot Management botScore*, WAF Attack Score waf*Score, Content Scanning, Leaked Credentials, JA3/JA4, Firewall-for-AI, fraud, API Shield, attack-signature fields) were excluded from v1 — requesting a field not present in a customer's GraphQL schema fails the whole query. These can be added as optional/enriched variants later.
  • Pagination: firewallEventsAdaptive is cursor-less, so the query uses orderBy: [datetime_ASC] with limit: 10000 per poll window (the API max). Very high-volume accounts exceeding 10k events per 10-minute window may need a smaller queryWindowInMin.

Follow-ups (not in this PR)

  • Wire the connector into the solution Package/mainTemplate.json + createUiDefinition.json, bump solution version, add ReleaseNotes.md entry.
  • Optional: analytic rules / hunting queries / parser + sample data for CloudflareSecurityEvents_CL.

https://claude.ai/code/session_01Ayem6m5j3y4xFsydDW1FBv


Generated by Claude Code

Claude (claude) and others added 5 commits June 6, 2026 02:12
…API)

Adds a Codeless Connector Framework (CCF) RestApiPoller connector that
ingests Cloudflare security/firewall events into Microsoft Sentinel via
the Cloudflare GraphQL Analytics API (firewallEventsAdaptive dataset,
queried at account scope).

- POST to https://api.cloudflare.com/client/v4/graphql with a templated
  GraphQL body (queryParametersTemplate + isPostPayloadJson), injecting the
  account tag via parameters('accountId') and the poll window via the
  {_QueryWindowStartTime}/{_QueryWindowEndTime} tokens.
- Bearer API token auth (Account Analytics > Read scope).
- DCR maps the camelCase GraphQL fields to PascalCase columns and derives
  TimeGenerated from the event datetime.
- New custom table CloudflareSecurityEvents_CL (90-day retention) with a
  broadly-available core field set (34 columns) chosen for compatibility
  across Cloudflare plans/entitlements.

Mirrors the structure of the sibling CloudflareApi_CCF (audit logs) connector.
Signed-off-by: GitHub <noreply@github.com>
The transformKql failed to compile (InvalidTransformQuery) because the
incoming column 'datetime' is parsed as the KQL datetime type keyword, and
'source' collides with the transform's implicit input table name. Reference
all incoming columns with bracket notation (['datetime'], ['source'], ...)
and use a single project to rename to the PascalCase table columns.
Apply the same bracket-quoted column reference fix to the transformKql
embedded in the packaged mainTemplate.json and 3.0.4.zip, so the deployed
artifact matches the corrected DCR and the data collection rule compiles.
Align with the project convention (PascalCase columns, simple transform) and
remove the brittle bracket-quoted rename.

- Do the camelCase->PascalCase mapping in the GraphQL query itself via field
  aliases (e.g. 'Action: action', 'TimeGenerated: datetime'), so the polled
  payload already uses the table's column names.
- DCR stream now declares PascalCase columns (TimeGenerated typed datetime,
  which parses the ISO8601 value), so the transform collapses to 'source'.
- This also sidesteps the KQL reserved-word collisions ('datetime', 'source')
  that forced the previous bracket-quoted project.

Applied to the source files and the packaged mainTemplate.json / 3.0.4.zip.
Verified the full aliased query against the live GraphQL API (HTTP 200, no
errors, all 34 PascalCase fields returned).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants