Skip to main content

Sending Netskope AI SecOps Events to Microsoft Sentinel

  • August 10, 2026
  • 0 replies
  • 29 views

Kmaheshwari
Netskope Employee
Forum|alt.badge.img+4

Netskope AI SecOps can deliver Cases, User Risk, and AI Risk events to Microsoft Sentinel in near real time through Outbound Webhooks and the Azure Monitor Logs Ingestion API. This article walks through the full setup: the Azure resources, the identity, the webhook configuration, and the queries to work with the data. Everything is copy-paste ready — replace the <angle-bracket> placeholders with your own values.

Figure 1. The AI SecOps Integrations page with the Outbound webhooks tile.
 

 

What you will build

 

 

Prerequisites

  • An Azure subscription with an existing Log Analytics workspace (Sentinel-enabled or plain Log Analytics both work).
  • Permission to deploy resources into a resource group, create a Microsoft Entra app registration, and assign Azure roles.
  • Azure CLI. Azure Cloud Shell works well; all commands below are single-line and run in PowerShell or bash.
  • A Netskope AI SecOps tenant with the Outbound Webhooks feature.

Values you will collect along the way

 

Value

Where you get it

Used in

<workspace-name>

Your existing Log Analytics workspace

Step 1

<workspace-resource-group>

Resource group that contains the workspace

Step 1

<resource-group>

Resource group for the DCE and DCR (may be the same)

Steps 1–2

<dce-ingestion-endpoint>

Step 1 deployment output

Step 4

<dcr-immutable-id>

Step 1 deployment output

Step 4

<dcr-resource-id>

Step 1 deployment output

Step 2

<tenant-id>

Your Entra tenant ID — Step 2 (az account show)

Step 4 (Token URL)

<client-id>

Your app registration's Application (client) ID — Step 2

Steps 2 and 4

<client-secret>

Step 2 credential creation — shown once

Step 4

 

Step 1 — Deploy the table, DCE, and DCR

Save the ARM template below as netskope-aisecops-dce-dcr.json. It creates three resources: the custom table NetskopeAISecOps_CL inside your workspace, a Data Collection Endpoint, and a Data Collection Rule wired to both. The workspace may live in a different resource group than the DCE and DCR; the template asks for both values. The DCE and DCR must be created in the workspace’s region — the deploy snippet below fetches it with one command and passes it in. The deployment is idempotent — re-running it updates in place.

Deploy it. If you omit --parameters, the CLI prompts for the workspace name and its resource group:

https://github.com/netskopeoss/Netskope_Web_Transactions_Azure_Sentinel/blob/main/Netskope-AI-SecOps-AzureSentinel/netskope-aisecops-dce-dcr.json

$loc = az monitor log-analytics workspace show --resource-group <workspace-resource-group> --workspace-name <workspace-name> --query location -o tsv

 

az deployment group create --resource-group <resource-group> --name aisecops-webhook --template-file netskope-aisecops-dce-dcr.json --parameters workspaceName=<workspace-name> workspaceResourceGroup=<workspace-resource-group> location=$loc

 

az deployment group show --resource-group <resource-group> --name aisecops-webhook --query properties.outputs

 Record four outputs: dceLogsIngestionEndpoint, dcrImmutableId, dcrResourceIdForRoleAssignment, and destinationUrl (the last is useful for the optional curl test in Step 5).

 

Step 2 — Create the Microsoft Entra identity

The webhook authenticates with OAuth2 client credentials. Create an app registration, generate a secret, and grant it write access on the DCR:

az ad app create --display-name "netskope-aisecops-webhook" --query appId -o tsv

az ad sp create --id <client-id>

az ad app credential reset --id <client-id> --append --years 1

az role assignment create --assignee <client-id> --role "Monitoring Metrics Publisher" --scope <dcr-resource-id>

az account show --query tenantId -o tsv

 

Four things to know:

  1. The first command prints the Application (client) ID — that is your <client-id>. It is unique to the app you just created in your own Entra tenant. There is no shared or Netskope-provided client ID for this integration; values copied from another environment or from screenshots will not work in yours.
  2. The credential reset output shows the client secret once in its password field. Copy the secret Value into a secret store immediately. Do not confuse it with the Secret ID: the Value contains special characters like ~ and dots and is what authenticates; the Secret ID is a plain GUID used only for managing the credential.
  3. The Monitoring Metrics Publisher role sits on the DCR itself, so a single assignment covers the integration. If the role command returns "principal not found", the service principal has not replicated yet — wait a minute and re-run just that command.
  4. The last command prints your Entra tenant ID. You will use it in Step 4 to build the Token URL: https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token. Like the client ID, it is specific to your tenant.

Portal alternative: Microsoft Entra ID → App registrations → New registration, then Certificates & secrets → New client secret. Copy the Value column right after creation.

 

Step 3 — Create the parser function

Save the query below as NetskopeAISecOpsEvents.kql and register it as a workspace function:

https://github.com/netskopeoss/Netskope_Web_Transactions_Azure_Sentinel/blob/main/Netskope-AI-SecOps-AzureSentinel/NetskopeAISecOpsEvents.kql

$parser = Get-Content ./NetskopeAISecOpsEvents.kql -Raw

az monitor log-analytics workspace saved-search create --resource-group <workspace-resource-group> --workspace-name <workspace-name> --name NetskopeAISecOpsEvents --category Netskope --display-name "NetskopeAISecOpsEvents" --saved-query $parser --func-alias NetskopeAISecOpsEvents

NetskopeAISecOps_CL is the table — where the data is stored. 

 

 

Step 4 — Configure the webhook in AI SecOps

In your Netskope tenant, open AI SecOps → Configuration → Integrations → Outbound webhooks and create a webhook.

Connection tab

Field

Value

Name

Any label, for example Azure Sentinel

Destination URL

<dce-ingestion-endpoint>  — the HOST ONLY, e.g. https://dce-netskope-aisecops-xxxx.eastus-1.ingest.monitor.azure.com. Do not append the path or query string; the webhook builds the full URL from the Payload tab fields

Auth type

OAuth2 client credentials

Token URL

https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token  — built with your own Entra tenant ID from Step 2

Client ID

<client-id>  — the Application (client) ID of the app you created in Step 2

Client secret

<client-secret>  — the Value, not the Secret ID

Scope

https://monitor.azure.com/.default  — required; an empty Scope fails the token request

Audience / resource

Leave empty

Custom headers

None

Verify TLS certificate

On. Leave the Custom CA bundle empty — Azure ingestion endpoints present publicly trusted certificates

 

Figure 2. Connection tab filled in.


 

Azure Government note: use https://login.microsoftonline.us/<tenant-id>/oauth2/v2.0/token as the Token URL and https://monitor.azure.us/.default as the Scope.

 

Events tab

Select the event types to deliver. All Cases, User Risk, and AI Risk types can share this single webhook — the table and parser handle every type.

Figure 3. Events tab with event types selected.

 

Payload tab

 

Field

Value

Included data fields

Core plus any optional groups you want (Case metadata, Investigation, Entities, File lineage, Risk detail). Note the PII markers — enable groups per your data-handling policy

Wrapping mode

Array (batch-shaped) — the Logs Ingestion API requires a JSON array body

DCR immutable ID

<dcr-immutable-id>  — from the Step 1 outputs

DCR stream name

Custom-NetskopeAISecOps_CL

Static fields

None required

Severity mapping

Keep the defaults unless your workflow needs different labels

Content type

application/json  — Compress body (gzip) off

Custom body template

Off

 

 

Figure 4. Payload tab with the DCR destination fields.

 

Review & test tab

 

Figure 5. Review & test tab showing the composed URL and test controls.

 

Step 5 — Test and verify

From Review & test, pick a real event type such as case.created and send a test. Prefer real event types over webhook.test: the synthetic test event carries no business fields and lands as a mostly empty row.

Allow a few minutes. Two timing behaviors are normal on a fresh setup: the first records into a new table take longer to appear, and DCR configuration changes take several minutes to reach the ingestion pipeline. Then query:

NetskopeAISecOps_CL
Figure 6. What success looks like — every delivered event type landing with populated envelope columns and Data payloads.
 

How the data is structured

Each webhook delivery is a JSON array. Each element is an envelope with delivery metadata at the top level and the actual event under data:

[

  {

    "id": "evt_...",

    "timestamp": "2026-07-02T19:30:00Z",

    "tenant_id": "1234",

    "region": "us-west-2",

    "api_version": "2026-08-01",

    "type": "case.created",

    "data": { "case_id": "case_01HXYZ", "severity": "high", "...": "..." }

  }

]

 

The table stores the envelope in five typed columns and the event whole in Data:

Column

Type

Source

TimeGenerated

datetime

envelope timestamp (delivery time)

EventType

string

envelope type

NetskopeTenantId

string

envelope tenant_id

Region

string

envelope region

ApiVersion

string

envelope api_version

Data

dynamic

envelope data — the complete event

 

 

Group

Columns

Cases — core

CaseId, EventTitle, Severity, RuleIds*, Url, User, EntityType, CreatedAt, Status, OldStatus, NewStatus, ChangedBy, Assignee, AssignedBy, InvestigationError, AlertCount, IncidentCount, DataSources*, AssignedTo, GroupId, ViewId, CreationSource, ClosureRecommendation

Cases — investigation

Verdict, Summary, SummaryMarkdown, RiskScore, RiskFactors*, RecommendedActions*, DataConfidence*, UserContext*, DataExposure*, IncidentsSummary*, Entities*, KeyEvidence*, FileLineage*

Risk — identity

UserId, UserName, AppId, AppName, McpServerId, McpServerName

Risk — scoring

Score, Band, PreviousBand, PreviousUci, RiskNote, RiskIndicators*, PrimaryDriver, CategorySubscores*, ScoredAt

 

Columns marked * stay dynamic. Reach inside them with dot notation: FileLineage.files[0].md5, DataConfidence.score, RiskIndicators.policy_alerts. Fields not present on a given event type are null — filter with isnotempty().

 

Troubleshooting

Symptom

Likely cause

Fix

Connection test fails with HTTP 400 from the token endpoint

Scope empty, a value in Audience/resource, or a v1 token URL (/oauth2/token)

Scope https://monitor.azure.com/.default, Audience empty, Token URL ending /oauth2/v2.0/token; save before retesting

AADSTS7000215: Invalid client secret

Secret ID pasted instead of the secret Value, or an expired secret

Use the Value (contains ~ and dots); create a new secret if the Value was lost

Ingestion returns InvalidApiVersion

Full ingestion URL pasted into Destination URL

Destination URL must be the host only; the DCR immutable ID and stream name belong in the Payload tab

Ingestion returns 401 or 403

Wrong credentials, or the Monitoring Metrics Publisher role is missing or not yet propagated

Verify with az role assignment list --assignee <client-id> --scope <dcr-resource-id>; allow a few minutes after assigning

Deployment fails with a not-found error for the workspace

Wrong workspaceName or workspaceResourceGroup parameter

Verify with az monitor log-analytics workspace show --resource-group <rg> --workspace-name <name> and re-run

Rows appear but business columns are null

Deliveries processed while a DCR change was still propagating, or webhook.test events

Wait a few minutes and resend a real event type; filter noise with where EventType != "webhook.test"

Test returned success but no row appears

Ingestion latency, or the time picker excludes the event time

Wait 5–10 minutes; query with TimeGenerated > ago(1h) and widen the picker

InvalidTransformQuery when customizing the DCR

The transform compiler supports a restricted KQL subset

Keep the DCR transform rename-only and do all field extraction in the saved function

 

Maintenance

The client secret expires on the date set at creation (one year in the commands above). Rotate it before expiry: create a new secret with az ad app credential reset --id <client-id> --append --years 1, update the webhook’s Client secret field, verify a test delivery, then remove the old credential in Entra. An expired secret stops deliveries without any error visible on the Azure side.