splunkctl 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- splunkctl/__init__.py +3 -0
- splunkctl/__main__.py +5 -0
- splunkctl/client.py +103 -0
- splunkctl/commands/__init__.py +0 -0
- splunkctl/commands/alerts.py +94 -0
- splunkctl/commands/apps.py +189 -0
- splunkctl/commands/commands_meta.py +72 -0
- splunkctl/commands/config_cmd.py +87 -0
- splunkctl/commands/dashboards.py +216 -0
- splunkctl/commands/indexes.py +199 -0
- splunkctl/commands/info.py +29 -0
- splunkctl/commands/inputs.py +204 -0
- splunkctl/commands/lookups.py +217 -0
- splunkctl/commands/parsers.py +177 -0
- splunkctl/commands/rules.py +266 -0
- splunkctl/commands/search.py +270 -0
- splunkctl/commands/skill_cmd.py +43 -0
- splunkctl/commands/users.py +211 -0
- splunkctl/config.py +82 -0
- splunkctl/guard.py +22 -0
- splunkctl/main.py +149 -0
- splunkctl/output.py +75 -0
- splunkctl/skill/SKILL.md +325 -0
- splunkctl-0.1.0.dist-info/METADATA +118 -0
- splunkctl-0.1.0.dist-info/RECORD +29 -0
- splunkctl-0.1.0.dist-info/WHEEL +5 -0
- splunkctl-0.1.0.dist-info/entry_points.txt +2 -0
- splunkctl-0.1.0.dist-info/licenses/LICENSE +190 -0
- splunkctl-0.1.0.dist-info/top_level.txt +1 -0
splunkctl/main.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""Click entry point and global flags."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
from splunkctl import __version__, output
|
|
9
|
+
from splunkctl.commands.alerts import alerts_group
|
|
10
|
+
from splunkctl.commands.apps import apps_group
|
|
11
|
+
from splunkctl.commands.commands_meta import commands_meta
|
|
12
|
+
from splunkctl.commands.config_cmd import config_group
|
|
13
|
+
from splunkctl.commands.dashboards import dashboards_group
|
|
14
|
+
from splunkctl.commands.indexes import indexes_group
|
|
15
|
+
from splunkctl.commands.info import info
|
|
16
|
+
from splunkctl.commands.inputs import inputs_group
|
|
17
|
+
from splunkctl.commands.lookups import lookups_group
|
|
18
|
+
from splunkctl.commands.parsers import parsers_group
|
|
19
|
+
from splunkctl.commands.rules import rules_group
|
|
20
|
+
from splunkctl.commands.search import search_group
|
|
21
|
+
from splunkctl.commands.skill_cmd import skill_group
|
|
22
|
+
from splunkctl.commands.users import users_group
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class _CLI(click.Group):
|
|
26
|
+
"""Top-level group with flag hoisting and SDK error handling.
|
|
27
|
+
|
|
28
|
+
Rewrites args so global flags (--yes, --json, --format, --fields)
|
|
29
|
+
work in any position, not just before the subcommand.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
_HOIST_FLAGS: frozenset[str] = frozenset(
|
|
33
|
+
{
|
|
34
|
+
"--yes",
|
|
35
|
+
"-y",
|
|
36
|
+
"--json",
|
|
37
|
+
"--debug",
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
_HOIST_VALUE: frozenset[str] = frozenset(
|
|
41
|
+
{
|
|
42
|
+
"--format",
|
|
43
|
+
"--fields",
|
|
44
|
+
"--timeout",
|
|
45
|
+
"--config",
|
|
46
|
+
"-c",
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]:
|
|
51
|
+
"""Move known global flags to the front of the arg list."""
|
|
52
|
+
prefix: list[str] = []
|
|
53
|
+
rest: list[str] = []
|
|
54
|
+
i = 0
|
|
55
|
+
while i < len(args):
|
|
56
|
+
if args[i] in self._HOIST_FLAGS:
|
|
57
|
+
prefix.append(args[i])
|
|
58
|
+
elif args[i] in self._HOIST_VALUE and i + 1 < len(args):
|
|
59
|
+
prefix.append(args[i])
|
|
60
|
+
prefix.append(args[i + 1])
|
|
61
|
+
i += 1
|
|
62
|
+
else:
|
|
63
|
+
rest.append(args[i])
|
|
64
|
+
i += 1
|
|
65
|
+
return super().parse_args(ctx, prefix + rest)
|
|
66
|
+
|
|
67
|
+
def invoke(self, ctx: click.Context) -> Any:
|
|
68
|
+
try:
|
|
69
|
+
return super().invoke(ctx)
|
|
70
|
+
except Exception as exc:
|
|
71
|
+
name = type(exc).__name__
|
|
72
|
+
if name == "HTTPError":
|
|
73
|
+
msg = str(exc)
|
|
74
|
+
if "403" in msg:
|
|
75
|
+
output.error(f"Permission denied: {msg}")
|
|
76
|
+
elif "401" in msg:
|
|
77
|
+
output.error(f"Authentication failed: {msg}")
|
|
78
|
+
elif "404" in msg:
|
|
79
|
+
output.error(f"Not found: {msg}")
|
|
80
|
+
else:
|
|
81
|
+
output.error(msg)
|
|
82
|
+
sys.exit(1)
|
|
83
|
+
if name == "AuthenticationError":
|
|
84
|
+
output.error(f"Authentication failed: {exc}")
|
|
85
|
+
sys.exit(1)
|
|
86
|
+
raise
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@click.group(cls=_CLI)
|
|
90
|
+
@click.version_option(version=__version__, prog_name="splunkctl")
|
|
91
|
+
@click.option("--json", "use_json", is_flag=True, help="Force JSON output.")
|
|
92
|
+
@click.option(
|
|
93
|
+
"--format",
|
|
94
|
+
"fmt",
|
|
95
|
+
type=click.Choice(["table", "json", "csv", "jsonl"]),
|
|
96
|
+
default=None,
|
|
97
|
+
help="Output format.",
|
|
98
|
+
)
|
|
99
|
+
@click.option("--fields", default=None, help="Comma-separated fields to project.")
|
|
100
|
+
@click.option("--out", "-o", type=click.Path(), default=None, help="Write to file.")
|
|
101
|
+
@click.option("--yes", "-y", is_flag=True, help="Confirm mutation, skip dry-run.")
|
|
102
|
+
@click.option(
|
|
103
|
+
"--config",
|
|
104
|
+
"-c",
|
|
105
|
+
type=click.Path(),
|
|
106
|
+
default=None,
|
|
107
|
+
help="Config file path.",
|
|
108
|
+
)
|
|
109
|
+
@click.option("--debug", is_flag=True, help="HTTP request/response logging.")
|
|
110
|
+
@click.option("--timeout", type=int, default=30, help="Request timeout in seconds.")
|
|
111
|
+
@click.pass_context
|
|
112
|
+
def cli(
|
|
113
|
+
ctx: click.Context,
|
|
114
|
+
*,
|
|
115
|
+
use_json: bool,
|
|
116
|
+
fmt: str | None,
|
|
117
|
+
fields: str | None,
|
|
118
|
+
out: str | None,
|
|
119
|
+
yes: bool,
|
|
120
|
+
config: str | None,
|
|
121
|
+
debug: bool,
|
|
122
|
+
timeout: int,
|
|
123
|
+
) -> None:
|
|
124
|
+
"""CLI tool for Splunk Enterprise SIEM operations."""
|
|
125
|
+
ctx.ensure_object(dict)
|
|
126
|
+
ctx.obj["json"] = use_json
|
|
127
|
+
ctx.obj["format"] = fmt
|
|
128
|
+
ctx.obj["fields"] = fields
|
|
129
|
+
ctx.obj["out"] = out
|
|
130
|
+
ctx.obj["dry_run"] = not yes
|
|
131
|
+
ctx.obj["config"] = config
|
|
132
|
+
ctx.obj["debug"] = debug
|
|
133
|
+
ctx.obj["timeout"] = timeout
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
cli.add_command(alerts_group)
|
|
137
|
+
cli.add_command(apps_group)
|
|
138
|
+
cli.add_command(commands_meta)
|
|
139
|
+
cli.add_command(config_group)
|
|
140
|
+
cli.add_command(dashboards_group)
|
|
141
|
+
cli.add_command(indexes_group)
|
|
142
|
+
cli.add_command(info)
|
|
143
|
+
cli.add_command(inputs_group)
|
|
144
|
+
cli.add_command(lookups_group)
|
|
145
|
+
cli.add_command(parsers_group)
|
|
146
|
+
cli.add_command(rules_group)
|
|
147
|
+
cli.add_command(search_group)
|
|
148
|
+
cli.add_command(skill_group)
|
|
149
|
+
cli.add_command(users_group)
|
splunkctl/output.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Dual output — TTY gets tables, pipes get JSON."""
|
|
2
|
+
|
|
3
|
+
import csv
|
|
4
|
+
import io
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
from tabulate import tabulate
|
|
12
|
+
|
|
13
|
+
type Rows = list[dict[str, Any]]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def render(
|
|
17
|
+
ctx: click.Context,
|
|
18
|
+
data: Rows | dict[str, Any],
|
|
19
|
+
) -> None:
|
|
20
|
+
"""Format and output data according to global flags.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
ctx: Click context carrying format flags.
|
|
24
|
+
data: Single dict or list of dicts to render.
|
|
25
|
+
"""
|
|
26
|
+
rows: Rows = [data] if isinstance(data, dict) else list(data)
|
|
27
|
+
obj: dict[str, Any] = ctx.obj or {}
|
|
28
|
+
|
|
29
|
+
fields: str | None = obj.get("fields")
|
|
30
|
+
if fields:
|
|
31
|
+
keep = [f.strip() for f in fields.split(",")]
|
|
32
|
+
rows = [{k: row.get(k) for k in keep} for row in rows]
|
|
33
|
+
|
|
34
|
+
fmt: str | None = obj.get("format")
|
|
35
|
+
use_json: bool = obj.get("json", False)
|
|
36
|
+
|
|
37
|
+
if use_json or fmt == "json":
|
|
38
|
+
text = json.dumps(rows, indent=2, default=str)
|
|
39
|
+
elif fmt == "jsonl":
|
|
40
|
+
text = "\n".join(json.dumps(r, default=str) for r in rows)
|
|
41
|
+
elif fmt == "csv":
|
|
42
|
+
text = _csv(rows)
|
|
43
|
+
elif fmt == "table" or sys.stdout.isatty():
|
|
44
|
+
text = (
|
|
45
|
+
tabulate(rows, headers="keys", tablefmt="simple") if rows else "No results."
|
|
46
|
+
)
|
|
47
|
+
else:
|
|
48
|
+
text = json.dumps(rows, indent=2, default=str)
|
|
49
|
+
|
|
50
|
+
out_path: str | None = obj.get("out")
|
|
51
|
+
if out_path:
|
|
52
|
+
Path(out_path).write_text(text + "\n")
|
|
53
|
+
click.echo(f"Written to {out_path}", err=True)
|
|
54
|
+
else:
|
|
55
|
+
click.echo(text)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def error(msg: str) -> None:
|
|
59
|
+
"""Print error to stderr."""
|
|
60
|
+
click.echo(f"Error: {msg}", err=True)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def info(msg: str) -> None:
|
|
64
|
+
"""Print info to stderr (keeps stdout clean for piping)."""
|
|
65
|
+
click.echo(msg, err=True)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _csv(rows: Rows) -> str:
|
|
69
|
+
if not rows:
|
|
70
|
+
return ""
|
|
71
|
+
buf = io.StringIO()
|
|
72
|
+
writer = csv.DictWriter(buf, fieldnames=list(rows[0].keys()), extrasaction="ignore")
|
|
73
|
+
writer.writeheader()
|
|
74
|
+
writer.writerows(rows)
|
|
75
|
+
return buf.getvalue().rstrip("\n")
|
splunkctl/skill/SKILL.md
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# splunkctl — Agent Skill Guide
|
|
2
|
+
|
|
3
|
+
You are operating a Splunk Enterprise instance via `splunkctl`. This guide
|
|
4
|
+
tells you how to authenticate, run commands, and handle common workflows.
|
|
5
|
+
|
|
6
|
+
## Auth
|
|
7
|
+
|
|
8
|
+
Set up credentials once — all commands inherit them automatically.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
# Interactive (human use):
|
|
12
|
+
splunkctl config init
|
|
13
|
+
|
|
14
|
+
# Non-interactive (agent use):
|
|
15
|
+
splunkctl config init --host localhost --port 8089 \
|
|
16
|
+
--username admin --password changeme --scheme https --no-verify
|
|
17
|
+
|
|
18
|
+
# Verify:
|
|
19
|
+
splunkctl config test
|
|
20
|
+
splunkctl config show # secrets redacted
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Credentials resolve in order: CLI flags > env vars > config file
|
|
24
|
+
(`~/.splunkctl/config.yaml`). Env vars: `SPLUNK_HOST`, `SPLUNK_PORT`,
|
|
25
|
+
`SPLUNK_USER`, `SPLUNK_PASS`, `SPLUNK_TOKEN`, `SPLUNK_SCHEME`.
|
|
26
|
+
|
|
27
|
+
Token auth: set `SPLUNK_TOKEN` for service-account access without a password.
|
|
28
|
+
|
|
29
|
+
## Global flags
|
|
30
|
+
|
|
31
|
+
| Flag | Purpose |
|
|
32
|
+
|---|---|
|
|
33
|
+
| `--json` | Force JSON output |
|
|
34
|
+
| `--format table\|json\|csv\|jsonl` | Output format |
|
|
35
|
+
| `--fields f1,f2` | Project specific fields |
|
|
36
|
+
| `-o, --out file` | Write output to file |
|
|
37
|
+
| `-y, --yes` | Apply mutations (skip dry-run) |
|
|
38
|
+
| `--timeout N` | Request timeout in seconds (default 30) |
|
|
39
|
+
| `-c, --config path` | Config file path override |
|
|
40
|
+
| `--debug` | HTTP request/response logging |
|
|
41
|
+
|
|
42
|
+
**Dry-run by default.** Every mutation previews what would change. Only
|
|
43
|
+
`--yes` applies it.
|
|
44
|
+
|
|
45
|
+
## Output behavior
|
|
46
|
+
|
|
47
|
+
- **stdout**: data payload only (table, JSON, CSV, JSONL)
|
|
48
|
+
- **stderr**: info messages, errors, dry-run previews
|
|
49
|
+
- **JSON format**: always a JSON array of objects, even for single items
|
|
50
|
+
- **Exit codes**: 0 = success, 1 = error. Dry-run exits 0.
|
|
51
|
+
|
|
52
|
+
## Commands
|
|
53
|
+
|
|
54
|
+
### Search
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
splunkctl search run 'index=main error | head 10'
|
|
58
|
+
splunkctl search run 'index=main' --earliest -24h --latest now --limit 50
|
|
59
|
+
splunkctl search run 'index=main' --earliest -7d --app search
|
|
60
|
+
splunkctl search export 'index=main | stats count by sourcetype'
|
|
61
|
+
splunkctl search oneshot '| makeresults count=5 | eval x=random()'
|
|
62
|
+
splunkctl search jobs # list recent jobs
|
|
63
|
+
splunkctl search job <sid> # get job status/results
|
|
64
|
+
splunkctl search cancel <sid> --yes # cancel a running job
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
SPL is auto-normalized: bare keywords get `search` prepended; pipe-leading
|
|
68
|
+
and generating commands (`makeresults`, `inputlookup`, `tstats`, etc.) are
|
|
69
|
+
passed through unchanged. Use `--app` to scope searches to a specific app.
|
|
70
|
+
|
|
71
|
+
### Rules (saved searches)
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
splunkctl rules list
|
|
75
|
+
splunkctl rules get 'My Rule'
|
|
76
|
+
splunkctl rules create --name 'New Rule' --search 'index=main error' \
|
|
77
|
+
--cron '*/5 * * * *' --actions email --description 'Alert on errors' --yes
|
|
78
|
+
splunkctl rules update 'My Rule' --search 'index=main fail' --yes
|
|
79
|
+
splunkctl rules update 'My Rule' --enabled --yes
|
|
80
|
+
splunkctl rules delete 'My Rule' --yes
|
|
81
|
+
splunkctl rules enable 'My Rule' --yes
|
|
82
|
+
splunkctl rules disable 'My Rule' --yes
|
|
83
|
+
splunkctl rules history 'My Rule'
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Alerts
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
splunkctl alerts list
|
|
90
|
+
splunkctl alerts get 'Alert Name'
|
|
91
|
+
splunkctl alerts actions # list alert action types
|
|
92
|
+
splunkctl alerts suppress 'Alert Name' --duration 7200 --yes
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Dashboards
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
splunkctl dashboards list
|
|
99
|
+
splunkctl dashboards list --app search
|
|
100
|
+
splunkctl dashboards get my_dashboard
|
|
101
|
+
splunkctl dashboards create --name new_dash --file dash.xml \
|
|
102
|
+
--app search --label 'My Dashboard' --yes
|
|
103
|
+
splunkctl dashboards update my_dash --file updated.xml --app search --yes
|
|
104
|
+
splunkctl dashboards delete my_dash --app search --yes
|
|
105
|
+
splunkctl dashboards export my_dash --out dash.xml
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Indexes
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
splunkctl indexes list
|
|
112
|
+
splunkctl indexes get main
|
|
113
|
+
splunkctl indexes create --name my_index --yes
|
|
114
|
+
splunkctl indexes create --name metrics_idx --datatype metric \
|
|
115
|
+
--max-size 500 --frozen-period 604800 --yes
|
|
116
|
+
splunkctl indexes update my_index --max-size 1000 --yes
|
|
117
|
+
splunkctl indexes update my_index --frozen-period 2592000 --yes
|
|
118
|
+
splunkctl indexes delete my_index --yes
|
|
119
|
+
splunkctl indexes clean my_index --yes # remove all events
|
|
120
|
+
splunkctl indexes reload --yes
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Inputs
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
splunkctl inputs list
|
|
127
|
+
splunkctl inputs list --kind monitor
|
|
128
|
+
splunkctl inputs get /var/log/syslog
|
|
129
|
+
splunkctl inputs create --name /var/log/app.log --kind monitor \
|
|
130
|
+
--index main --sourcetype syslog --yes
|
|
131
|
+
splunkctl inputs update /var/log/app.log --sourcetype json --yes
|
|
132
|
+
splunkctl inputs update /var/log/app.log --disabled --yes
|
|
133
|
+
splunkctl inputs delete /var/log/app.log --yes
|
|
134
|
+
splunkctl inputs enable /var/log/app.log --yes
|
|
135
|
+
splunkctl inputs disable /var/log/app.log --yes
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Lookups
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
splunkctl lookups list
|
|
142
|
+
splunkctl lookups list --app Splunk_Security_Essentials
|
|
143
|
+
splunkctl lookups get my_lookup.csv
|
|
144
|
+
splunkctl lookups upload my_lookup.csv --file data.csv --app search --yes
|
|
145
|
+
splunkctl lookups update my_lookup.csv --file updated.csv --app search --yes
|
|
146
|
+
splunkctl lookups download my_lookup.csv --app search
|
|
147
|
+
splunkctl lookups download my_lookup.csv --app search --out local.csv
|
|
148
|
+
splunkctl lookups delete my_lookup.csv --app search --yes
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Parsers (sourcetypes & extractions)
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
splunkctl parsers sourcetypes # list all sourcetypes
|
|
155
|
+
splunkctl parsers get syslog # get sourcetype config
|
|
156
|
+
splunkctl parsers extractions # list field extractions
|
|
157
|
+
splunkctl parsers create --sourcetype mysource --category Custom --yes
|
|
158
|
+
splunkctl parsers create --sourcetype mysource \
|
|
159
|
+
--category Custom --transforms my_extraction --yes
|
|
160
|
+
splunkctl parsers update mysource --category Operating_System --yes
|
|
161
|
+
splunkctl parsers delete mysource --yes
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Apps
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
splunkctl apps list
|
|
168
|
+
splunkctl apps get SplunkForwarder
|
|
169
|
+
splunkctl apps install --name my_app --path /path/to/app.tar.gz --yes
|
|
170
|
+
splunkctl apps uninstall my_app --yes
|
|
171
|
+
splunkctl apps update my_app --enabled --visible --yes
|
|
172
|
+
splunkctl apps update my_app --disabled --hidden --yes
|
|
173
|
+
splunkctl apps reload --yes
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Note: `apps install --path` refers to the package path on the Splunk server
|
|
177
|
+
filesystem.
|
|
178
|
+
|
|
179
|
+
### Users
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
splunkctl users list
|
|
183
|
+
splunkctl users get admin
|
|
184
|
+
splunkctl users roles # list all roles
|
|
185
|
+
splunkctl users create --name newuser --password 'pass' \
|
|
186
|
+
--roles user --email user@example.com --yes
|
|
187
|
+
splunkctl users update newuser --roles 'user,power' --yes
|
|
188
|
+
splunkctl users delete newuser --yes
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Config
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
splunkctl config init # interactive setup
|
|
195
|
+
splunkctl config init --host h --port 8089 --username u --password p
|
|
196
|
+
splunkctl config show # display config (redacted)
|
|
197
|
+
splunkctl config test # verify connectivity
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Info & version
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
splunkctl info # server info
|
|
204
|
+
splunkctl --version # CLI version
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Agent discovery
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
splunkctl commands # JSON command tree
|
|
211
|
+
splunkctl skill # print this guide
|
|
212
|
+
splunkctl skill install # install to ~/.claude/skills/
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Workflow patterns
|
|
216
|
+
|
|
217
|
+
### Investigate an alert
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
splunkctl alerts list
|
|
221
|
+
splunkctl rules get 'Alert Rule Name'
|
|
222
|
+
splunkctl search run 'index=main error' --earliest -7d --limit 1000
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Audit detection coverage
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
splunkctl rules list --json | jq '[.[] | select(.is_scheduled == "1")]'
|
|
229
|
+
splunkctl rules list --json | jq '[.[] | select(.disabled == "1")]'
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Detection rule lifecycle
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
# Create, test, enable:
|
|
236
|
+
splunkctl rules create --name 'Failed Logins' \
|
|
237
|
+
--search 'index=main sourcetype=auth action=failure | stats count by user' \
|
|
238
|
+
--cron '*/15 * * * *' --actions email --yes
|
|
239
|
+
splunkctl search run 'index=main sourcetype=auth action=failure | stats count by user'
|
|
240
|
+
splunkctl rules enable 'Failed Logins' --yes
|
|
241
|
+
splunkctl rules history 'Failed Logins'
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Export a dashboard for version control
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
splunkctl dashboards export my_dashboard --out dashboards/my_dashboard.xml
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Bulk lookup update
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
splunkctl lookups download hosts.csv --app search --out hosts.csv
|
|
254
|
+
# Edit hosts.csv locally...
|
|
255
|
+
splunkctl lookups update hosts.csv --file hosts.csv --app search --yes
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Check index health
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
splunkctl indexes list --json \
|
|
262
|
+
| jq '.[] | {name, totalEventCount, currentDBSizeMB}'
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### User and role audit
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
splunkctl users list --json | jq '.[] | {name, roles, email}'
|
|
269
|
+
splunkctl users roles --json | jq '.[] | {name, capabilities}'
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Discover data sources
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
splunkctl search oneshot '| metadata type=sourcetypes index=*' --limit 500
|
|
276
|
+
splunkctl search oneshot '| metadata type=sources index=main' --limit 100
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## SPL tips
|
|
280
|
+
|
|
281
|
+
| Pattern | Example |
|
|
282
|
+
|---|---|
|
|
283
|
+
| Time range | `--earliest -24h --latest now` |
|
|
284
|
+
| Stats | `index=main \| stats count by sourcetype` |
|
|
285
|
+
| Table | `index=main \| table _time host source message` |
|
|
286
|
+
| Dedup | `index=main \| dedup host` |
|
|
287
|
+
| Where | `index=main \| where count > 10` |
|
|
288
|
+
| Eval | `index=main \| eval dur=end-start` |
|
|
289
|
+
| Timechart | `index=main \| timechart span=1h count by source` |
|
|
290
|
+
| Lookup | `\| inputlookup my_lookup.csv` |
|
|
291
|
+
| Generate | `\| makeresults count=10 \| eval x=random()` |
|
|
292
|
+
| REST | `\| rest /services/server/info` |
|
|
293
|
+
| Metadata | `\| metadata type=sources index=main` |
|
|
294
|
+
| Tstats | `\| tstats count where index=main by sourcetype` |
|
|
295
|
+
| Rex | `\| rex field=_raw "code=(?<code>\d+)"` |
|
|
296
|
+
|
|
297
|
+
## Error handling
|
|
298
|
+
|
|
299
|
+
- **Connection errors**: run `splunkctl config test` to verify auth
|
|
300
|
+
- **Timeout**: increase with `--timeout 120`
|
|
301
|
+
- **SSL errors**: SSL verification is off by default. Use `--verify` during
|
|
302
|
+
`config init` to enable certificate validation for production.
|
|
303
|
+
- **Not found**: commands print `Error: ...` to stderr and exit 1
|
|
304
|
+
- **Dry-run block**: add `--yes` to apply mutations
|
|
305
|
+
- **Permission denied**: check user roles with `splunkctl users get <name>`
|
|
306
|
+
- **Debug**: add `--debug` to see full HTTP request/response logs
|
|
307
|
+
|
|
308
|
+
## Output piping
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
# JSON to jq
|
|
312
|
+
splunkctl rules list --json | jq '.[] | .name'
|
|
313
|
+
|
|
314
|
+
# CSV to file
|
|
315
|
+
splunkctl indexes list --format csv --out indexes.csv
|
|
316
|
+
|
|
317
|
+
# JSONL for streaming
|
|
318
|
+
splunkctl search export 'index=main' --format jsonl > events.jsonl
|
|
319
|
+
|
|
320
|
+
# Field projection
|
|
321
|
+
splunkctl users list --fields name,roles
|
|
322
|
+
|
|
323
|
+
# Alternate config
|
|
324
|
+
splunkctl -c /path/to/other.yaml config test
|
|
325
|
+
```
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: splunkctl
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CLI tool for Splunk Enterprise SIEM operations.
|
|
5
|
+
Author: dannyota
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/dannyota/splunkctl
|
|
8
|
+
Project-URL: Repository, https://github.com/dannyota/splunkctl
|
|
9
|
+
Project-URL: Issues, https://github.com/dannyota/splunkctl/issues
|
|
10
|
+
Keywords: splunk,siem,security,cli,detection,spl
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: System Administrators
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Security
|
|
18
|
+
Classifier: Topic :: System :: Systems Administration
|
|
19
|
+
Requires-Python: >=3.13
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: splunk-sdk>=2.1.0
|
|
23
|
+
Requires-Dist: click>=8.0
|
|
24
|
+
Requires-Dist: pyyaml>=6.0
|
|
25
|
+
Requires-Dist: tabulate>=0.9
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# splunkctl
|
|
29
|
+
|
|
30
|
+
CLI tool for Splunk Enterprise SIEM operations.
|
|
31
|
+
|
|
32
|
+
Query, inspect, and manage a Splunk Enterprise instance from the terminal.
|
|
33
|
+
Built on the [splunk-sdk-python](https://github.com/dannyota/splunk-sdk-python)
|
|
34
|
+
fork with [Click](https://click.palletsprojects.com/).
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install splunkctl
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Requires Python 3.13+.
|
|
43
|
+
|
|
44
|
+
## Quick start
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
splunkctl config init # interactive setup
|
|
48
|
+
splunkctl config test # verify connectivity
|
|
49
|
+
splunkctl search run 'index=main | head 10' # run a search
|
|
50
|
+
splunkctl rules list # list detection rules
|
|
51
|
+
splunkctl dashboards list # list dashboards
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Commands
|
|
55
|
+
|
|
56
|
+
| Group | Description |
|
|
57
|
+
|---|---|
|
|
58
|
+
| `config` | Setup, show config, test connectivity |
|
|
59
|
+
| `info` | Server info (version, OS, license) |
|
|
60
|
+
| `search` | Run, export, oneshot, job management |
|
|
61
|
+
| `rules` | Detection rules (saved searches) — full CRUD |
|
|
62
|
+
| `alerts` | Fired alerts, alert actions, suppression |
|
|
63
|
+
| `dashboards` | Dashboard CRUD (XML) |
|
|
64
|
+
| `indexes` | Index management |
|
|
65
|
+
| `inputs` | Data inputs (monitor, tcp, udp, script, http) |
|
|
66
|
+
| `lookups` | Lookup table CRUD (CSV) |
|
|
67
|
+
| `parsers` | Source types and field extractions |
|
|
68
|
+
| `apps` | App install, uninstall, update |
|
|
69
|
+
| `users` | User and role management |
|
|
70
|
+
| `commands` | Machine-readable command tree (JSON) |
|
|
71
|
+
| `skill` | Embedded agent operating guide |
|
|
72
|
+
|
|
73
|
+
## Global flags
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
--json Force JSON output
|
|
77
|
+
--format FMT Output format: table, json, csv, jsonl
|
|
78
|
+
--fields f1,f2 Project specific fields
|
|
79
|
+
--out FILE Write output to file
|
|
80
|
+
--yes / -y Apply mutations (skip dry-run preview)
|
|
81
|
+
--timeout N Request timeout in seconds (default 30)
|
|
82
|
+
--config FILE Config file path
|
|
83
|
+
--debug HTTP request/response logging
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Dry-run by default
|
|
87
|
+
|
|
88
|
+
All write operations preview what would change. Pass `--yes` to apply.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
splunkctl rules delete 'My Rule' # shows preview only
|
|
92
|
+
splunkctl rules delete 'My Rule' --yes # actually deletes
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Output formats
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
splunkctl rules list # table (TTY) or JSON (pipe)
|
|
99
|
+
splunkctl rules list --json # force JSON
|
|
100
|
+
splunkctl rules list --format csv # CSV
|
|
101
|
+
splunkctl rules list --fields name,cron # project fields
|
|
102
|
+
splunkctl rules list --out rules.json # write to file
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Agent integration
|
|
106
|
+
|
|
107
|
+
splunkctl ships with an embedded operating guide for AI agents
|
|
108
|
+
(Claude Code, etc.):
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
splunkctl skill # print the guide
|
|
112
|
+
splunkctl skill install # install to ~/.claude/skills/
|
|
113
|
+
splunkctl commands # JSON command tree for discovery
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
Apache-2.0
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
splunkctl/__init__.py,sha256=jaQAmH6F2QEzLK6CKTTeLVFm_ItSvm0-XYBdWs_z44k,77
|
|
2
|
+
splunkctl/__main__.py,sha256=BFgY3u1UOfyuBe8JM6zFQz0AIfY9YA2ooWnX9keF1Vs,85
|
|
3
|
+
splunkctl/client.py,sha256=XwZ51yZI17wJKVyytdYMRGU_2W5O9tT7Av6BPrcO0zA,3121
|
|
4
|
+
splunkctl/config.py,sha256=bYkgERBS-iTWoR2NJB5LdOlBB4PJW5amXj4u-h6UjP4,2251
|
|
5
|
+
splunkctl/guard.py,sha256=5F6QBx4yPpvR1Cb4BkhtkCZFDC1tPtMFrwaRespiiWo,595
|
|
6
|
+
splunkctl/main.py,sha256=vRUHcBu5UsXfDIkVyKLYByOe-nIAKUDKARKY9Jxfwls,4651
|
|
7
|
+
splunkctl/output.py,sha256=3bPTp1UAEeVrWm8D-N0zxtEB1nb_so1YygHv1iUMPSs,2012
|
|
8
|
+
splunkctl/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
splunkctl/commands/alerts.py,sha256=ScaYrdXXCMRBOG-3T5hFDWgiUBpkj_ajmiicbHSe3AY,2784
|
|
10
|
+
splunkctl/commands/apps.py,sha256=USO02YA2FqqrFeEBEKXmZuG0Mh992-jQSD5LT96mgKU,5052
|
|
11
|
+
splunkctl/commands/commands_meta.py,sha256=lNfWG_R5vcXIyQEC--GiIrmez-yn7F0kFGp4nJGlk6g,2260
|
|
12
|
+
splunkctl/commands/config_cmd.py,sha256=pwudX7vWFDkCHwB0l8qz7rjsNeajkZSAlC9-0IjumYg,2514
|
|
13
|
+
splunkctl/commands/dashboards.py,sha256=_wtypQ9-daQ3blAIiC7QU0KkAxVuEQlum3Nc3vbcawc,6677
|
|
14
|
+
splunkctl/commands/indexes.py,sha256=Tfb5RCPxXN1ziknj5KUqG3un-IbdvGPa7Qb7sw2Gsvo,5532
|
|
15
|
+
splunkctl/commands/info.py,sha256=oOUl37rPKTOSRQa_l3VPXhsu1lqJCAVGHzxtm6unRWA,766
|
|
16
|
+
splunkctl/commands/inputs.py,sha256=EdPT-QcRMfPBR6ocZcC30RdmMFcmOp2IBfSeL-l6raI,5474
|
|
17
|
+
splunkctl/commands/lookups.py,sha256=PDnrFuhAcW45-7J1q2F9Oso1MMQg6AezhR33oUkw_P0,6786
|
|
18
|
+
splunkctl/commands/parsers.py,sha256=CDB6xiWqOObhGaFZsZGanyVhVg93Y0x0ZPDElZgzV-o,5080
|
|
19
|
+
splunkctl/commands/rules.py,sha256=kAuDUnuWmKMOhT4Y-6DgJMavHmk3FAOX5v4Kz2Gz8O4,7523
|
|
20
|
+
splunkctl/commands/search.py,sha256=F-fpx5aNZbOFog3MN_v8GIYka8dkhpVE2qi7URuQIlM,7299
|
|
21
|
+
splunkctl/commands/skill_cmd.py,sha256=AHHyAIQ37-7YE063G4VJt0vEdBeoHpVPNurlZUtFGB0,1286
|
|
22
|
+
splunkctl/commands/users.py,sha256=LnSqzby83_zmoXaJgjWydWcNY1GGm5eeMyN4zF6xYzg,5651
|
|
23
|
+
splunkctl/skill/SKILL.md,sha256=tXwDMQ9K60ZgbGtmMu_tsGMhVNpMcAoea30iZHB9tl8,10148
|
|
24
|
+
splunkctl-0.1.0.dist-info/licenses/LICENSE,sha256=5bLe84fMyhGGYnIjBvpaFTCmsYiTfZg7hII9LgXXtRU,10759
|
|
25
|
+
splunkctl-0.1.0.dist-info/METADATA,sha256=3gUIxZlp9w08K9xguXeItolG0yeXvfgxQxFJc9QI0X0,3732
|
|
26
|
+
splunkctl-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
27
|
+
splunkctl-0.1.0.dist-info/entry_points.txt,sha256=bmtfsYIWQv5DqX00d4MZEEyZWlmL_qR_uCtj5ZCCCKQ,49
|
|
28
|
+
splunkctl-0.1.0.dist-info/top_level.txt,sha256=xEU2C2YYO7cEc1h7-DOcEsfprA0T_Mvrjr5fW3RKtiQ,10
|
|
29
|
+
splunkctl-0.1.0.dist-info/RECORD,,
|