Forwarding to Sentinel

Microsoft Sentinel (formerly Azure Sentinel) is a security information and event management (SIEM) and security orchestration, automation, and response (SOAR) solution built on Azure services. See Microsoft’s Sentinel documentation for more details.

Prerequisites

Event forwards originate from region-specific IPs. For the full list of outbound IPs by region, see SaaS Regions and IP Ranges. Update your firewall and allow inbound requests from these IP addresses to enable Sysdig to handle event forwarding.

To successfully integrate Sentinel with Sysdig’s event forwarding, you must have access to the Log Analytics Workspace that Sentinel uses. You need to configure the Logs Ingestion API in Azure Monitor so Sysdig can send data to Microsoft Sentinel. You can configure it through the Azure portal or CLI by following the steps below, or use a Terraform script for a more automated setup:

  1. Create a Data Collection Endpoint to expose an HTTPS endpoint that Sysdig can send data to. Take note of the Logs Ingestion URI.

  2. Create a custom DCR-based Log Analytics table to collect data received from Sysdig. Schema:

    • TimeGenerated (datetime)
    • EventType (string)
    • EventURL (string)
    • RawEvent (dynamic)
  3. Create a Data Collection Rule to define the DCE-to-Analytics pipeline:

    • Select the DCE created in step 1.
    • Define the stream with the same schema as the table in step 2 (used for both input and output). Take note of the name you give it.
    • Set the Log Analytics table as the destination.
    • Define a simple data flow that links the DCE, the destination table, and the input and output streams. Once created, take note of the Immutable ID.
  4. Create an App Registration in Microsoft Entra, and note its Application ID.

  5. For the App Registration, create Client secret credentials, and note the Secret value.

  6. Allow the App Registration to send data to the Logs Ingestion endpoint. Open the DCR you created at step 3, go to Role Assignments and assign the “Monitoring Metrics Publisher” role to the App Registration created in step 4.

  1. Before running the Terraform script, run:
    az login
    export ARM_SUBSCRIPTION_ID=<your-subscription-id>
    export ARM_TENANT_ID=<your-tenant-id>
    
  2. Save the following script to a .tf file.
    terraform {
      required_version = ">= 1.5.0"
    
      required_providers {
        azurerm = {
          source  = "hashicorp/azurerm"
          version = "~> 3.100"
        }
        azuread = {
          source  = "hashicorp/azuread"
          version = "~> 2.50"
        }
        azapi = {
          source  = "azure/azapi"
          version = "~> 1.13"
        }
      }
    }
    
    provider "azurerm" {
      features {}
    }
    
    provider "azuread" {}
    
    provider "azapi" {}
    
    # ---------------------------------------------------------------------------
    # Variables - Global
    # ---------------------------------------------------------------------------
    
    variable "location" {
      type        = string
      default     = "eastus"
      description = "Azure region for all resources."
    }
    
    variable "create_resource_group" {
      type        = bool
      default     = true
      description = "Set to false to reference an existing resource group instead of creating one."
    }
    
    variable "create_workspace" {
      type        = bool
      default     = true
      description = "Set to false to reference an existing Log Analytics workspace (with Sentinel already onboarded) instead of creating one."
    }
    
    variable "workspace_name" {
      type        = string
      default     = "sysdig-sentinel-workspace"
      description = "Name of the Log Analytics workspace. Created when create_workspace = true; referenced when false."
    }
    
    variable "resource_group_name" {
      type        = string
      default     = "sysdig_forwarding"
      description = "Name of the resource group. Created when create_resource_group = true; referenced when false."
    }
    
    # ---------------------------------------------------------------------------
    # Variables - Workspace to create
    # ---------------------------------------------------------------------------
    
    variable "workspace_sku" {
      type    = string
      default = "PerGB2018"
    }
    
    variable "workspace_retention_days" {
      type    = number
      default = 30
    }
    
    variable "daily_quota_gb" {
      type        = number
      default     = -1
      description = "Daily ingestion quota in GB. -1 means unlimited."
    }
    
    # ---------------------------------------------------------------------------
    # Variables - Optional
    # ---------------------------------------------------------------------------
    
    variable "app_display_name" {
      type        = string
      default     = "Sysdig Log Forwarder"
      description = "Display name for the Entra app registration created in the customer's tenant."
    }
    
    variable "secret_expiry_days" {
      type        = number
      default     = null
      description = "Lifetime of the client secret in days. Leave unset (null) for a non-expiring secret."
    }
    
    variable "dce_name" {
      type    = string
      default = "sysdig-forwarding-dce"
    }
    
    variable "dcr_name" {
      type    = string
      default = "sysdig-forwarding-dcr"
    }
    
    variable "custom_table_name" {
      type        = string
      default     = "SysdigEvents_CL"
      description = "Name of the custom DCR-based table. Must end in _CL."
    
      validation {
        condition     = endswith(var.custom_table_name, "_CL")
        error_message = "Custom table names must end with '_CL'."
      }
    }
    
    variable "tags" {
      type    = map(string)
      default = {}
    }
    
    # ---------------------------------------------------------------------------
    # Data sources
    # ---------------------------------------------------------------------------
    
    data "azurerm_client_config" "current" {}
    
    data "azurerm_resource_group" "rg" {
      count = var.create_resource_group ? 0 : 1
      name  = var.resource_group_name
    }
    
    data "azurerm_log_analytics_workspace" "la" {
      count               = var.create_workspace ? 0 : 1
      name                = var.workspace_name
      resource_group_name = local.rg_name
    }
    
    # ---------------------------------------------------------------------------
    # Locals
    # ---------------------------------------------------------------------------
    
    locals {
      rg_name     = var.create_resource_group ? azurerm_resource_group.rg[0].name : data.azurerm_resource_group.rg[0].name
      rg_location = var.create_resource_group ? azurerm_resource_group.rg[0].location : data.azurerm_resource_group.rg[0].location
    
      workspace_id = var.create_workspace ? azurerm_log_analytics_workspace.la[0].id : data.azurerm_log_analytics_workspace.la[0].id
    
      # Stream name used both in the DCR and as an output for the Go client.
      stream_name = "Custom-${var.custom_table_name}"
    }
    
    # ---------------------------------------------------------------------------
    # Resource group
    # ---------------------------------------------------------------------------
    
    resource "azurerm_resource_group" "rg" {
      count    = var.create_resource_group ? 1 : 0
      name     = var.resource_group_name
      location = var.location
      tags     = var.tags
    }
    
    # ---------------------------------------------------------------------------
    # Log Analytics Workspace
    # ---------------------------------------------------------------------------
    
    resource "azurerm_log_analytics_workspace" "la" {
      count               = var.create_workspace ? 1 : 0
      name                = var.workspace_name
      location            = local.rg_location
      resource_group_name = local.rg_name
      sku                 = var.workspace_sku
      retention_in_days   = var.workspace_retention_days
      daily_quota_gb      = var.daily_quota_gb
      tags                = var.tags
    }
    
    # ---------------------------------------------------------------------------
    # Microsoft Sentinel onboarding
    # ---------------------------------------------------------------------------
    
    resource "azurerm_sentinel_log_analytics_workspace_onboarding" "sentinel" {
      count        = var.create_workspace ? 1 : 0
      workspace_id = azurerm_log_analytics_workspace.la[0].id
    }
    
    # ---------------------------------------------------------------------------
    # Data Collection Endpoint
    # ---------------------------------------------------------------------------
    
    resource "azurerm_monitor_data_collection_endpoint" "dce" {
      name                          = var.dce_name
      location                      = local.rg_location
      resource_group_name           = local.rg_name
      kind                          = "Linux"
      public_network_access_enabled = true
      tags                          = var.tags
    }
    
    # ---------------------------------------------------------------------------
    # Custom DCR-based table  (azapi — azurerm cannot create new _CL tables)
    # ---------------------------------------------------------------------------
    
    resource "azapi_resource" "custom_table" {
      type      = "Microsoft.OperationalInsights/workspaces/tables@2022-10-01"
      name      = var.custom_table_name
      parent_id = local.workspace_id
    
      body = jsonencode({
        properties = {
          plan = "Analytics"
          schema = {
            name = var.custom_table_name
            columns = [
              { name = "TimeGenerated", type = "datetime" },
              { name = "EventType", type = "string" },
              { name = "EventURL", type = "string" },
              { name = "RawEvent", type = "dynamic" }
            ]
          }
        }
      })
    
      depends_on = [azurerm_sentinel_log_analytics_workspace_onboarding.sentinel]
    }
    
    # ---------------------------------------------------------------------------
    # Data Collection Rule
    # ---------------------------------------------------------------------------
    
    resource "azurerm_monitor_data_collection_rule" "dcr" {
      name                        = var.dcr_name
      location                    = local.rg_location
      resource_group_name         = local.rg_name
      data_collection_endpoint_id = azurerm_monitor_data_collection_endpoint.dce.id
      tags                        = var.tags
    
      # Stream columns mirror the table schema exactly — passthrough transform, no KQL manipulation.
      # The Go client must send JSON with these field names.
      stream_declaration {
        stream_name = local.stream_name
    
        column {
          name = "TimeGenerated"
          type = "datetime"
        }
        column {
          name = "EventType"
          type = "string"
        }
        column {
          name = "EventURL"
          type = "string"
        }
        column {
          name = "RawEvent"
          type = "dynamic"
        }
      }
    
      destinations {
        log_analytics {
          workspace_resource_id = local.workspace_id
          name                  = "la-destination"
        }
      }
    
      data_flow {
        streams       = [local.stream_name]
        destinations  = ["la-destination"]
        output_stream = local.stream_name
        transform_kql = "source"
      }
    
      depends_on = [azapi_resource.custom_table]
    }
    
    # ---------------------------------------------------------------------------
    # App Registration, Service Principal, and client secret
    # ---------------------------------------------------------------------------
    
    resource "azuread_application" "sysdig_forwarder" {
      display_name = var.app_display_name
      notes        = "Owned by the customer; grants Sysdig SaaS write access to the Sentinel log ingestion pipeline."
    }
    
    resource "azuread_service_principal" "sysdig_forwarder" {
      client_id = azuread_application.sysdig_forwarder.client_id
      notes     = "Service Principal for the Sysdig log forwarder (Sentinel Logs Ingestion API writer)"
    }
    
    resource "azuread_application_password" "sysdig_forwarder" {
      application_id    = azuread_application.sysdig_forwarder.id
      display_name      = "sysdig-forwarder-secret"
      end_date_relative = var.secret_expiry_days != null ? "${var.secret_expiry_days * 24}h" : null
    }
    
    # Least-privilege: scoped to this DCR only — not the workspace or subscription.
    resource "azurerm_role_assignment" "dcr_publisher" {
      scope                = azurerm_monitor_data_collection_rule.dcr.id
      role_definition_name = "Monitoring Metrics Publisher"
      principal_id         = azuread_service_principal.sysdig_forwarder.object_id
    }
    
    # ---------------------------------------------------------------------------
    # Outputs — everything the Sysdig SaaS connector config needs
    # ---------------------------------------------------------------------------
    
    output "tenant_id" {
      value       = data.azurerm_client_config.current.tenant_id
      description = "Azure AD tenant ID."
    }
    
    output "subscription_id" {
      value       = data.azurerm_client_config.current.subscription_id
      description = "Azure subscription ID."
    }
    
    output "data_collection_endpoint_logs_ingestion_uri" {
      value       = azurerm_monitor_data_collection_endpoint.dce.logs_ingestion_endpoint
      description = "Base URI for the Logs Ingestion API (DCE endpoint)."
    }
    
    output "data_collection_rule_immutable_id" {
      value       = azurerm_monitor_data_collection_rule.dcr.immutable_id
      description = "Immutable ID of the DCR — required in the ingestion request path."
    }
    
    output "stream_name" {
      value       = local.stream_name
      description = "DCR stream name — required in the ingestion request path."
    }
    
    output "service_principal_object_id" {
      value       = azuread_service_principal.sysdig_forwarder.object_id
      description = "Object ID of the Sysdig forwarder service principal in this tenant."
    }
    
    output "service_principal_application_id" {
      value       = azuread_application.sysdig_forwarder.client_id
      description = "Client (application) ID of the Sysdig forwarder Entra app."
    }
    
    output "service_principal_client_secret" {
      value       = azuread_application_password.sysdig_forwarder.value
      description = "Client secret for the Sysdig forwarder app. Store this securely — it is shown only once."
      sensitive   = true
    }
    
    output "log_analytics_workspace_id" {
      value       = local.workspace_id
      description = "Full resource ID of the Log Analytics workspace."
    }
    
  3. In the script, set the Variables - Global values, or pass them as arguments (-var variable=value). To create the workspace, set the Variables - Workspace to create values as well. The Variables - Optional values let you tweak the setup further; their defaults fit most cases.
  4. Set up and apply the Terraform script:
    terraform init
    terraform plan
    terraform apply
    
  5. Collect the outputs; you need them to configure the integration later.
  6. Collect the client secret (service_principal_client_secret). Treat it as a sensitive value.
    terraform output -raw service_principal_client_secret
    

Configure Standard Integration

  1. Log in to Sysdig Secure as Admin and go to Integrations > Add Integrations.

  2. Search and choose Microsoft Sentinel.

  3. Configure the required options:

    • Integration Name: Define an integration name.

    • Tenant ID: Enter your Azure tenant ID (the Directory ID, available in the Azure portal) that hosts the Sentinel Log Analytics Workspace.

    • Data Collection Endpoint URI: Enter the Logs Ingestion URI for the DCE you configured in step 1.

    • Data Collection Rule ID: Enter the Immutable ID for the DCR you configured in step 3.

    • Stream name: Enter the stream name you configured for the DCR in step 3.

    • Service Principal App ID: Enter the Application ID for the App Registration you created in step 4.

    • Service Principal App Secret: Enter the secret you created in step 5.

    • Data to Send: Select from the drop-down the types of Sysdig data that should be forwarded to Sentinel. The available list depends on the Sysdig features and products you have enabled.

    • Toggle the enable switch as necessary. Remember that you need to select Test Integration before you enable the integration.

  4. Select Save.

Configure Agent Local Forwarding

Review the configuration steps and use the following parameters for this integration.

TypeAttributeRequired?TypeAllowed valuesDefaultDescription
SENTINELtenantIdyesstringAzure Tenant ID hosting the Sentinel instance
SENTINELclientIdyesstringApp Registration ID
SENTINELclientSecretyesstringApp Registration Client Secret
SENTINELdceUriyesstringDCE Logs Ingestion URI
SENTINELdceImmutableIdyesstringDCE Immutable ID
SENTINELstreamNameyesstringStream name in the DCR