Skip to content
jpforcioliPublic

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

1 watching

Forks

Latest commit

 

History

11 Commits

Folders and files

Repository files navigation

fmgshell

An interactive shell to browse and operate one or more FortiManagers over their JSON-RPC API: ls ADOMs/devices/objects, cd around, and cp objects between ADOMs or even between two different FortiManagers — with tab completion and readline-style keybindings.

The virtual file system is derived from FortiManager's own swagger specs (fmgshell/fmg_jsonrpc_api/swagger/), so the available paths reflect the real API surface rather than a hand-maintained list. ADOM/device/object instances are always fetched live from the connected FortiManager(s).

Install

uv venv .venv --python 3.14
uv pip install --python .venv/bin/python -e .

Launch

.venv/bin/fmgshell

(or source .venv/bin/activate first, then just fmgshell)

Check the installed version:

fmgshell --version

Usage

fmgshell can be logged into any number of FortiManagers at once — that's what lets cp transfer objects between them, not just between ADOMs of the same one. Every path starts with a connection name, e.g. /fmg1/dvmdb/adom/root; you can browse the structure (ls, cd) without being logged in at all, but reaching real data (ADOM names, objects, ...) requires logging into that path's connection first.

Each login picks a connection name: by default the next unused fmgN (fmg1, fmg2, ...), or anything you like via login ... as <name>. While only one connection is configured, it's the implicit default everywhere: a path that doesn't start with a connection name is assumed to mean it (so single-FortiManager use never needs to spell it out), and logout/status default their connection argument to it too. Once a second connection is added, that default disappears and every command/path must name a connection explicitly.

fmgshell:/> ls                              # (empty until you log in)
fmgshell:/> login -i <fmg1-ip-or-fqdn> -u <username> [-p <password>] [--port 443]
                                             # named "fmg1" automatically
fmgshell:/> login -i <fmg2-ip-or-fqdn> -u <username>
                                             # named "fmg2" automatically
fmgshell:/> login -i <fmg3-ip-or-fqdn> -u <username> as core
                                             # named "core" explicitly

If -p is omitted, you'll be prompted for the password without echo.

Navigate and operate:

fmgshell:/> cd /dvmdb/adom                  # same as /fmg1/dvmdb/adom
fmgshell:/fmg1/dvmdb/adom> ls                # real ADOM names from fmg1
fmgshell:/fmg1/dvmdb/adom> cd /pm/config/adom/<adom1>/obj/firewall/address
fmgshell:...address> ls                     # real address objects in adom1
fmgshell:...address> cat <name>             # show the object's full JSON
fmgshell:...address> cp <name> <adom2>/<name>
                                             # same FMG, same name, different ADOM
fmgshell:...address> cp <name> <adom2>/<newname>
                                             # copy + rename in one step
fmgshell:...address> cp -v <name> fmg2/<adom>/<name>
                                             # copy to the OTHER FortiManager
fmgshell:/> status                          # system status of the only connection; `status fmg2` if there's more than one
fmgshell:/> debug enable                    # print raw JSON-RPC requests/responses (all connections)
fmgshell:/> debug disable
fmgshell:/> debug                           # (or `debug status`) show whether debug is on, per connection
fmgshell:/> version                         # fmgshell version + FortiManager ADOM version in use
fmgshell:/> logout                          # logs out of the only connection; `logout fmg2` if there's more than one
fmgshell:/> quit

cp's destination is never the full structural path — just the identity segments the source's own path is parametrized on, in the same order, followed by the target name. The leading <fmg>/ may be omitted, which keeps the copy on the source's own connection; give it explicitly (fmg1 or fmg2) to copy across FortiManagers. For ADOM-scoped objects the shape is [<fmg>/]<adom>/<name>; for device/vdom-scoped objects (e.g. /pm/config/device/{device}/vdom/{vdom}/...) it's [<fmg>/]<device>/<vdom>/<name>:

fmgshell:...address> cp addr1 adom2/addr1           # same FMG, different ADOM
fmgshell:...address> cp addr1 adom2/addr1-copy      # copy + rename, same FMG
fmgshell:...address> cp addr1 fmg2/adom2/addr1      # different FortiManager entirely
fmgshell:...authentication-rule> cp rule1 fgt2/root/rule1
                                                     # device/vdom-scoped object

Tab completion on the destination suggests fmg1/fmg2, then valid ADOM/device/vdom names for each identity slot (queried live from whichever connection you typed), and existing object names for the final one. Because tab-completion can't reliably guess whether an omitted first part means "connection" or "ADOM", completion always spells the connection out; typing the command by hand still accepts the shorter form.

ls -l (or -al) shows a created/last-modified column, and by whom, when FortiManager exposes that for the object type. For ADOMs/devices it comes straight from dvmdb; for pm/config objects (addresses, policies, ...) it requires the "extra info"/"no loadsub" get options (see this writeup), which ls -l sends automatically — plain ls/cd don't, to keep navigation fast:

fmgshell:/> cd /fmg1/dvmdb/adom
fmgshell:/fmg1/dvmdb/adom> ls -al
adom1   created: 2024-03-09 17:00:00, os version: 7.2, workspace mode: workflow
global  created: 2023-07-22 06:26:40, os version: 6.2, workspace mode: disabled
root    created: 2023-11-14 23:13:20, os version: 7.4, workspace mode: normal

fmgshell:/> cd /fmg1/pm/config/adom/root/obj/firewall/address
fmgshell:...address> ls -al
addr1   created: 2023-04-13 19:51:15, modified: 2023-04-18 12:25:48, modified by: admin, version: 3
addr2   -

Tab completion works at every step (FMG connections, ADOMs, devices, object names, sub-resources). The shell is built on cmd2/prompt_toolkit, so standard emacs/readline keybindings work: Ctrl-A/Ctrl-E (line start/end), Ctrl-R (reverse history search), up/down arrows for history, etc. Command history persists across sessions in .fmgshell_history.

Known limitations

  • Hardcoded to FortiManager version 74's swagger files; other versions aren't wired in yet.
  • Object "identity field" detection uses a priority list (name, policyid, seq, ...) that covers the common tables but isn't exhaustive.
  • cp only strips oid before re-adding the object; some object types may need more cleanup (e.g. UUID handling).
  • cp only supports objects identified by a name (the common case); FMG's rare singleton config objects (no identity segment of their own, e.g. a per-ADOM "system dns" settings block) aren't copyable yet.
  • The ls -al "workspace mode" label (disabled/normal/workflow) is a best-effort mapping of the raw workspace_mode integer — FortiManager's swagger spec doesn't document that enum. Unrecognized values show as mode <n>.
  • Not yet tested against a live FortiManager.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages