MENU
    Microsoft Defender
    • 12 Jun 2025
    • 3 Minutes to read
    • Dark

    Microsoft Defender

    • Dark

    Article summary

    Overview

    LimaCharlie can ingest Microsoft 365 Defender logs via three methods Azure Event Hub Adapter, the Microsoft Defender API, or a Custom Webhook

    Documentation for creating an event hub can be found here here.

    Telemetry Platform: msdefender

    Deployment Configurations

    All adapters support the same client_options, which you should always specify if using the binary adapter or creating a webhook adapter. If you use any of the Adapter helpers in the web app, you will not need to specify these values.

    • client_options.identity.oid: the LimaCharlie Organization ID (OID) this adapter is used with.
    • client_options.identity.installation_key: the LimaCharlie Installation Key this adapter should use to identify with LimaCharlie.
    • client_options.platform: the type of data ingested through this adapter, like text, json, gcp, carbon_black, etc.
    • client_options.sensor_seed_key: an arbitrary name for this adapter which Sensor IDs (SID) are generated from, see below.

    Adapter-specific Options

    • connection_string - The connection string provided in Azure for connecting to the Azure Event Hub, including the EntityPath=... at the end which identifies the Hub Name (this component is sometimes now shown in the connection string provided by Azure).

    Guided Deployment

    In the LimaCharlie web app, you can find a Microsoft Defender helper for connecting to an existing Azure Event Hub and ingesting Microsoft Defender logs.

    CLI Deployment

    The following example configuration ingests Microsoft Defender logs from an Azure Event Hub to LimaCharlie.

    ./lc_adapter azure_event_hub client_options.identity.installation_key=<INSTALLATION_KEY> \
    client_options.identity.oid=<OID> \
    client_options.platform=msdefender \
    client_options.sensor_seed_key=<SENSOR_SEED_KEY> \
    client_options.hostname=msdefender \
    "connection_string=Endpoint=sb://mynamespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=fnaaaaaaaaaaaaaaak0g54alYbbbbbbbbbbbbbbbALQ=;EntityPath=lc-stream"
    Bash

    Infrastructure as Code Deployment

    # Adapter Documentation: https://docs.limacharlie.io/docs/adapter-types
    
    sensor_type: "azure_event_hub"
    defender:
      tenant_id: "YOUR_AZURE_DEFENDER_TENANT_ID" # (required) Your Azure AD Tenant ID where Defender is homed (often same as Entra ID tenant).
      client_id: "YOUR_AZURE_APP_REGISTRATION_CLIENT_ID_FOR_DEFENDER" # (required) The Application (client) ID of an Azure App Registration with permissions for Microsoft Defender (e.g., SecurityEvents.Read.All).
      client_secret: "YOUR_AZURE_APP_REGISTRATION_CLIENT_SECRET_FOR_DEFENDER" # (required) The client secret for the App Registration. Store securely.
      client_options:
        identity:
          oid: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # (required) Organization ID from LimaCharlie.
          installation_key: "YOUR_LC_INSTALLATION_KEY_DEFENDER" # (required) Installation key associated with the OID.
        hostname: "ms-defender-adapter-prod-01.example.com" # (required if not using sensor_hostname_path)
        platform: "saas_api_microsoft_defender" # (required) Indicates the source is the Microsoft Defender API.
        architecture: null # (optional) Not applicable for SaaS API.
        mapping:
          # Defender API returns JSON, so parsing_re is usually null.
          parsing_re: null
          # (optional) Path to uniquely ID the original alert, incident, or machine event. Defender objects usually have an 'id'.
          sensor_key_path: "id"
          # (optional) If client_options.hostname is NOT set, use this to dynamically extract a device hostname from the event.
          sensor_hostname_path: "machineDnsName" # Example: if processing Defender for Endpoint machine-specific alerts/events.
          # (optional) Example: "DEFENDER_ALERT_HIGH_SEVERITY", "DEFENDER_INCIDENT_ACTIVE" based on fields in the data.
          event_type_path: "DEFENDER_{{ .alertType | token | upper | default \"GENERIC\" }}_{{ .severity | token | upper }}" # Assuming 'alertType' and 'severity' fields.
          # (optional) JSON path to the event's occurrence time. Defender uses various timestamp fields like 'lastUpdateTime', 'createdDateTime', 'eventDateTime'.
          event_time_path: "lastUpdateTime" # Or "createdDateTime", "eventDateTime" depending on the specific Defender data source (alerts, incidents, advanced hunting).
          # (optional) JSON path for a field to populate LimaCharlie's investigation_id.
          investigation_id_path: "incidentId" # If pulling incidents, or "relatedAlertIds[0]" if processing alerts that are part of an incident.
          # (optional) Use +/- syntax for transforms.
          transform:
            "+security_product_family": "MicrosoftDefender"
            "+alert_title_from_defender": "{{ .title }}"
            # "+affected_device_count": "{{ .devices | len }}" # Example: if 'devices' is a list and 'len' function is available
            "-detectionSource": null # Example: if this field is redundant with event_type
            "-evidenceDetails": null # Can be very large and complex; consider if needed for general telemetry
          # (optional) A list of field paths to drop.
          drop_fields:
          - "comments" # Often free-form and large
          - "rawDataBundle" # If there's a very large raw data field
          sid_replication_path: null # (optional)
        # mappings: null
        indexing:
          enabled: true
          default_index: "defender-logs-{{ .identity.oid | substr 0 8 }}"
        is_compressed: false # (optional)
        sensor_seed_key: "SEED_KEY_DEFENDER_ADAPTER_001" # (required)
        dest_url: "https://input.limacharlie.io" # (optional) The destination URL. Usually defaults correctly.
    YAML


    Was this article helpful?