This guide shows you how to map events to Splunk Common Information Model (CIM) fields in TQL. You’ll learn how to choose a CIM data model and dataset, apply dataset tags, populate normalized fields, set Splunk HEC metadata, and preserve source-specific details.
CIM is organized around data models and datasets rather than a single event record hierarchy. For Splunk searches and accelerated data models to find your events, the mapping must produce the right tags and normalized fields for the chosen dataset.
Use the CIM skill
Section titled “Use the CIM skill”Install the tenzir-cim skill when you want an agent to help with CIM schema
decisions. See Use agent skills
for installation and usage examples.
Ask the agent to choose the data model and dataset before it maps fields. The skill can inspect tags, constraints, recommended fields, calculated fields, and lookup-backed values for the selected dataset.
Choose the CIM dataset
Section titled “Choose the CIM dataset”Start from the event semantics, then choose the closest CIM data model and
dataset. For a firewall connection event, choose the Network_Traffic data
model and the All_Traffic dataset.
For Network_Traffic / All_Traffic, start with these fields and tags:
tag: Includenetworkandcommunicate._time: The event timestamp.src,src_ip, andsrc_port: The source endpoint.dest,dest_ip, anddest_port: The destination endpoint.transport: The layer 4 protocol, such astcp,udp, oricmp.action: The normalized traffic action, such asallowed,blocked, orteardown.bytes,bytes_in, andbytes_out: Traffic volume fields.dvcandvendor_product: The reporting product context.
Write a small mapping
Section titled “Write a small mapping”The following example shows a package mapper that maps a parsed firewall
connection event to CIM Network Traffic fields. It keeps all mapping work inside
the event argument, puts the source data under fw, applies the dataset tags,
normalizes action and transport values, maps endpoint fields, and preserves
unmapped residue for review.
---args: named: - name: event description: The field that holds the event to map. type: field default: this---
$event = {...$event, fw: $event, cim: {}}
let $actions = { allow: "allowed", allowed: "allowed", block: "blocked", blocked: "blocked", deny: "blocked", denied: "blocked", drop: "blocked", dropped: "blocked", teardown: "teardown",}
$event.cim._time = move $event.fw.ts$event.cim.tag = ["network", "communicate"]$event.cim.host = $event.fw.device$event.cim.source = "example-firewall"$event.cim.sourcetype = "example:firewall"$event.cim.vendor_product = "Example Networks Example Firewall"$event.cim.action = $actions[$event.fw.action.to_lower()]? else $event.fw.action.to_lower()$event.cim.src = $event.fw.src_ip$event.cim.src_ip = move $event.fw.src_ip$event.cim.src_port = move $event.fw.src_port$event.cim.dest = $event.fw.dst_ip$event.cim.dest_ip = move $event.fw.dst_ip$event.cim.dest_port = move $event.fw.dst_port$event.cim.transport = (move $event.fw.proto).to_lower()$event.cim.bytes_out = move $event.fw.bytes_out$event.cim.bytes_in = move $event.fw.bytes_in$event.cim.bytes = $event.cim.bytes_in + $event.cim.bytes_out$event.cim.dvc = move $event.fw.device
drop $event.fw.action
$event = {...$event.cim, unmapped: $event.fw}A call with a firewall event such as {ts: 2024-01-15T10:30:45Z, action: "allowed", src_ip: "10.0.0.5", src_port: 51544, dst_ip: "203.0.113.10", dst_port: 443, proto: "tcp", bytes_out: 1280, bytes_in: 8192, device: "edge-fw-01"} produces a CIM event like this:
{ _time: 2024-01-15T10:30:45Z, tag: [ "network", "communicate", ], host: "edge-fw-01", source: "example-firewall", sourcetype: "example:firewall", vendor_product: "Example Networks Example Firewall", action: "allowed", src: "10.0.0.5", src_ip: "10.0.0.5", src_port: 51544, dest: "203.0.113.10", dest_ip: "203.0.113.10", dest_port: 443, transport: "tcp", bytes_out: 1280, bytes_in: 8192, bytes: 9472, dvc: "edge-fw-01", unmapped: {},}Apply the mapping pattern
Section titled “Apply the mapping pattern”Use the same structure for larger mappings:
- Use the event scope: Package mappers accept a named
eventfield argument withdefault: this, create source and target namespaces with an initial spread, and mutate only fields below$event. - Keep a source namespace: Keep the parsed event under a short namespace
such as
fw,dns,edr, oreventbefore you create CIM fields. - Choose the dataset first: Let the CIM data model, dataset tags, and constraints drive the mapping.
- Apply dataset tags: Include the tags from the selected dataset and its parent chain so Splunk data model searches can find the event.
- Populate recommended fields: Map recommended fields such as
src,dest,action,transport,dvc, andvendor_productwhen the source provides them. - Use lookup-backed values: Normalize values such as
actionandtransportto the values documented by CIM lookups. - Preserve unmapped fields: Keep source fields that don’t have a deliberate
CIM target in
unmappedso reviewers can audit the mapping.
Send CIM events to Splunk
Section titled “Send CIM events to Splunk”When you send CIM-shaped events to Splunk HEC, pass Splunk metadata through the
dedicated to_splunk options:
my_source::cim::mapto_splunk "https://splunk.example.com:8088", hec_token=secret("splunk-hec-token"), time=_time, host=host, source=source, sourcetype=sourcetypeIf the parsed source event lives in a nested field, pass that field explicitly and promote the mapped CIM event before the sink reads top-level metadata:
my_source::cim::map event=parsedthis = parsedto_splunk "https://splunk.example.com:8088", hec_token=secret("splunk-hec-token"), time=_time, host=host, source=source, sourcetype=sourcetypeUse the index option when the destination index differs per event.