Skip to content

Three questions worth asking first

You have it connected. These three questions are the ones worth asking first, not because they are impressive but because each one exercises a different part of the tool surface, and together they show you where the seams are.

Ask them of a lab appliance the first time through.

Start wide. Ask the model to describe the appliance and its network:

Give me an overview of this firewall — what it is, what interfaces it has, and what VLANs are configured.

Three calls happen: get_system_status, list_interfaces, and list_vlans.

Two things in the answer are worth looking at closely.

The interface list is shorter than the one in the web UI. FortiOS generates bookkeeping interfaces alongside real ones — a quarantine interface for every wireless VAP, an SSL-VPN tunnel root — and those are hidden. hidden_internal names them rather than counting them, so you can see exactly what was left out. If you want them included, ask for internal interfaces and the model passes include_internal.

Which ones count as bookkeeping is decided by asking the appliance rather than by reading the names, and hidden_internal_basis tells you which of those happened. That distinction earned itself: guessing from name prefixes had ssl.root and naf.root exactly backwards, in both directions.

The interface addresses are host addresses, not networks. An interface reported as 10.20.30.1/24 really does have .1 on it. That sounds too obvious to mention until you know that FortiOS writes an interface address and a subnet object in the identical format, and that collapsing one of them to its network is a mistake with no symptom.

Pick an address you can see on the network and ask who has it. On a lab box, find one first with show me the current DHCP leases.

What is 10.20.30.84?

This is find_device, and it is the tool that shows most clearly why the surface is shaped the way it is. One call reads three FortiOS endpoints — the wireless client list, the DHCP lease table, and the ARP table — and merges them by MAC into one record:

{
"mac": "aa:bb:cc:dd:ee:01",
"seen_in": ["dhcp", "arp", "wifi"],
"ip": "10.20.30.84",
"hostname": "kevins-laptop",
"interface": "vlan30",
"ssid": "office",
"signal_dbm": -54,
"wireless": true
}

seen_in is the field to read first. A device in all three is a wireless client with a lease that the appliance is actively talking to. A device in arp only is statically addressed, or its lease came from a DHCP server that is not this firewall — either way, it is present and the appliance knows about it.

The join is by MAC, and that is why the tool exists rather than the model doing it. A join key has to be canonical on every axis at once — case and punctuation — or AA-BB-CC-DD-EE-FF and aa:bb:cc:dd:ee:ff index one device as two, and the failure is silent: three partial records that look like three separate devices.

It also means you can paste a MAC in whatever form you copied it from. A switch CLI’s 2047.477d.db7b and a label’s 20-47-47-7D-DB-7B both find the device.

Pick an address object from list_address_objects and ask:

Is the address object DMZ-SERVERS safe to delete?

This is find_references, and it is the question that precedes every configuration change on a firewall. The answer comes from the appliance’s own reference lookup, the same one behind the reference counter in the web UI.

{
"object": "DMZ-SERVERS",
"verdict": "referenced",
"resolved_as": ["address"],
"total_references": 3,
"candidate_tables": 74,
"references": [
{ "table": "firewall.policy", "object": "9", "attribute": "dstaddr" },
{ "table": "firewall.addrgrp", "object": "ALL-SERVERS", "attribute": "member" }
],
"sources_checked": { "object_usage": "ok", "policies": "ok", "routes": "ok" },
"safe_to_delete": false,
"policies": [
{ "id": 9, "name": "block-legacy-smb", "enabled": true, "action": "deny", "referenced_as": ["destination"] },
{ "id": 12, "name": "dmz-mgmt", "enabled": false, "action": "accept", "referenced_as": ["destination", "source"] }
],
"groups": [{ "name": "ALL-SERVERS", "kind": "address_group" }],
"vips": [],
"routes": []
}

candidate_tables: 74 is the number worth pausing on. That is how many tables could hold a reference to an address object on this firmware, and it is why the tool asks the appliance instead of checking a few tables by hand and hoping.

Notice policy 12: it is disabled. It still counts as a reference, because a disabled policy is a policy somebody intends to turn back on, and deleting the object it points at turns re-enabling it into a surprise.

Now try it with a deliberate typo — DMZ-SERVRES. The verdict comes back object_not_found and there is no safe_to_delete key at all, because the question was about a name rather than an object. Without that verdict a misspelling would report zero references and a confident yes.

Three of the five verdicts omit safe_to_delete for reasons like that, and they are normal outcomes rather than errors — especially with a correctly least-privileged token. What each verdict means.

You have now used all three kinds of tool: a configuration read, a live-state read, and a cross-referencing read.

Reading a ruleset is the next thing most people want, and it is the one where evaluation order starts to matter. The tool reference is the complete surface when you want to know what else is there.