f5audit
Read-only audit tool for F5 BIG-IP LTM. It collects configuration and statistics via iControl REST (GET only), correlates object references, and produces a multi-sheet Excel report identifying unused objects (nodes, pools, virtual servers, monitors) as input for a human-driven, change- controlled cleanup.
This tool never modifies the device. The "suggested command" columns in the report are informational text only; nothing is ever executed.
Safety design
- The HTTP client (
F5ReadOnlyClient) exposes a singleget()method. There are nopost/patch/put/deletemethods. The only internal write is the token login, hardcoded to/mgmt/shared/authn/login. This is enforced by structural tests. - No
tmsh/bash execution endpoints are used or referenced anywhere. - No explicit logout (deleting the token would be a write); tokens expire on their own (~20 minutes).
- Passwords are read from an interactive prompt (
getpass) or theF5_PASSenvironment variable — never from a CLI argument, and never written to disk or logs. - Management-plane friendly: sequential requests only,
$top/$skippagination, per-request delay (--delay, default 0.1 s), 30 s timeout, max 2 retries with exponential backoff. - The optional
pingpost-process is the sole SSH feature: opt-in, separate from collection, and structurally ping-only — fixed command templates, the only interpolated value is anipaddress-validated IPv4 literal, and non-ping commands are refused at runtime. Ping is read-only ICMP; nothing on the device is created, modified, or deleted. SSH host keys are auto-accepted (management-network tool, same spirit as--insecure).
Requirements
- Python >= 3.9,
requests,openpyxl - An account with a read-only role (Auditor/Guest) and iControl REST access on the BIG-IP
- Network access to the management interface (TCP 443)
- Only for the optional
pingpost-process:pip install 'f5audit[ssh]'(paramiko) and SSH access (TCP 22) with an account that can runping
Installation
pip install f5audit
From source: pip install . — or without installing, from the project
directory: python -m f5audit ...
Usage
1. Validate access first
f5audit validate --host 192.0.2.1 --user auditor --insecure
Probes login plus the key GET endpoints and prints a diagnosis per response code (bad credentials, missing REST access, denied endpoints, old BIG-IP versions, network timeouts).
2. Collect once, analyze offline N times
f5audit collect --host 192.0.2.1 --user auditor --insecure --save-raw ./raw/
f5audit analyze --from-raw ./raw/ --out report.xlsx
collect saves every raw JSON response (with timestamps) to disk;
analyze --from-raw re-analyzes from that cache without touching the
F5 again. This is the recommended workflow: one collection per session,
all further analysis offline.
Resumable collection: pointing --save-raw at a directory that
already contains raw JSON fetches only the missing datasets — use it
to resume an aborted collection, or to top up an old cache with data a
newer version collects (e.g. the ARP/self-IP tables). Whatever failed
previously has no file and is retried once; whatever succeeded is never
re-fetched. Note the resulting cache mixes collection times (flagged on
the Summary sheet); for a fully time-consistent snapshot, use a fresh
directory.
One-step alternative
f5audit analyze --host 192.0.2.1 --user auditor --insecure --save-raw ./raw/ --out report.xlsx
3. Optional post-process: ping the removal candidates over SSH
pip install 'f5audit[ssh]'
f5audit ping --report report.xlsx --host 192.0.2.1 --user auditor
Double-checks the removal candidates in an already generated report:
a node with no ARP entry that also does not answer ping is a much safer
deletion conversation. The command reads the IP column of the
Orphan Nodes sheet — by construction the nodes that are not
IN USE, so in-use nodes are never pinged — deduplicates the IPs (a
node in several pools is pinged once), runs ping on the BIG-IP over
SSH for each unique IPv4, and writes two columns into both the
Inventory and Orphan Nodes sheets in place, replicating each IP's
result across every row where it appears:
Ping (from F5) |
Meaning |
|---|---|
YES / NO |
The node answered / did not answer ICMP from the F5 |
UNKNOWN |
Ping output could not be parsed (raw excerpt in Ping note) |
NOT TESTED |
Skipped: non-zero route domain, IPv6, FQDN node, or SSH failed mid-run |
Notes:
- Works with both Advanced-shell (bash) and tmsh-shell accounts — the command form is auto-detected with one probe.
- Password from
F5_PASSor an interactive prompt, as everywhere else; SSH keys/agent are also tried automatically. - Close the report in Excel first: if the file is locked, results are
saved to
<name>_ping.xlsxinstead of being lost. - Re-running overwrites the ping columns (never duplicates them).
- Like ARP, ping is point-in-time evidence:
NOmeans "did not answer at that moment", not "gone".--count(default 2) echo requests are sent per IP, sequentially.
Options
| Flag | Meaning |
|---|---|
--user / F5_USER |
Username (password via prompt or F5_PASS) |
--login-provider |
Token auth provider (default tmos; set for TACACS+/RADIUS) |
--insecure |
Skip TLS verification (self-signed mgmt certs); prints a warning |
--delay |
Seconds between requests (default 0.1) |
--top |
Pagination page size (default 100) |
--format xlsx|csv |
Excel workbook or one CSV per sheet |
--allow-standby |
On a standby unit, emit traffic- and availability-based verdicts marked UNRELIABLE (standby) instead of skipping them |
--report (ping) |
Existing .xlsx report to annotate in place |
--ssh-port (ping) |
SSH port (default 22) |
--count (ping) |
Echo requests per IP (default 2) |
Exit codes: 0 OK · 1 connection/auth error · 2 analysis completed
with warnings (standby device, denied partitions, missing endpoints).
Verdicts
| Verdict | Meaning |
|---|---|
ORPHAN |
Not referenced by anything (node: no pool membership; pool: no VS/iRule/policy reference; monitor: no user). Only issued when the inventory is complete and no attached dynamic iRule can reach the object (see MANUAL REVIEW). |
MANUAL REVIEW |
A dynamic iRule (pool $var, pool [...] — including datagroup lookups whose value reaches the pool command; a class match used only as a condition before pool <literal> is a static reference) or a missing ltm/rule endpoint means the object could be referenced at runtime. Never auto-cleanup these. A dynamic iRule attached to a virtual server reaches the pools of its own partition, the VS's partition, /Common, and any partition named literally (/Partition/...) in its Tcl; pools in other partitions are not affected, and nodes inherit the reach of their pools. |
INACTIVE |
Configured and referenced, but disabled or zero total connections since the last counter reset. |
OFFLINE (decommission candidate) |
Referenced, but the whole dependency chain is monitor-offline: every member of the pool is down, so the pool and its virtual servers are offline. A deletion candidate to confirm with the config owner — availability is point-in-time, so it may also mean maintenance. A node is only included when it is dead in every pool it belongs to; a node alive in another pool stays IN USE ("in use elsewhere"). Only issued on the ACTIVE unit. A pool is capped at MANUAL REVIEW when an attached dynamic iRule can reach it (deleting the pool could break that iRule at runtime); its dead member nodes are not — dynamic iRules select pools, never nodes, and do not change pool membership. |
UNRELIABLE (standby) |
Traffic-based verdict computed on a standby unit (only with --allow-standby). |
UNRELIABLE (incomplete inventory) |
Some partitions were not readable; a reference could exist in an invisible partition. |
IN USE |
Everything else. |
Operational warning
- Collect on the ACTIVE unit of the HA pair. On a standby unit traffic
counters are zeros and the tool will skip traffic analysis (or mark it
UNRELIABLEwith--allow-standby). - Traffic counters reset on reboot / stats reset. Ideally collect after several weeks of uptime; the report includes the failover-state age as context.
INACTIVEmeans "no traffic since the counters started", not "safe to delete". Use the plannedcompareworkflow (v2) — two collections some weeks apart — to distinguish real zero traffic from a recent reset. The raw cache already stores per-file timestamps to enable this.- Availability is point-in-time: an
OFFLINEchain reflects monitor state at collection time and may mean maintenance rather than decommissioning. Always confirm with the config owner before requesting deletion. - The ARP table is also point-in-time and per-unit: "no ARP entry" on a local subnet means the host was idle (or down) at collection time — dynamic entries expire after minutes of silence — and on a standby unit the table reflects that unit, not the pair. The network columns are informational context, never a verdict input.
Report sheets
- Summary — hostname, version, HA state, uptime context, partitions, verdict counts, active warnings.
- Inventory — one row per pool member (plus rows for pool-less
nodes), fully correlated: node ↔ pool ↔ virtual server ↔ monitor ↔
iRule/policy references, statuses and traffic, plus the network
context columns described below. Each row carries both
the
Node verdictand the (colored)Pool verdictwith its notes, since a node can beOFFLINEinside a pool held atMANUAL REVIEW. Two columns concern iRules and are not expected to match:VS iRulesis configuration (the iRules attached to the row's virtual servers — in F5, iRules only attach to virtual servers), whileiRules selecting poolis code analysis (every iRule whose Tcl containspool <this pool>, whichever virtual server it is attached to). The latter, withPolicies forwarding to pool, is the evidence that keeps a pool with no default-pool reference from beingORPHAN. - Orphan Nodes · 4. Orphan-Inactive Pools (with the same
iRules selecting pool/Policies forwarding to poolevidence columns) · 5. Inactive Virtual Servers (with the attachediRules) — filtered views with informationaltmshcommands for the change request. - Dead Chains — one row per monitor-offline pool (verdict
OFFLINE (decommission candidate), orMANUAL REVIEWwhen dynamic iRules cap it), grouping the whole chain (virtual servers → pool → member nodes) with per-object verdicts and the informationaltmsh deletelines to take to the config owner. Only objects with anOFFLINEverdict get a delete line: a capped pool, or a node still alive in another pool, is listed without one. - Orphan Monitors — filtered view with informational
tmshcommands. - Manual Review — objects touched by dynamic logic, with the iRule/policy that causes the doubt.
Color coding: red = ORPHAN · yellow = MANUAL REVIEW / UNRELIABLE · orange = INACTIVE · purple = OFFLINE · green = IN USE.
Network context columns (Inventory, Orphan Nodes)
Every collection also reads the F5's own network tables — self-IPs
(net/self), the static ARP entries (net/arp) and the dynamic ARP
table (net/arp/stats), all plain GETs — and appends two informational
columns per node:
- ARP MAC — the node's MAC address when it appears in the ARP table.
- Network note — one of:
in ARP table (MAC ...)(the host answered ARP recently, so it existed at collection time);on local subnet, no ARP entry (idle or down)(directly connected per the self-IP subnets, but silent);not directly connected (behind a router)(the F5 would never have an ARP entry for it); or a degradation note (FQDN node, IPv6 address, self-IP data unavailable,network data not collectedfor caches from older versions).
An orphan node that is also absent from ARP makes a safer deletion conversation — but these columns never change a verdict.
Development
pip install -e ".[dev]"
ruff check f5audit tests && ruff format --check f5audit tests
pytest
No test touches the network; everything runs from anonymized JSON fixtures and mocked HTTP sessions. Structural tests assert the client exposes no write verbs and that no forbidden endpoint appears in the source. CI runs lint plus the test suite on Python 3.9 through 3.14; all checks must pass before a PR can merge.
Releases are published to PyPI automatically: bump __version__ in
f5audit/__init__.py, merge, and create a GitHub release tagged
v<version>. The release workflow verifies the tag matches the package
version, builds, and publishes via PyPI Trusted Publishing (no stored
API tokens).
Metadata
Release files for f5audit 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| f5audit-0.3.0.tar.gz | 64.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| f5audit-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 109.4 kB
Release files / f5audit-0.3.0.tar.gz
| Download URL | f5audit-0.3.0.tar.gz |
|---|---|
| Size | 64.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4fee0694505eb02c1824f9be6f9e47eacf98c6306adca7eef56d6c4f57bc4564
|
|
BLAKE2b-256 checksum How to use checksums |
92ca06afc5752ff38e96e56d40d7eb21ceacd6213785982c037fcbc402139813
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 27, 2026.
Transparency logRelease files / f5audit-0.3.0-py3-none-any.whl
| Download URL | f5audit-0.3.0-py3-none-any.whl |
|---|---|
| Size | 44.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
473cf2582c3927828285193bc0e6bdfdb8ae772639f978f85a427f9a6ebc4212
|
|
BLAKE2b-256 checksum How to use checksums |
4db492d7ff97436e75c4a2b2fb7a1102dd51e3bbb7dfa1113885f700accf0e3c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 27, 2026.
Transparency log