The moment you templatise firewall config in FortiManager, you hit the same wall: 90% of the config is identical across every branch, but 10% — the WAN interface, the LAN subnet, the BGP router-id, the site code — is unique per device. Hard-code that 10% and the template only fits one box. Metadata variables are how you carve out the unique parts so one template fits the whole fleet.
The idea
A metadata variable is a named value that FortiManager resolves per device at install time. You write {{lan_subnet}} once in a shared CLI template; each device supplies its own value; the rendered config is unique but the source is single.
config system interface
edit "{{lan_if}}"
set ip {{lan_gateway}} {{lan_mask}}
set allowaccess ping
next
end
Install this to three devices and each gets its own interface, IP and mask — from one template.
The template holds the logic; the variables hold the exceptions. Keeping those two separate is the whole trick — the 90% that's identical lives in one place, the 10% that differs lives in a small, reviewable dataset.
Defining the variables
Metadata variables live under Policy & Objects → Object Configurations → Advanced → Metadata Variables (exact path shifts a little by version). Create the variable, mark it required or give it a default, then set a per-device value on each managed device.
For a fleet, setting them one-by-one in the GUI defeats the purpose — export the device list, fill the values in a spreadsheet, and set them through the JSON-RPC API so the data is the source of truth, not the clicks. The point is that the values become a small, reviewable dataset and the logic stays in one template.
Where it pays off
- IPsec / ADVPN spokes — every spoke runs identical tunnel logic; only the overlay IP and WAN interface differ. (See ADVPN at Scale with FortiManager Templates for the full fabric build.)
- BGP — shared neighbour logic, per-device
router-id. - Naming and tagging — a
site_codevariable drives interface descriptions, hostnames and policy comments so the config is self-documenting.
Guardrails
- Default carefully. A required variable with no value fails the install loudly — which is what you want. A variable with a wrong default fails silently by shipping a bad config. Prefer required-with-no-default for anything security-relevant.
- Preview before you push. The Install Wizard's preview renders the variables for real — read the diff on a pilot device before you touch the fleet.
- One source of truth. Once a value is a metadata variable, stop editing it directly on the device. The next install will overwrite local drift, and you'll spend an afternoon working out why.
Templates give you consistency; metadata variables give you the exceptions without breaking the consistency. Together they're the difference between managing a fleet and managing fifty snowflakes.