83eddc872a
First real run returned empty and we had no idea why — the script was silently swallowing stderr via `2>/dev/null`. Remove the suppression and try both `execute dhcp lease-list all` and the no-arg form, keeping whichever returns non-error output. Also emit a clearer diagnostic when both fail, pointing the user at an interactive SSH to poke at command syntax.
87 lines
2.9 KiB
Bash
Executable File
87 lines
2.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# discover-fortigate.sh — pull DHCP lease list from a FortiGate.
|
|
#
|
|
# SSHes to a FortiGate admin account, runs `execute dhcp lease-list`,
|
|
# parses the output into TSV (IP, MAC, hostname, vdom, source).
|
|
#
|
|
# Usage:
|
|
# scripts/discover-fortigate.sh <fortigate-host>
|
|
#
|
|
# Env overrides:
|
|
# FORTIGATE_SSH_USER default: admin
|
|
#
|
|
# Output: TSV on stdout, one lease per line:
|
|
# IP MAC HOSTNAME VDOM SOURCE
|
|
# Where SOURCE is "fortigate:<host>" so multiple runs can be concatenated
|
|
# and still identified.
|
|
#
|
|
# Example:
|
|
# scripts/discover-fortigate.sh ana-fw.phasefinal.com > leases-ana.tsv
|
|
# scripts/discover-fortigate.sh nh3-gw.phasefinal.com > leases-nh3.tsv
|
|
#
|
|
# Requires: ssh config (or `~/.ssh/config` host alias) for the FortiGate,
|
|
# with key auth OR interactive password. FortiGate's SSH expects admin-level
|
|
# credentials.
|
|
|
|
set -euo pipefail
|
|
|
|
if [ -z "${1:-}" ]; then
|
|
echo "usage: $(basename "$0") <fortigate-host>" >&2
|
|
exit 2
|
|
fi
|
|
|
|
HOST="$1"
|
|
USER="${FORTIGATE_SSH_USER:-admin}"
|
|
|
|
# FortiGate CLI command. `execute dhcp lease-list all` dumps every vdom.
|
|
# If the device is single-vdom, `execute dhcp lease-list` (no arg) also works.
|
|
#
|
|
# Do NOT suppress stderr — FortiGate's error messages are the main debugging
|
|
# signal when the command returns empty. Let them flow to the caller.
|
|
|
|
raw=""
|
|
for cmd in 'execute dhcp lease-list all' 'execute dhcp lease-list'; do
|
|
raw=$(ssh -o StrictHostKeyChecking=accept-new "${USER}@${HOST}" "$cmd" || true)
|
|
if [ -n "$raw" ] && ! grep -qiE 'unknown action|parse error|command fail' <<<"$raw"; then
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ -z "$raw" ]; then
|
|
echo "error: no lease data from $HOST" >&2
|
|
echo " — try running 'ssh ${USER}@${HOST}' interactively and running" >&2
|
|
echo " 'execute dhcp lease-list ?' to discover the right subcommand shape" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Parse.
|
|
#
|
|
# FortiGate lease output looks approximately like:
|
|
#
|
|
# VDOM: root
|
|
# Interface: internal
|
|
# IP MAC Hostname Lease-Expiry
|
|
# 10.250.50.100 00:1a:2b:3c:4d:5e laptop-01 Mon Apr 21 14:30:00 2026
|
|
# 10.250.50.101 aa:bb:cc:dd:ee:ff printer Mon Apr 21 15:00:00 2026
|
|
#
|
|
# Format varies by FortiOS version. The awk below is defensive:
|
|
# - only accepts lines where field 1 matches IPv4
|
|
# - assumes MAC is field 2, hostname is field 3-4 (may contain whitespace),
|
|
# which is the most common layout.
|
|
awk -v src="fortigate:${HOST}" -v vdom="" '
|
|
/^VDOM:/ { vdom=$2; next }
|
|
/^Interface:/ { next }
|
|
/^IP[[:space:]]/ { next }
|
|
/^[[:space:]]*$/ { next }
|
|
$1 ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/ {
|
|
ip = $1
|
|
mac = $2
|
|
# Hostname may be missing (shows "-" or empty) or contain spaces; take
|
|
# the rest of the line minus the trailing date fields.
|
|
host = ""
|
|
for (i=3; i<=NF-6; i++) host = host (host=="" ? "" : " ") $i
|
|
if (host == "" || host == "-") host = "-"
|
|
printf "%s\t%s\t%s\t%s\t%s\n", ip, mac, host, (vdom==""?"-":vdom), src
|
|
}
|
|
' <<<"$raw"
|