Anyone who has tried to load an AWS CloudTrail export into Splunk knows the frustration: the files look like plain JSON, yet the data comes out wrong. CloudTrail wraps every event in one large {"Records":[...]} object, so Splunk indexes each file as a single giant event, and searches, field extraction and timestamps all break. The problem grows when the export arrives as hundreds of .tar.gz archives scattered across region, year, month and day folders, with no obvious way to process them in bulk. Add a daily license quota and a few gigabytes of compressed logs that expand to tens of gigabytes once indexed, and a careless one-shot load can trigger license warnings. This solution removes those pain points with a small, repeatable set of scripts. One script extracts the archives and flattens every file into one event per line, in parallel, while keeping your folder structure. A ready-made props.conf makes events land at the right time with their JSON fields extracted. A second script loads the files in batches you can size to your license, and it remembers what it has already ingested, so you can safely stop and resume. The result is a clean, searchable CloudTrail dataset in Splunk without manual file handling or guesswork, and the whole process is documented below so you can reuse it for your own exports.
 

The solution

  1. Flatten: extract the archives and convert every file to newline-delimited JSON (one event per line).
  2. Configure: apply the aws:cloudtrail sourcetype in props.conf so events get the correct timestamp and fields.
  3. Load: ingest the flattened files with Splunk’s one-shot input, in batches that respect your license quota.

 

Prerequisites

  • Linux host with bash, find, xargs, tar, zcat
  • jq (check with command -v jq)
  • Splunk Enterprise with admin access, installed on the same host
  • Free disk space roughly equal to the uncompressed size of your logs
Install jq if missing:
sudo apt-get install -y jq    # Debian/Ubuntu sudo yum install -y jq        # RHEL/CentOS/Amazon Linux
 
 

Usage

1. Prepare Splunk
Create the index (or use any existing, then skip the “add index” command):
$SPLUNK_HOME/bin/splunk add index aws_cloudtrail
Copy splunk/props.conf to $SPLUNK_HOME/etc/system/local/ (or into a custom app’s local/ folder), then restart Splunk:
$SPLUNK_HOME/bin/splunk restart
Make sure the index’s retention (frozenTimePeriodInSecs) is longer than the age of your data, otherwise events may expire right after indexing.
2. Flatten the logs
./scripts/flatten.sh <source_dir> <dest_dir> [parallel_jobs]
Example:
./scripts/flatten.sh /raw /data/cloudtrail_flat 4
What it does:
  • Extracts .tar.gz archives, then flattens all CloudTrail files
  • Removes outputs that contain no events
  • Lists any failed files in <dest_dir>_failed.log and exits with code 2
  • Cleans up its temporary extraction folder when everything succeeds
3. Check the volume against your license
Splunk counts uncompressed bytes indexed per day. Check the flattened size first:
du -sh /data/cloudtrail_flat
If it is larger than your daily quota, load in several runs using the size limit in the next step.
4. Load into Splunk
./scripts/oneshot.sh <ndjson_dir> [max_gb_per_run]
Examples:
./scripts/oneshot.sh /data/cloudtrail_flat        # load everything ./scripts/oneshot.sh /data/cloudtrail_flat 40     # stop after ~40 GB, continue tomorrow
Environment variables (all optional):
Re-running the script is safe: files already listed in the done log are skipped.
5. Verify
Run these searches in Splunk:

index=aws_cloudtrail | stats count by awsRegion

index=aws_cloudtrail | timechart span=1d count

index=aws_cloudtrail | stats min(_time) as first max(_time) as last | convert ctime(first) ctime(last)

Confirm the date range matches your export and that fields such as eventName, userIdentity.arn and sourceIPAddress are extracted.
To monitor license usage while loading:

index=_internal source=*license_usage.log type=Usage earliest=-1d

| stats sum(b) as bytes

| eval GB=round(bytes/1024/1024/1024,2)

 

Troubleshooting

Notes
  • oneshot.sh uses Linux stat -c, so it is not compatible with macOS.
  • If the scripts are not executable after cloning, run chmod +x scripts/*.sh or call them with bash scripts/<name>.sh.
  • Keep your original export as a backup; the scripts never modify the source folder.
  • Optional: the Splunk Add-on for AWS adds CIM field mappings for aws:cloudtrail. Its own inputs read from S3/SQS, so for local exports keep using this workflow.

Author

<a href="https://blackcell.io/tibor-luter/" target="_blank">Tibor Luter</a>

Tibor Luter

SOC DIRECTOR

Tibor Luter is the SOC Director at Black Cell and has been leading the CSIRT team since its formation in 2015. Tibor is also responsible for expanding the SOC’s technical capabilities and services in the Middle East and North Africa (MENA) region.

Related Posts

Share This