Skip to main content

evtxtoelk

Load Windows Event Log (.evtx) files into Elasticsearch, or export them as JSON lines for any other collector.

Build Quality Gate Coverage

Every record becomes one document with the event's TimeCreated as @timestamp, the full Event structure, and EventData collapsed into a searchable {Name: value} object. Corrupt records are skipped and counted instead of aborting the load.

Install

Requires Python 3.10 or newer and Elasticsearch 8 or 9.

pip install evtxtoelk

or, from a checkout:

uv sync

Usage

evtxtoelk FILE [FILE ...] DESTINATION [options]

DESTINATION is an Elasticsearch URL, a path ending in .json, .jsonl or .ndjson to write JSON lines instead of indexing, or - for JSON lines on stdout. A bare host or host:port is accepted and treated as http://host:9200.

Load two logs into the default hostlogs index, creating it with the recommended mapping if it does not exist:

evtxtoelk Security.evtx System.evtx http://localhost:9200 --create-index

Tag every document with case metadata and use a custom index:

evtxtoelk Security.evtx http://localhost:9200 -i case-1234 -m '{"case": "1234", "host": "WKS01"}'

Secured cluster with a self-signed certificate:

evtxtoelk Security.evtx https://es.example.com:9200 -u elastic --insecure

Or with a CA bundle and an API key:

evtxtoelk Security.evtx https://es.example.com:9200 --api-key "$ES_API_KEY" --ca-certs ca.pem

Export to a JSON-lines file instead (for Wazuh, Filebeat, jq, ...):

evtxtoelk Security.evtx security.json

Inspect the documents without touching anything:

evtxtoelk Security.evtx - | head -1 | jq .

Options

Option Default Purpose
-i, --index hostlogs Target index
-s, --bulk-size 500 Documents per bulk request
-m, --meta JSON object stored under meta on every document
-u, --user / -p, --password Basic auth. Password is prompted when omitted
--api-key Elasticsearch API key
--ca-certs CA bundle for TLS verification
-k, --insecure Skip certificate verification
--timeout 60 Request timeout in seconds
--create-index Create the index with the recommended mapping
-o, --output Write JSON lines to a file (- for stdout)
--dry-run Same as --output - or a - destination
-v, --verbose Debug logging

Exit status is 0 when every readable record was indexed and 1 when any bulk item failed or the cluster could not be reached. Skipped (corrupt) records are reported in the summary line but do not change the exit status.

Document layout

{
  "@timestamp": "2016-07-08T18:12:51.681641+00:00",
  "Event": {
    "System": {
      "Provider": {"@Name": "Microsoft-Windows-Security-Auditing"},
      "EventID": {"@Qualifiers": "", "#text": "4624"},
      "TimeCreated": {"@SystemTime": "2016-07-08T18:12:51.681641+00:00"},
      "Channel": "Security",
      "Computer": "WKS01"
    },
    "EventData": {
      "Data": {"SubjectUserName": "alice", "LogonType": "2"}
    }
  },
  "meta": {"case": "1234"}
}

Rules applied on the way in:

  • EventData/Data elements with a Name become keys under EventData.Data. Dots in names are replaced with underscores and leading or trailing dots are dropped, because Elasticsearch rejects .NETServiceMethod style names.
  • Unnamed Data elements and other odd payloads are serialised into a RawData string so a field never changes type between records.
  • --create-index (or scripts/apply_mapping.sh) creates the index with @timestamp and TimeCreated mapped as dates and dynamic date and number detection turned off. Without it Elasticsearch dynamic mapping is used, which also works for the sample corpora but is more exposed to a stray value locking a field to the wrong type.

Python API

from evtxtoelk import EvtxToElk, ensure_index, iter_documents, make_client

es = make_client("https://es.example.com:9200", api_key="...", ca_certs="ca.pem")
ensure_index(es, "hostlogs")
result = EvtxToElk(es, index="hostlogs", metadata={"case": "1234"}).load("Security.evtx")
print(result.indexed, result.failed, result.skipped)

# or just iterate the documents
for doc in iter_documents("Security.evtx"):
    ...

The 1.x call EvtxToElk.evtx_to_elk("Security.evtx", "localhost:9200") still works and returns a LoadResult.

Development

uv sync                          # Python 3.14 environment with dev tools
uv run pytest                    # unit tests, no Elasticsearch needed
docker compose up -d --wait      # single-node Elasticsearch 9.5 on localhost:9200
uv run pytest -m integration     # end-to-end tests against it
docker compose down -v

To exercise the loader against a few hundred real-world logs, clone EVTX-ATTACK-SAMPLES into .cache/ and run the samples marker:

git clone --depth 1 https://github.com/sbousseaden/EVTX-ATTACK-SAMPLES .cache/EVTX-ATTACK-SAMPLES
uv run pytest -m samples

Lint and format with uv run ruff check . and uv run ruff format ..

Releasing

Bump version in pyproject.toml and __version__ in evtxtoelk/__init__.py, note the release in CHANGELOG.md, merge, then publish a GitHub release whose tag is v<version>. The Release workflow rebuilds, checks the tag against the package version, and publishes to PyPI through trusted publishing.

CI runs the unit and integration tests on every push and pull request against an Elasticsearch service container, then uploads coverage to SonarCloud.

History

The original 2018 write-up, EvtxToElk: a Python module to load Windows Event Logs into Elasticsearch, was published on the Dragos blog. Dragos has since removed it; an archived copy is on the Wayback Machine. See CHANGELOG.md for what changed in 2.0.

License

Apache License 2.0. See LICENSE.txt.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

evtxtoelk-2.0.0.tar.gz (24.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

evtxtoelk-2.0.0-py3-none-any.whl (17.0 kB view details)

Uploaded Python 3

File details

Details for the file evtxtoelk-2.0.0.tar.gz.

File metadata

  • Download URL: evtxtoelk-2.0.0.tar.gz
  • Upload date:
  • Size: 24.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for evtxtoelk-2.0.0.tar.gz
Algorithm Hash digest
SHA256 e03e6d904c3ec265caff3c87b33754cc460eaba844671f91eb67ab95cc67d7b9
MD5 31a40c1caaebfb0021f6be896a207527
BLAKE2b-256 e52fce187e77ab79bbbc4d1279ca441bfbad2736238044cb9e40db0644a9838e

See more details on using hashes here.

File details

Details for the file evtxtoelk-2.0.0-py3-none-any.whl.

File metadata

  • Download URL: evtxtoelk-2.0.0-py3-none-any.whl
  • Upload date:
  • Size: 17.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for evtxtoelk-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8c562634b5e90503272800edfcadc2a6679a48c35081474ccc976413dd524ac3
MD5 f74855cbd78f1a87fc54a2a1df33a3a3
BLAKE2b-256 eb3732ae389c1938788626b693c62239b4d4661605d6dd86c3baced3643af54a

See more details on using hashes here.

Release history Release notifications | RSS feed

2.2.0

2 files

2.1.0

2 files

This release

2.0.0 This release

2 files

1.0.2

2 files

1.0.1

2 files

1.0.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page