Elastic Defend Operations - Response Actions CLI for Elastic Endpoint Security XDR
Project description
edops
Elastic Defend Operations - command-line interface for Elastic Endpoint Security / Elastic Defend response actions. Isolate hosts, kill processes, retrieve files, execute commands, run osquery queries, investigate alerts, inspect session views, and run a semi-interactive shell — all from the terminal, directly against the Kibana response actions API.
Built and tested on Elastic Stack 9.3.3, but most of it should work on 8.19.+ also.
Demo
Generate your own: install VHS, set
EDOPS_KIBANA_URLandEDOPS_KIBANA_API_KEY, then runvhs docs/tapes/demo-main.tape. Seedocs/tapes/for tapes covering individual features.
Features
- Response actions — isolate, unisolate, kill/suspend processes, get files, execute commands, upload files, scan, memory dump
- osquery — run live queries, fetch results, list saved queries
- Session View — render the process tree and terminal I/O captured by Elastic for any detection alert
- Fleet agents — list enrolled agents, filter by hostname prefix, IP, or MAC address
- Interactive console — endpoint-scoped console with tab-completion for all commands
- Semi-interactive shell — execute actions wrapped as a remote shell session (stdout/stderr rendered locally)
- Alerts — query Elastic Security detection alerts, globally or per endpoint
- JSON output on every command for scripting and piping
- Comment/ticket traceability — stamp every API call with a ticket reference
- Flexible auth — API key or username/password; config file,
.env, environment variables, or global CLI flags per invocation
Target Audience
SOC analysts, blue teams, and incident responders.
Applicability beyond that
is neither the goal nor the focus.
This CLI is built to interact with Elastic Kibana, Fleet, Elastic Agent and Elastic Endpoint Security (XDR) for endpoint visibility and response actions.
Requirements
- Python 3.10+
- Elastic Stack 9.x (Enterprise license required)
- Elastic Agent enrolled via Fleet with Endpoint Security enabled on target endpoints
- Kibana user with the correct role for response actions
Installation
From PyPI
pip install edops
pipx is recommended for CLI tools — it installs in an
isolated environment and puts edops on your PATH:
pipx install edops
From source
git clone https://github.com/renini/edops.git
cd edops
pip install -e .
# or
pipx install -e .
Quick Start
# 1. Point at your Kibana instance
edops config set --url https://kibana.lab.internal:5601
edops config set --api-key # prompts with hidden input — nothing in shell history
# 2. Find your endpoint
edops agents workstation-01
# 3. Act on it (agent ID from step 2)
edops execute <agent-id> --command "whoami" --comment "INC-1337"
Configuration
Settings are stored in ~/.config/edops/config.json (mode 0600).
edops config set --url https://kibana.lab.internal:5601
edops config set --api-key # prompts with hidden input — nothing stored in shell history
edops config show
| Option | Default | Description | |---|---|---| | --url | — | Kibana URL | |
--api-key | — | API key (base64 id:key); omit value to be prompted | |
--username / --password | — | Basic auth alternative to API key;
--password omit value to be prompted | | --space-id | default | Kibana
space | | --ca-cert | — | Path to CA bundle for self-signed certs | |
--no-verify-ssl | — | Disable TLS verification (never use against production)
| | --require-comment / --no-require-comment | on | Require a ticket
reference on every action | | --persist-history / --no-persist-history | off
| Save console/shell command history to ~/.config/edops/ across sessions |
Credential priority (highest to lowest)
- Global CLI flags (
--url,--api-key,--username,--password) — per-invocation override - Shell environment variables (
EDOPS_KIBANA_*) .envfile in the current working directoryconfig.json- Auto-prompt — if nothing provides a URL or credentials, edops asks interactively
Environment variables
| Variable | Description | |---|---| | EDOPS_KIBANA_URL | Kibana base URL
(e.g. https://kibana.lab.internal:5601) | | EDOPS_KIBANA_API_KEY | API key
(base64 id:key) | | EDOPS_KIBANA_USERNAME | Username for basic auth | |
EDOPS_KIBANA_PASSWORD | Password for basic auth | | EDOPS_KIBANA_VERIFY_SSL
| Set to false to skip TLS verification | | EDOPS_KIBANA_CA_CERT | Path to a
custom CA certificate bundle | | EDOPS_KIBANA_SPACE_ID | Kibana space ID
(default: default) |
.env file
Place a .env in your working directory to set credentials without exporting
shell variables — useful when working against multiple stacks from different
project directories:
# .env (add to .gitignore — never commit this file)
EDOPS_KIBANA_URL=https://kibana.lab.internal:5601
EDOPS_KIBANA_API_KEY=<base64-id:key>
Authentication
API key (recommended) — generate in Kibana → Stack Management → API keys:
edops config set --api-key # hidden prompt, nothing in shell history
edops config set --api-key <base64> # pass directly
Username / password:
edops config set --username elastic
edops config set --password # hidden prompt
Per-invocation override — useful on shared machines or when switching stacks:
edops --url https://kibana.lab.internal:5601 --api-key <base64> processes <agent-id>
edops --api-key processes <agent-id> # prompts for key, uses URL from config
edops --url https://staging:5601 agents workstation-01
TLS
edops config set --ca-cert /path/to/ca.crt # custom CA for self-signed certs
edops config set --no-verify-ssl # disable verification (not for production)
Commands
Endpoint discovery
| Command | Description | |---|---| | edops agents [query] | List enrolled
Fleet agents, filter by hostname prefix, IP, or MAC — omit to list all | |
edops agents --online | Only show agents currently online | |
edops metadata <agent-id> | Show OS, agent version, policy, capabilities,
isolation state |
edops agents # all enrolled agents
edops agents WIN-VM-01 # hostname prefix match
edops agents 10.10.10. # all agents on 10.10.10.x
edops agents de:ad:be # MAC address prefix
edops agents --online # all agents currently online
edops agents WIN-VM-01 --online # hostname prefix, online only
Isolation
| Command | Description | |---|---| | edops isolate <agent-id> | Isolate a
host from the network | | edops unisolate <agent-id> | Release network
isolation |
Both accept --wait to poll until the action completes, --comment for
traceability, and --json for raw output.
edops isolate <agent-id> --comment "INC-1337 ransomware containment" --wait
edops unisolate <agent-id> --comment "INC-1337 containment lifted" --wait
Process management
| Command | Description | |---|---| | edops processes <agent-id> | List
running processes | | edops kill-process <agent-id> --pid <pid> | Terminate a
process by PID | | edops kill-process <agent-id> --entity-id <id> | Terminate
by process entity ID | | edops suspend-process <agent-id> --pid <pid> |
Suspend a process by PID | | edops suspend-process <agent-id> --entity-id <id>
| Suspend by process entity ID |
File operations
| Command | Description | |---|---| | edops execute <agent-id> --command <cmd>
| Execute a shell command and return stdout/stderr | |
edops get-file <agent-id> --path <path> | Retrieve a file from the endpoint |
| edops upload <agent-id> --file <local-path> | Upload a local file to the
endpoint | | edops scan <agent-id> --path <path> | Scan a file or directory
with Elastic Defend | | edops memory-dump <agent-id> | Full kernel memory dump
(Windows only) | | edops memory-dump <agent-id> --pid <pid> | Process memory
dump |
edops execute <agent-id> --command "whoami && hostname" --comment INC-1337
edops execute <agent-id> --command "heavyscript.ps1" --timeout 300 --comment INC-1337
edops get-file <agent-id> --path "C:\Users\analyst\Desktop\suspicious.exe" \
--output-dir ./evidence --comment INC-1337
edops scan <agent-id> --path "/tmp/dropped_file" --comment INC-1337
Security alerts
edops alerts # all alerts from the last 24 hours
edops alerts <agent-id> # alerts for a specific endpoint
edops alerts <agent-id> --status open # filter by status: open, acknowledged, closed
edops alerts <agent-id> --since 7d # time window: 30m, 6h, 7d, etc.
edops alerts --json | jq '.hits.total.value' # total alert count across all endpoints
Session View
Render the process tree and captured terminal I/O for any detection alert — equivalent to Kibana's Session View, but in the terminal:
edops session-view <alert-id>
The <alert-id> is the document _id from the alert, visible via
edops alerts --json:
# Get alert IDs for a specific endpoint
edops alerts <agent-id> --json | jq -r '.hits.hits[]._id'
# Inspect the full attack chain for an alert
edops session-view 55485a1b9d9a0348d3f0328cf6565665b2e8d280a2692e40b8c35ba586750be6
Session View 55485a1b9d9a0… 2026-05-03 14:10:46
Rule: Malware Prevention Alert
Process Tree
root sshd-session (11793)
└── analyst -bash (11820) [I/O]
└── root sudo -s (11822)
└── root su (11830) [I/O]
├── root ps fax (11831)
├── root wget https://secure.eicar.org/eicar.com (12085)
├── root vim malware (12100) [I/O] ◄ alerted
└── root wget https://nothing.malicious.example.org (12099)
Session Export
Export the terminal I/O for an alert's session to an asciicast file for archiving, sharing, or converting to GIF/MP4:
edops session-export <alert-id> # save to tty_<alert-id[:16]>.cast
edops session-export <alert-id> --output session.cast # custom filename
edops session-export <alert-id> --comment INC-1337 # filename becomes tty_INC-1337-id.cast
edops session-export <alert-id> --force # overwrite existing file
edops session-export <alert-id> --gif # also produce a GIF via agg
edops session-export <alert-id> --mp4 # also produce an MP4 via agg + ffmpeg
edops session-export <alert-id> --no-intro # skip the 2-second animated intro
Session Play
Replay a session as live terminal output — accepts an alert ID or a .cast file:
edops session-play <alert-id> # replay the alerted process's I/O
edops session-play <alert-id> --all # include I/O from all session processes
edops session-play session.cast # replay a previously exported .cast file
edops session-play <alert-id> --yes # skip the safety prompt
osquery
# Run a live query and wait for results
edops osquery run <agent-id> --query "SELECT name, pid, path FROM processes LIMIT 10"
# Use a saved query or a pack
edops osquery run <agent-id> --saved-query-id my-query
edops osquery run <agent-id> --pack-id my-pack
# Fire and forget — fetch results later
edops osquery run <agent-id> --query "SELECT * FROM users" --no-wait
edops osquery results <action-id> <query-action-id>
# List all saved queries
edops osquery saved-queries
Action management
edops status <action-id> # status and per-agent output of a response action
edops actions # list recent response actions
edops actions <endpoint-id> # filter by endpoint
edops actions --command execute # filter by command type (isolate, execute, etc.)
edops actions --page 2 --page-size 50 # pagination
Interactive Modes
Console
A full response console scoped to a single endpoint. All commands run against the same agent — no need to repeat the agent ID. Tab-completion is available for commands and their flags.
edops console <agent-id> --comment INC-1337
Elastic Defend >_ Respond console endpoint=<agent-id> host=WIN-VM-01 os=Windows 10 Pro ip=10.10.10.11
[WIN-VM-01]> processes
[WIN-VM-01]> execute --command "net localgroup administrators"
[WIN-VM-01]> alerts --status open
[WIN-VM-01]> osquery --query "SELECT name, path FROM processes WHERE on_disk = 0"
[WIN-VM-01]> isolate --wait
[WIN-VM-01]> comment INC-5678 # change the active ticket reference mid-session
[WIN-VM-01]> exit
Available console commands: isolate, unisolate, processes, kill-process,
suspend-process, get-file, execute, upload, scan, memory-dump,
status, actions, alerts, osquery, osquery-results, osquery-saved,
metadata, shell, comment, help, exit
Shell
Semi-interactive shell that wraps execute response actions. Each command you
type is dispatched as an Elastic response action; stdout/stderr are extracted
from the returned zip and printed locally.
edops shell <agent-id> --comment INC-1337
Edops Shell <agent-id>
Ctrl+D or 'exit' to quit · commands routed via Kibana execute action
[65d97abd]> whoami
nt authority\system
[65d97abd]> net localgroup administrators
Alias name administrators
...
[65d97abd]> exit
Note: Each command is a separate API round-trip (submit → poll → download). Expect 3–10 seconds per command depending on network and endpoint load.
File Retrieval
Files returned by get-file, execute (when output is large), and
memory-dump are saved as password-protected zip archives. The password is
always elastic.
edops get-file <agent-id> --path "C:\Users\analyst\Desktop\suspicious.exe" \
--output-dir ./evidence --comment INC-1337
unzip -P elastic ./evidence/INC-1337_suspicious.exe.zip
The filename is prefixed with the --comment value when provided (e.g.
INC-1337_suspicious.exe.zip), making it easy to organise evidence by case.
Comment and Ticket Traceability
Every response action accepts --comment to attach a ticket or case reference.
This is recorded in the Kibana action log and is visible in the Security app.
edops isolate <agent-id> --comment "INC-1337 - ransomware containment"
Enforce comments across all actions:
edops config set --require-comment
With this set, any action without --comment will be rejected before hitting
the API.
In edops console, change the active comment mid-session without leaving:
[WIN-VM-01]> comment INC-5678
Global Options
These flags are accepted before any subcommand:
edops --version # show version and exit
edops --verbose <command> # print each HTTP request and response
edops --url <url> <command> # override Kibana URL for this invocation
edops --api-key <key> <command> # override API key for this invocation
edops --api-key <command> # prompt for API key with hidden input
edops --username <u> --password <p> <command> # basic auth override
edops --password <command> # prompts for password with hidden input
edops --help # show top-level help
edops <command> --help # show help for a specific command
Security Notes
Elastic Endpoint Security (XDR) operates with high privileges across endpoints. This tool interfaces directly with its control plane.
- Treat access as extremely sensitive
- Audit and monitor usage — every action is stamped in the Kibana action log
- Compromised XDR access == GAME OVER
Credential storage
Credentials are stored in ~/.config/edops/config.json (mode 0600). Prefer
environment variables or per-invocation flags on shared systems. Never commit
credentials to version control.
API key vs. username/password
API keys (Kibana → Stack Management → API keys) are preferred over username/password:
- Scoped to specific privileges
- Revocable without changing the account password
- Auditable individually in the Kibana audit log
TLS
Never use --no-verify-ssl against a production stack. If your Kibana uses a
self-signed or internal CA, use --ca-cert instead:
edops config set --ca-cert /path/to/internal-ca.crt
Shell and console history
Command history in edops shell and edops console is not persisted by
default. Enable it only if you understand the tradeoff:
edops config set --persist-history # saves to ~/.config/edops/{shell,console}_history
Credits and Acknowledgements
- Endgame (now part of Elastic) — for the high-end endpoint security solution, which offers great resistance against cyber threat actors.
- Elastic — for the powerful stack and getting the wider community involved in cybersecurity defense, by being transparent on detection methods and tooling (i.e. YARA rules and other detection rules).
- Anthropic — for their assisted AI coding software; this project was created from idea to code with their top-tier models, credits $$ and Claude Code.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file edops-0.1.2.tar.gz.
File metadata
- Download URL: edops-0.1.2.tar.gz
- Upload date:
- Size: 252.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4dd4a371b105f242fb5d180e904c1fbe52e66adf5ff6168a9cf403149cf650b8
|
|
| MD5 |
d68da8ba1a1b01afd1b066f808c536da
|
|
| BLAKE2b-256 |
bc827f86b60ad88b4c1f65418fa1c17962b5ae54c0d3e42e862806fb910d2eeb
|
Provenance
The following attestation bundles were made for edops-0.1.2.tar.gz:
Publisher:
release.yml on renini/edops
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
edops-0.1.2.tar.gz -
Subject digest:
4dd4a371b105f242fb5d180e904c1fbe52e66adf5ff6168a9cf403149cf650b8 - Sigstore transparency entry: 1443052116
- Sigstore integration time:
-
Permalink:
renini/edops@349453810ded28393b01100db2a878f24f56eef7 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/renini
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@349453810ded28393b01100db2a878f24f56eef7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file edops-0.1.2-py3-none-any.whl.
File metadata
- Download URL: edops-0.1.2-py3-none-any.whl
- Upload date:
- Size: 44.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f31435b6629c05fff670cc9d43096f84e329f7724e935e677fb1c67cb5df3d7e
|
|
| MD5 |
ba29e220c0b86b61ed0029452c284654
|
|
| BLAKE2b-256 |
d9ab86b319b774a37c46f973350d6de6d81cb8ee6c7a88c7bb6a66f1f4865fbe
|
Provenance
The following attestation bundles were made for edops-0.1.2-py3-none-any.whl:
Publisher:
release.yml on renini/edops
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
edops-0.1.2-py3-none-any.whl -
Subject digest:
f31435b6629c05fff670cc9d43096f84e329f7724e935e677fb1c67cb5df3d7e - Sigstore transparency entry: 1443052198
- Sigstore integration time:
-
Permalink:
renini/edops@349453810ded28393b01100db2a878f24f56eef7 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/renini
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@349453810ded28393b01100db2a878f24f56eef7 -
Trigger Event:
push
-
Statement type: