Skip to main content
Glama
FortiManager MCP Server

FortiManager MCP Server

by rstierli

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
LOG_LEVELNoLogging level (e.g., DEBUG, INFO, WARNING).INFO
DEFAULT_ADOMNoDefault Administrative Domain (ADOM) for operations.root
FMG_TOOL_MODENoTool loading mode: 'full' or 'dynamic'.full
MCP_AUTH_TOKENNoBearer token for HTTP authentication (optional).
MCP_SERVER_HOSTNoBind address for HTTP mode.127.0.0.1
MCP_SERVER_MODENoMCP transport mode: 'http', 'stdio', or 'auto'.auto
MCP_SERVER_PORTNoPort for HTTP mode.8000
FMG_POLICY_SAFETYNoPolicy safety mode: 'strict', 'warn', 'disabled'.strict
FMG_SCRIPT_SAFETYNoScript safety mode: 'strict', 'disabled'.strict
FORTIMANAGER_HOSTYesFortiManager hostname or IP address.
MCP_ALLOWED_HOSTSNoList of allowed Host headers (JSON array string).
FORTIMANAGER_TIMEOUTNoRequest timeout in seconds.30
FORTIMANAGER_PASSWORDNoPassword for authentication.
FORTIMANAGER_USERNAMENoUsername for authentication (alternative to API token).
FORTIMANAGER_API_TOKENNoAPI token for authentication (recommended).
FORTIMANAGER_VERIFY_SSLNoEnable SSL verification for FortiManager connection.true
FORTIMANAGER_MAX_RETRIESNoMaximum number of retry attempts.3

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}

Tools

Functions exposed to the LLM to take actions

NameDescription
health_checkA

Check FortiManager MCP server health and connection status.

create_device_interfaceA

Create a VLAN subinterface in a device's device DB.

Reads back with get_device_interface_config; push to the FortiGate with preview_install + install_device_settings.

Args: device: Managed device name name: New interface name (e.g. "vlan15") parent: Physical/aggregate parent interface (e.g. "internal") vlanid: VLAN id (1-4094) ip: Interface address, CIDR ("192.0.2.254/24") or "address netmask" allowaccess: Administrative access to allow (e.g. ["ping", "https"]) role: Interface role: lan, wan, dmz or undefined alias: Short alias shown in the GUI vdom: VDOM the interface belongs to (default "root") description: Interface description

Returns: Creation result

update_device_interfaceA

Update fields on a device-DB interface. Only provided fields change.

Args: device: Managed device name name: Interface name to update ip: New address, CIDR or "address netmask" form allowaccess: Replacement administrative-access list role: Interface role: lan, wan, dmz or undefined alias: Short alias shown in the GUI description: Interface description vlanid: New VLAN id (1-4094)

Returns: Update result

delete_device_interfaceC

Delete an interface from a device's device DB.

Args: device: Managed device name name: Interface name to delete

Returns: Deletion result

list_device_dhcp_serversA

List DHCP servers configured in a device's device DB.

Args: device: Managed device name vdom: VDOM to read (default "root")

Returns: dict with device, vdom, count and dhcp_servers (raw device-DB objects, keyed by numeric id)

create_device_dhcp_serverA

Create a DHCP server scope in a device's device DB.

Args: device: Managed device name interface: Interface the scope serves (e.g. "vlan15") start_ip: First address of the lease range end_ip: Last address of the lease range netmask: Scope netmask (e.g. "255.255.255.0") default_gateway: Gateway handed to clients (defaults to the interface address if omitted, per FortiOS behavior) dns_servers: Up to three DNS servers to hand out; omitted means the device's system DNS is used (dns-service default) lease_time: Lease time in seconds vdom: VDOM the scope lives in (default "root")

Returns: Creation result including the new scope id

create_device_snifferA

Create a firewall sniffer definition on a managed device.

This targets the FortiGate's own persistent firewall sniffer object (device-DB, Configuration API firewall.json /firewall/sniffer) so the capture actually runs on the device's own traffic -- not FortiManager's separate, unrelated local diagnostic sniffer (/cli/global/system/sniffer, a different object that happens to share a name). Unlike FMG's local sniffer there is no packet-count cap or start/stop action: the definition captures continuously while status is "enable", same as any other persistent FortiOS config object -- disable or delete it to stop.

Args: device: Managed device name interface: Interface name traffic sniffing takes place on host: Hosts to filter for (e.g. "1.1.1.1", "2.2.2.0/24") port: Ports to sniff (e.g. "80", "1-1024") protocol: IANA protocol number as a string (e.g. "6" for TCP) vlan: VLAN(s) to sniff capture_ipv6: Include IPv6 packets (default: False) capture_non_ip: Include non-IP packets (default: False) status: "enable" or "disable" (default: "enable") logtraffic: "all", "utm", or "disable" (optional) sniffer_id: Explicit sniffer ID (0-9999); omit to let FortiOS assign the next available one vdom: VDOM the sniffer lives in (default "root")

Returns: Creation result including the new sniffer id

create_device_on_demand_snifferA

Create a bounded on-demand packet sniffer on a managed device.

Distinct FortiOS object from create_device_sniffer's firewall sniffer: this one (firewall on-demand-sniffer) is name-keyed rather than id-keyed, and bounded by max_packet_count rather than running continuously -- closer to a one-shot capture job than a standing diagnostic rule. Filters are lists (multiple hosts/ports/ protocols at once), not the single range-string form the persistent sniffer uses.

How to actually trigger the capture and retrieve packets once this definition exists is not part of FortiOS's Configuration API (this tool only stages the definition, config-CRUD like every other tool in this module) -- that would be a separate monitor-API or CLI mechanism, out of scope here.

Args: device: Managed device name name: On-demand sniffer name interface: Interface name traffic sniffing takes place on max_packet_count: Maximum packets to capture (default: 4000) hosts: IPv4/IPv6 hosts to filter for ports: Ports to filter for (each 1-65535) protocols: IANA protocol numbers to filter for (each 0-255) capture_non_ip: Include non-IP packets (default: False) vdom: VDOM the sniffer lives in (default "root")

Returns: Creation result

update_device_dhcp_serverA

Update a device-DB DHCP server scope by id. Only provided fields change.

The lease range is replaced as a whole, so start_ip and end_ip must be given together.

Args: device: Managed device name dhcp_server_id: Numeric scope id (see list_device_dhcp_servers) start_ip: New first address of the lease range end_ip: New last address of the lease range netmask: New scope netmask default_gateway: New gateway handed to clients dns_servers: Up to three DNS servers; replaces the served set lease_time: New lease time in seconds vdom: VDOM the scope lives in (default "root")

Returns: Update result

delete_device_dhcp_serverA

Delete a DHCP server scope from a device's device DB.

Args: device: Managed device name dhcp_server_id: Numeric scope id (see list_device_dhcp_servers) vdom: VDOM the scope lives in (default "root")

Returns: Deletion result

list_device_vapsB

List wireless VAPs (SSIDs) in a device's device DB.

Args: device: Managed device name vdom: VDOM to read (default "root")

Returns: dict with device, vdom, count and vaps (passphrases stripped)

create_device_vapA

Create a wireless VAP (SSID) in a device's device DB.

The VAP becomes broadcastable once added to a FortiAP profile (assign_vap_to_wtp_profile).

Args: device: Managed device name name: VAP object name ssid: SSID to broadcast security: Security mode, a FortiOS wireless-controller vap security enum value; default "wpa2-only-personal". Supported here: the personal modes ("wpa-personal", "wpa-only-personal", "wpa2-only-personal"), the SAE modes ("wpa3-sae", "wpa3-sae-transition"), "owe" and "open". Enterprise and WEP modes are refused, since neither a radius/user-group nor a WEP key is set by this tool. passphrase: Credential, 8-63 characters, required by the personal and SAE modes. It is sent as passphrase for the personal modes and as sae-password for SAE; transition mode carries both, since it runs a WPA2 leg alongside SAE. vlanid: VLAN id to map wireless clients into vdom: VDOM the VAP lives in (default "root")

Returns: Creation result with any echoed passphrase stripped

delete_device_vapA

Delete a wireless VAP from a device's device DB.

Args: device: Managed device name name: VAP object name to delete vdom: VDOM the VAP lives in (default "root")

Returns: Deletion result

assign_vap_to_wtp_profileA

Add a VAP (SSID) to a FortiAP profile's radios so it gets broadcast.

Reads the profile, appends the VAP to each selected radio's manual VAP list, and switches that radio's vap-all mode to "manual" (the mode that honors an explicit list). VAPs already present are left in place.

Args: device: Managed device name profile: FortiAP (WTP) profile name vap: VAP object name to broadcast radios: Radios to add the SSID to, from 1-3 (default [1, 2]) vdom: VDOM the profile lives in (default "root")

Returns: Update result listing the radios changed

list_device_wtp_profilesB

List FortiAP (WTP) profiles in a device's device DB.

Args: device: Managed device name vdom: VDOM to read (default "root")

Returns: dict with device, vdom, count and profiles (credential fields stripped -- see _WTP_PROFILE_SECRET_FIELDS)

get_device_wtp_profileA

Get a FortiAP (WTP) profile from a device's device DB, radios included.

Args: device: Managed device name profile: FortiAP (WTP) profile name vdom: VDOM the profile lives in (default "root")

Returns: dict with device, vdom and the stored profile object (credential fields stripped -- see _WTP_PROFILE_SECRET_FIELDS), or a not_found error if the profile does not exist

update_device_wtp_profile_radioA

Update a single radio's channel and/or channel width on a FortiAP profile.

This is a read-modify-write, like assign_vap_to_wtp_profile: the profile is read first and every other field already on the target radio (band, power, vap-all, vaps, mode, ...) is carried through unchanged, minus a couple of read-only bookkeeping keys the API injects on GET but rejects on write (see _WTP_RADIO_READONLY_KEYS). FortiManager replaces a nested radio object rather than merging into it, so writing back only channel/channel-bonding would reset the rest to defaults on the next install_device_settings.

Args: device: Managed device name profile: FortiAP (WTP) profile name radio: Radio to update, 1-3 channel: Channel(s) to pin the radio to. For radio 1/2 (2.4/5GHz) this is a single-element list, e.g. [36], to pin one channel. For radio 3 (6GHz) at a bonding width above 20MHz, FortiOS represents the wide channel as the full list of its member 20MHz sub-channels rather than a single label -- pass either the single conventional wide-channel label (e.g. [15] for 160MHz channel 15) and it is expanded to the correct member list automatically, or the explicit member list itself (e.g. [1, 5, 9, 13, 17, 21, 25, 29]). channel_bonding: Channel width/bonding, one of WTP_PROFILE_CHANNEL_BONDING_MODES ("20MHz", "40MHz", "80MHz", "160MHz") vdom: VDOM the profile lives in (default "root")

Returns: Update result

list_device_wtpsA

List managed FortiAPs registered in a device's device DB.

Args: device: Managed device name (the wireless controller / FortiGate) vdom: VDOM to read (default "root")

Returns: dict with device, vdom, count and wtps (device-DB objects keyed by wtp-id/serial, credential fields stripped -- see _WTP_SECRET_FIELDS)

get_device_wtpA

Get one managed FortiAP by wtp-id (serial number).

Args: device: Managed device name wtp_id: FortiAP serial number (the wtp-id / table mkey) vdom: VDOM the AP is registered in (default "root")

Returns: dict with device, vdom and the stored wtp object (credential fields stripped -- see _WTP_SECRET_FIELDS), or a not_found error if it does not exist

create_device_wtpA

Register a managed FortiAP in a device's device DB.

This is the wireless-controller.wtp table: the mapping from a physical AP's serial number to the profile it runs. A wtp-profile that exists in the device DB but is not referenced by any wtp entry is pruned by FortiManager on install, so registering the AP here is required before a profile-only setup survives install_device_settings.

Args: device: Managed device name wtp_id: FortiAP serial number (the wtp-id / table mkey) wtp_profile: FortiAP (WTP) profile name to assign name: Friendly display name admin: Authorization state: "discovered" (awaiting authorization), "enable" (authorized) or "disable"; default "discovered" location: Free-text location string comment: Free-text comment vdom: VDOM to register the AP in (default "root")

Returns: Creation result

update_device_wtpA

Update fields on a device-DB managed FortiAP. Only provided fields change.

Repointing wtp_profile drops a reference from the old profile. FortiManager prunes a wtp-profile that no wtp entry references on install (see create_device_wtp), so moving the last AP off a profile loses that profile on the next install_device_settings.

Args: device: Managed device name wtp_id: FortiAP serial number (the wtp-id / table mkey) wtp_profile: New FortiAP (WTP) profile name to assign name: New friendly display name admin: New authorization state: "discovered", "enable" or "disable" location: New free-text location string comment: New free-text comment vdom: VDOM the AP is registered in (default "root")

Returns: Update result

delete_device_wtpA

Delete a managed FortiAP registration from a device's device DB.

This drops the AP's reference to its wtp-profile. FortiManager prunes a wtp-profile that no wtp entry references on install (see create_device_wtp), so deleting the last AP on a profile loses that profile on the next install_device_settings.

Args: device: Managed device name wtp_id: FortiAP serial number (the wtp-id / table mkey) vdom: VDOM the AP is registered in (default "root")

Returns: Deletion result

create_device_groupA

Create a device group in FortiManager's device manager database.

Device groups organize managed devices (and other groups) for bulk operations like policy installation. This is a dvmdb object -- creation does not touch any live device.

Args: adom: ADOM name where the group will be created name: Group name (dvmdb group.name, master key) os_type: Device platform family the group targets (default: "unknown"). One of: unknown, fos, fsw, foc, fml, faz, fwb, fch, fct, log, fmg, fsa, fdd, fac, fpx, fna, ffw, fsr, fad, fdc, fap, fxt, fts, fai, fwc, fis, fed, fpa, fca, ftc, fss, fra, sim, fdt. description: Optional group description

Returns: dict: Create result with keys: - status: "success" or "error" - group: Created group name/adom - message: Status or error message

Example: >>> result = await create_device_group( ... adom="root", ... name="Branch-Firewalls", ... os_type="fos", ... description="All branch FortiGates", ... )

delete_device_groupA

Delete a device group from FortiManager.

Removes the group object itself. Member devices are not affected -- only their group membership is removed.

WARNING: This operation cannot be undone.

Args: adom: ADOM name where the group is located name: Group name to delete

Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message

Example: >>> result = await delete_device_group("root", "Branch-Firewalls")

add_device_to_groupA

Add a single device to a device group.

Non-destructive: existing group members are preserved.

Args: adom: ADOM name group: Target device group name device: Device name to add vdom: Device VDOM to add (default: "root")

Returns: dict: Add result with keys: - status: "success" or "error" - message: Status or error message

Example: >>> result = await add_device_to_group("root", "Branch-Firewalls", "FGT-Branch1")

remove_device_from_groupA

Remove a single device from a device group.

Args: adom: ADOM name group: Device group name device: Device name to remove vdom: Device VDOM to remove (default: "root")

Returns: dict: Remove result with keys: - status: "success" or "error" - message: Status or error message

Example: >>> result = await remove_device_from_group("root", "Branch-Firewalls", "FGT-Branch1")

add_devices_to_group_bulkA

Add multiple devices to a device group in one call.

Non-destructive: existing group members are preserved. All devices are added on the same VDOM; call add_device_to_group individually for devices that need a different VDOM.

Args: adom: ADOM name group: Target device group name devices: List of device names to add vdom: VDOM to add each device on (default: "root")

Returns: dict: Add result with keys: - status: "success" or "error" - requested_count: Number of devices in the request. NOT a count of memberships actually changed: FortiManager reports no per-member result, so a device that does not exist or is already a member is indistinguishable from one that was added. - message: Status or error message

Example: >>> result = await add_devices_to_group_bulk( ... "root", "Branch-Firewalls", ["FGT-Site1", "FGT-Site2"] ... )

remove_devices_from_group_bulkA

Remove multiple devices from a device group in one call.

Args: adom: ADOM name group: Device group name devices: List of device names to remove vdom: VDOM the devices were added on (default: "root")

Returns: dict: Remove result with keys: - status: "success" or "error" - requested_count: Number of devices in the request. NOT a count of memberships actually changed: FortiManager's DELETE on a non-member is a lenient no-op that still reports success, so a device that was never in the group is indistinguishable from one that was removed. - message: Status or error message

Example: >>> result = await remove_devices_from_group_bulk( ... "root", "Branch-Firewalls", ["FGT-Site1", "FGT-Site2"] ... )

add_group_to_groupA

Nest a device group inside another device group.

A group member has no VDOM (unlike a device member), which is what distinguishes a nested-group entry from a device entry in the dvmdb group membership list.

Args: adom: ADOM name parent_group: Group that will contain child_group child_group: Group to nest inside parent_group

Returns: dict: Add result with keys: - status: "success" or "error" - message: Status or error message

Example: >>> result = await add_group_to_group("root", "All-Branches", "Branch-Firewalls")

remove_group_from_groupB

Remove a nested group from its parent device group.

Args: adom: ADOM name parent_group: Group that currently contains child_group child_group: Nested group to remove from parent_group

Returns: dict: Remove result with keys: - status: "success" or "error" - message: Status or error message

Example: >>> result = await remove_group_from_group("root", "All-Branches", "Branch-Firewalls")

list_device_vdomsA

List VDOMs for a specific device.

Virtual Domains (VDOMs) are independent virtual instances within a FortiGate device, each with its own configuration.

Args: device: Device name adom: ADOM name (default: from DEFAULT_ADOM env var, or "root")

Returns: dict: VDOM list with keys: - status: "success" or "error" - count: Number of VDOMs - vdoms: List of VDOM objects with name, status, etc. - message: Error message if failed

Example: >>> result = await list_device_vdoms("FGT-HQ", "root") >>> for vdom in result['vdoms']: ... print(f"VDOM: {vdom['name']}")

get_device_statusA

Get device status including connection and config sync status.

Returns status information for all devices or a specific device:

  • Connection status (up/down)

  • Config sync status (in_sync/out_of_sync)

  • DB status (modified/no_changes)

  • Device status (installed/checkedin/etc.)

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") device: Specific device name (optional, returns all if not specified)

Returns: dict: Device status with keys: - status: "success" or "error" - count: Number of devices - devices: List of device status objects - message: Error message if failed

Example: >>> # Get all device status >>> result = await get_device_status("root") >>> for dev in result['devices']: ... print(f"{dev['name']}: {dev['conn_status_str']}")

>>> # Get specific device status
>>> result = await get_device_status("root", "FGT-HQ")
search_devicesA

Search for devices with filters.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by device name (partial match) platform_filter: Filter by platform type (e.g., "FortiGate-VM") os_version_filter: Filter by OS version as major[.minor[.patch]] (e.g., "7", "7.4" or "7.4.12"; a leading "v" is accepted) connection_status: Filter by status ("up" or "down")

Returns: dict: Search results with keys: - status: "success" or "error" - count: Number of matching devices - devices: List of matching device objects - message: Error message if failed

Example: >>> # Find all FortiGate VMs >>> result = await search_devices(platform_filter="FortiGate-VM")

>>> # Find offline devices
>>> result = await search_devices(connection_status="down")

>>> # Find devices by name pattern
>>> result = await search_devices(name_filter="Branch")
add_deviceA

Add a new device to FortiManager.

Registers a device with FortiManager for central management. Can add either a real device (with IP) or a model device (with serial number).

Args: adom: ADOM name where device will be added name: Device display name ip: Device IP address (for real device connection) serial_number: Device serial number (for model device or validation) admin_user: Admin username for device connection admin_pass: Admin password for device connection description: Device description platform: Platform type (default: "FortiGate-VM64") mgmt_mode: Management mode - "fmg" (FortiManager only), or "fmgfaz" (both) flags: Additional flags like ["create_task"]

Returns: dict: Add result with keys: - status: "success" or "error" - device: Added device information - task_id: Task ID if run as background task - message: Error message if failed

Example: >>> # Add a FortiGate with IP >>> result = await add_device( ... adom="root", ... name="FGT-Branch1", ... ip="192.168.1.1", ... admin_user="admin", ... admin_pass="password123" ... )

>>> # Add a model device (offline provisioning)
>>> result = await add_device(
...     adom="root",
...     name="FGT-Lab",
...     serial_number="FGVM020000123456"
... )
add_model_deviceA

Add a model device for offline provisioning.

Model devices allow pre-configuring policies and settings before the actual device connects to FortiManager. Useful for zero-touch provisioning workflows.

Args: adom: ADOM name where device will be added name: Device display name serial_number: Device serial number (e.g., "FGVM020000123456") platform: Platform type (default: "FortiGate-VM64") os_version: FortiOS version (default: "7.0") description: Device description

Returns: dict: Add result with keys: - status: "success" or "error" - device: Added device information - message: Status or error message

Example: >>> result = await add_model_device( ... adom="root", ... name="FGT-NewBranch", ... serial_number="FGVM02TM12345678", ... platform="FortiGate-60F", ... os_version="7.4" ... )

delete_deviceA

Delete a device from FortiManager.

Removes a device registration. Does not affect the actual device or its configuration - only removes it from FortiManager management.

WARNING: This operation cannot be undone.

Args: adom: ADOM name where device is located device: Device name to delete flags: Additional flags like ["create_task"]

Returns: dict: Delete result with keys: - status: "success" or "error" - task_id: Task ID if run as background task - message: Status or error message

Example: >>> result = await delete_device("root", "FGT-OldBranch") >>> if result['status'] == 'success': ... print("Device removed from FortiManager")

add_devices_bulkA

Add multiple devices to FortiManager in bulk.

Registers multiple devices at once for efficiency.

Args: adom: ADOM name where devices will be added devices: List of device configurations. Each device dict can contain: - name: Device display name (required) - ip: Device IP address - sn: Serial number - adm_usr: Admin username - adm_pass: Admin password - desc: Description - platform_str: Platform type - os_ver: OS version flags: Additional flags like ["create_task"]

Returns: dict: Bulk add result with keys: - status: "success" or "error" - requested_count: Number of devices in the request. NOT a count of devices actually added: the add runs as a task, so per-device results only exist once it completes. - task_id: Task ID if run as background task - message: Error message if failed

Example: >>> devices = [ ... {"name": "FGT-Site1", "ip": "10.0.1.1", "adm_usr": "admin", "adm_pass": "pass1"}, ... {"name": "FGT-Site2", "ip": "10.0.2.1", "adm_usr": "admin", "adm_pass": "pass2"}, ... ] >>> result = await add_devices_bulk("root", devices)

delete_devices_bulkA

Delete multiple devices from FortiManager in bulk.

Removes multiple device registrations at once.

WARNING: This operation cannot be undone.

Args: adom: ADOM name where devices are located devices: List of device names to delete flags: Additional flags like ["create_task"]

Returns: dict: Bulk delete result with keys: - status: "success" or "error" - requested_count: Number of devices in the request. NOT a count of devices actually deleted: the delete runs as a task, so per-device results only exist once it completes. - task_id: Task ID if run as background task - message: Error message if failed

Example: >>> result = await delete_devices_bulk("root", ["FGT-Old1", "FGT-Old2"])

update_deviceA

Update device properties.

Modify device metadata like description and location.

Args: adom: ADOM name device: Device name description: New device description latitude: GPS latitude for device location longitude: GPS longitude for device location

Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message

Example: >>> result = await update_device( ... adom="root", ... device="FGT-HQ", ... description="Main headquarters firewall", ... latitude=37.7749, ... longitude=-122.4194 ... )

reload_device_listA

Reload the device list from FortiManager database.

Forces FortiManager to refresh its device cache. Useful after direct database changes or if device list appears stale.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root")

Returns: dict: Reload result with keys: - status: "success" or "error" - message: Status or error message

get_device_realtime_statusA

Get real-time status from a managed device.

Queries the actual device through FortiManager proxy to get current system status including CPU, memory, and uptime.

Args: adom: ADOM name device: Device name

Returns: dict: Real-time status with keys: - status: "success" or "error" - data: Device status from FortiGate API - message: Error message if failed

Example: >>> result = await get_device_realtime_status("root", "FGT-HQ") >>> print(f"Uptime: {result['data'].get('uptime')}")

get_device_interfacesA

Get interface information from a managed device.

Queries the actual device through FortiManager proxy to get current interface configuration and status.

Args: adom: ADOM name device: Device name

Returns: dict: Interface information with keys: - status: "success" or "error" - data: Interface list from FortiGate API - message: Error message if failed

Example: >>> result = await get_device_interfaces("root", "FGT-HQ") >>> for iface in result['data']: ... print(f"{iface['name']}: {iface.get('ip')}")

get_device_interface_configA

Read a device's interface CONFIG objects from FortiManager's device DB.

Unlike get_device_interfaces (which proxies the live FortiGate monitor API and cannot filter by VLAN), this reads the interface configuration FortiManager keeps in its device database and supports server-side filtering by VLAN id and/or exact interface name. Use it to map a queried IP / client to the VLAN, interface and physical port it belongs to.

Args: device: Managed device name (e.g. "FGT-HQ"). Defaults to DEFAULT_DEVICE when omitted; required if that is not configured. vlanids: Optional list of VLAN ids to filter on (matches vlanid in) name: Optional exact interface name to filter on (e.g. "port1")

Returns: dict with keys: - device - interfaces: raw device-DB interface config (list or dict), credential fields stripped -- see _INTERFACE_SECRET_FIELDS - summary: condensed per-interface list (name/alias/vlanid/ip/...) - interface_count: number of summarized interfaces - error / error_code: on failure

get_device_client_locationA

Locate a client on a device (Asset Identity Center): which FortiAP / FortiSwitch + VLAN it is connected through.

Proxies the device's detected-device inventory (/api/v2/monitor/user/device/query) -- the data behind the FortiManager "Asset Identity Center" view -- and answers "where is this client connected". Each match resolves to the access point / switch + port and VLAN the client is on, plus hostname, vendor/OS, online state, DHCP lease and vuln count.

Filter to one client by ip, mac or hostname (each provided filter narrows; a hostname match is case-insensitive substring). With no filter, the full detected-client inventory is returned (summarized).

Args: adom: ADOM name (defaults to DEFAULT_ADOM / "root" when omitted) device: Managed device name (e.g. "FGT-HQ"). Defaults to DEFAULT_DEVICE when omitted; required if that is not configured. ip: Client IPv4 address to locate (exact match) mac: Client MAC address to locate (case-insensitive) hostname: Client hostname to locate (case-insensitive substring)

Returns: dict with keys: - adom, device - query: the filter used (or None for full inventory) - match_count - clients: summarized detected-client records (ap/switch/port/vlan/...) - error / error_code: on failure

get_firmware_upgrade_pathA

Preview the multi-step firmware upgrade path to a target version.

Non-destructive: internally forces flags="f_preview" so FortiManager returns the intermediate versions it would step the device through without starting an upgrade. Some FortiOS upgrades cannot jump directly to the target release and must pass through intermediate builds first; this shows that path before you call upgrade_device_firmware.

Args: device: Managed device name target_version: Target firmware version, e.g. "7.2.9-b1688" adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") vdom: VDOM to scope the request to (optional)

Returns: dict: Upgrade path with keys: - status: "success" or "error" - upgrade_path: List of version strings FMG would step through - message: Error message if failed

Example: >>> result = await get_firmware_upgrade_path("FGT-HQ", "7.2.9-b1688") >>> print(result["upgrade_path"]) ['6.0.8-303', '6.2.4-1112', '7.2.9-1688']

list_available_firmwareA

List firmware versions available for a platform.

Combines versions on FortiGuard (FDS) servers with any versions imported by a FortiManager administrator -- this is the device-reported catalog of what CAN be installed, not just what's already downloaded (see list_firmware_images for that).

Args: platform: Platform string, e.g. "FortiGate-VM64-KVM" (omit to get firmware for all platforms) product: Product code, e.g. "FGT", "FMG" (optional)

Returns: dict: Firmware catalog with keys: - status: "success" or "error" - version_list: List of {platform, product, versions: [...]} - message: Error message if failed

Example: >>> result = await list_available_firmware(platform="FortiGate-VM64-KVM") >>> for entry in result["version_list"]: ... print(entry["platform"], len(entry["versions"]))

list_firmware_imagesA

List firmware image files stored on the FortiManager's local disk.

These are the files already imported by an administrator and/or downloaded from FortiGuard -- a subset of what list_available_firmware reports, scoped to what FortiManager could push without first fetching it from FortiGuard.

Args: system: Product code to filter by, e.g. "FMG", "FGT" (optional; FortiManager returns every stored image when omitted or when there's no match for the given code)

Returns: dict: Local image list with keys: - status: "success" or "error" - image_list: List of {platform, product, version, build, date, image_path, image_size} - message: Error message if failed

upgrade_device_firmwareA

Trigger a firmware upgrade on a managed device.

Device-affecting: this reboots the device onto the new firmware. Asynchronous -- FortiManager runs the upgrade as a background task (create_task="enable" is always sent) and this returns immediately with a task_id. Poll it with get_task/wait_for_task rather than assuming success from this call's return.

Strongly recommended to call get_firmware_upgrade_path first: FMG may need to step the device through intermediate versions to reach the target, and this tool upgrades straight to target_version without walking that path itself.

Safety: governed by FMG_FIRMWARE_SAFETY (default "strict"), same pattern as trigger_fmg_restore -- refused unless confirm=True is also passed. A firmware upgrade reboots a real managed device onto new code; there is no dry-run and no content to screen, so the gate is a plain explicit confirmation. This is unrelated to spawn_guarded below, which only bounds concurrency, not whether the call is allowed to happen at all. Set FMG_FIRMWARE_SAFETY=disabled to remove the gate.

Args: device: Managed device name target_version: Target firmware version, e.g. "7.2.9-b1688" adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") vdom: VDOM to scope the request to (optional) flags: One upgrade flag (optional): "f_boot_alt_partition" (boot from alternate partition after upgrade), "f_skip_retrieve" (don't retrieve device config after upgrade), "f_skip_multi_steps" (skip the multi-step upgrade process), "f_skip_fortiguard_img" (let the device download firmware from FortiGuard itself) schedule_time: Schedule the upgrade for a future time instead of running it immediately (optional; FMG-native time format) confirm: Must be True to proceed under FMG_FIRMWARE_SAFETY=strict (default). Ignored when FMG_FIRMWARE_SAFETY=disabled.

Returns: dict: Trigger result with keys: - status: "success" or "error" - task_id: Task ID for monitoring (if successful) - message: Status or error message

Example: >>> path = await get_firmware_upgrade_path("FGT-HQ", "7.2.9-b1688") >>> result = await upgrade_device_firmware( ... "FGT-HQ", "7.2.9-b1688", confirm=True ... ) >>> if result["status"] == "success": ... await wait_for_task(result["task_id"], timeout=1800)

get_firmware_upgrade_reportA

Get the firmware upgrade report for a device under a named profile.

Reports per-step upgrade results (health checks, package status, timestamps) for an upgrade run tracked under profile_name. The How-To Guide separately lists a "get upgrade history" question but marks it TBD with no confirmed distinct endpoint -- it speculates the same URL this wraps, so no separate history tool is provided; this is the one confirmed endpoint for both purposes.

profile_name must be a firmware upgrade profile FortiManager already knows about (e.g. one created through the GUI's Firmware Upgrade wizard, or the auto-created Firmware Template "key" a /um/image/upgrade/ext-style upgrade returns -- not something this module generates on its own).

Args: device: Managed device name profile_name: Firmware upgrade profile name to report on adom: ADOM name (default: from DEFAULT_ADOM env var, or "root")

Returns: dict: Upgrade report with keys: - status: "success" or "error" - report: Raw report data (nested per-device task/step details) - message: Error message if failed

trigger_fmg_backupA

Trigger a FortiManager system backup to a remote server.

Starts an asynchronous backup of the FortiManager's own configuration to an external FTP/SCP/SFTP/TFTP server. This is a FortiManager-itself operation -- it does not touch any managed device. Use wait_for_task() or get_task() (system_tools.py) to monitor completion; once done, the backup file is on the remote server at the given filename.

Args: service: Transfer protocol - "ftp", "scp", "sftp", or "tftp" server: Remote server hostname or IP address filename: Destination path and file name on the remote server (e.g. "tmp/fmg_backup.dat") username: Login username for the remote server (required for ftp/ scp/sftp; optional) userpasswd: Login password for the remote server (optional) port: Remote server port (optional; protocol default used if unset) passwd: Password to encrypt the backup file with (optional; FMG 7.0.11+/7.2.5+/7.4.2+ no longer allow an unencrypted backup)

Returns: dict: Backup result with keys: - status: "success" or "error" - task_id: Task ID for monitoring (if returned) - message: Status or error message

Example: >>> result = await trigger_fmg_backup( ... service="ftp", ... server="10.210.35.207", ... filename="tmp/fmg_backup.dat", ... username="tiger", ... userpasswd="fortinet", ... passwd="fortinet", ... ) >>> if result["status"] == "success": ... await wait_for_task(result["task_id"])

trigger_fmg_restoreA

Restore the FortiManager system from a backup on a remote server.

Restores the FortiManager's own configuration from a backup file previously uploaded to an external FTP/SCP/SFTP/TFTP server (see trigger_fmg_backup). This is a disruptive, FortiManager-itself operation -- it does not touch any managed device, but it does REPLACE FMG's ENTIRE configuration and will interrupt the FMG service.

Safety: governed by FMG_RESTORE_SAFETY (default "strict"). Under strict, this call is refused unless confirm=True is also passed -- unlike install_package (gated by a verified preview) or firewall policy writes (gated by content screening), there is no dry-run or content check possible for a restore, so the gate is a plain explicit confirmation instead. Set FMG_RESTORE_SAFETY=disabled to remove the gate entirely.

Args: service: Transfer protocol - "ftp", "scp", "sftp", or "tftp" server: Remote server hostname or IP address filename: Path and file name of the backup on the remote server username: Login username for the remote server (optional) userpasswd: Login password for the remote server (optional) port: Remote server port (optional; protocol default used if unset) passwd: Password to decrypt the backup file, if it was encrypted (optional) confirm: Must be True to proceed under FMG_RESTORE_SAFETY=strict (default). Ignored when FMG_RESTORE_SAFETY=disabled.

Returns: dict: Restore result with keys: - status: "success" or "error" - message: Status or error message

Example: >>> result = await trigger_fmg_restore( ... service="ftp", ... server="10.210.35.207", ... filename="tmp/fmg_backup.dat", ... username="tiger", ... userpasswd="fortinet", ... confirm=True, ... )

list_packet_capturesA

List existing FortiManager packet capture definitions.

Returns the packet capture (sniffer) definitions configured on the FortiManager itself, for traffic originating or destined to the FMG. These are configuration records, not live capture results -- use get_packet_capture_status() for running/packet-count state.

Returns: dict: Capture list with keys: - status: "success" or "error" - count: Number of capture definitions - captures: List of capture objects (id, interface, host, port, protocol, vlan, ipv6, non-ip, max-packet-count) - message: Error message if failed

Example: >>> result = await list_packet_captures() >>> for cap in result["captures"]: ... print(f"{cap['id']}: {cap['interface']}")

add_packet_captureA

Add a new FortiManager packet capture definition.

Creates a packet capture (sniffer) definition for traffic originating or destined to the FortiManager itself. The definition is created but not started -- call start_packet_capture() with the returned capture_id to begin capturing.

Args: interface: Interface to capture on (e.g. "port1") host: Filter by host address (optional, matches any host if unset) port: Filter by port or port range as a string, e.g. "80" or "1-1024" (optional) protocol: Filter by IP protocol number as a string, e.g. "6" for TCP (optional) vlan: Filter by VLAN ID as a string (optional) capture_ipv6: Include IPv6 traffic (default: False) capture_non_ip: Include non-IP traffic (default: False) max_packet_count: Maximum number of packets to capture (default: 4000, matching the FMG default)

Returns: dict: Create result with keys: - status: "success" or "error" - capture_id: ID of the created capture definition (FMG auto-assigns this) - message: Status or error message

Example: >>> result = await add_packet_capture(interface="port8", max_packet_count=300) >>> if result["status"] == "success": ... await start_packet_capture(result["capture_id"])

start_packet_captureB

Start a packet capture from an existing definition.

Args: capture_id: ID of an existing packet capture definition (see list_packet_captures() or add_packet_capture())

Returns: dict: Start result with keys: - status: "success" or "error" - message: Status or error message

Example: >>> result = await start_packet_capture(1)

stop_packet_captureB

Stop a running packet capture.

Args: capture_id: ID of an existing packet capture definition

Returns: dict: Stop result with keys: - status: "success" or "error" - message: Status or error message

Example: >>> result = await stop_packet_capture(1)

get_packet_capture_statusA

Get the running/packet-count status of packet captures.

FortiManager reports capture progress for all definitions in one call; when capture_id is given this filters the response down to that one definition.

Args: capture_id: ID of a specific capture definition to filter to (optional; returns all captures' status if unset)

Returns: dict: Status result with keys: - status: "success" or "error" - count: Number of capture statuses returned - captures: List of {id, max_packets, packets, running} objects - message: Error message if failed

Example: >>> result = await get_packet_capture_status(capture_id=2) >>> print(result["captures"][0]["running"]) # 1 if still running

get_fmg_licenseA

Get the FortiManager license/contract status.

Returns the FortiManager's own license contract details (support entitlements, expiry dates, serial number) -- not managed-device licensing.

Returns: dict: License result with keys: - status: "success" or "error" - license: License data (contract list, count) - message: Error message if failed

Example: >>> result = await get_fmg_license() >>> print(result["license"]["contract"][0]["serial"])

delete_taskA

Delete a task record from FortiManager's task list.

Deletes a completed task's record, or cancels/stops a still-running one (per the How-To guide: "Deleting a task could be used to delete a completed task or cancelling/stopping a running task."). Complements list_tasks/get_task/wait_for_task in system_tools.py.

Args: task_id: Task ID number

Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message

Example: >>> result = await delete_task(11111)

list_addressesA

List firewall address objects in an ADOM.

Address objects define network entities (hosts, subnets, FQDNs) used in firewall policies.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match) type_filter: Filter by type ("ipmask", "fqdn", "iprange", "wildcard")

Returns: dict: Address list with keys: - status: "success" or "error" - count: Number of addresses - addresses: List of address objects - message: Error message if failed

Example: >>> # List all addresses >>> result = await list_addresses("root")

>>> # Find FQDN addresses
>>> result = await list_addresses("root", type_filter="fqdn")
get_addressA

Get detailed information about a firewall address object.

Args: adom: ADOM name name: Address object name

Returns: dict: Address details with keys: - status: "success" or "error" - address: Full address configuration - message: Error message if failed

create_address_subnetA

Create a subnet/network address object.

Args: adom: ADOM name name: Address object name subnet: IP/netmask (e.g., "10.0.0.0/24" or "10.0.0.0 255.255.255.0") comment: Optional comment

Returns: dict: Create result with keys: - status: "success" or "error" - name: Created address name - message: Status or error message

Example: >>> result = await create_address_subnet( ... adom="root", ... name="LAN-Subnet", ... subnet="192.168.1.0/24", ... comment="Local network" ... )

create_address_hostA

Create a host address object (single IP).

Args: adom: ADOM name name: Address object name ip: Host IP address (e.g., "10.0.0.100") comment: Optional comment

Returns: dict: Create result with keys: - status: "success" or "error" - name: Created address name - message: Status or error message

Example: >>> result = await create_address_host( ... adom="root", ... name="WebServer", ... ip="192.168.1.100", ... comment="Production web server" ... )

create_address_fqdnA

Create an FQDN (Fully Qualified Domain Name) address object.

FQDN addresses are resolved dynamically by FortiGate.

Args: adom: ADOM name name: Address object name fqdn: Domain name (e.g., "www.example.com") comment: Optional comment

Returns: dict: Create result with keys: - status: "success" or "error" - name: Created address name - message: Status or error message

Example: >>> result = await create_address_fqdn( ... adom="root", ... name="Google-DNS", ... fqdn="dns.google.com" ... )

create_address_rangeA

Create an IP range address object.

Args: adom: ADOM name name: Address object name start_ip: Start IP address end_ip: End IP address comment: Optional comment

Returns: dict: Create result with keys: - status: "success" or "error" - name: Created address name - message: Status or error message

Example: >>> result = await create_address_range( ... adom="root", ... name="DHCP-Pool", ... start_ip="192.168.1.100", ... end_ip="192.168.1.200" ... )

update_addressA

Update an existing address object.

Only specified fields will be updated.

Args: adom: ADOM name name: Current address name new_name: New name (optional) subnet: New subnet for ipmask type (optional) fqdn: New FQDN for fqdn type (optional) comment: New comment (optional)

Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message

delete_addressA

Delete a firewall address object.

WARNING: This will fail if the address is in use by policies or groups.

Args: adom: ADOM name name: Address object name to delete

Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message

list_address_groupsA

List firewall address groups in an ADOM.

Address groups contain multiple address objects for easier policy management.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match)

Returns: dict: Group list with keys: - status: "success" or "error" - count: Number of groups - groups: List of address group objects - message: Error message if failed

get_address_groupB

Get detailed information about an address group.

Args: adom: ADOM name name: Address group name

Returns: dict: Group details with keys: - status: "success" or "error" - group: Full group configuration including members - message: Error message if failed

create_address_groupA

Create an address group.

Args: adom: ADOM name name: Group name members: List of address object names to include comment: Optional comment

Returns: dict: Create result with keys: - status: "success" or "error" - name: Created group name - message: Status or error message

Example: >>> result = await create_address_group( ... adom="root", ... name="Web-Servers", ... members=["WebServer1", "WebServer2", "WebServer3"], ... comment="Production web servers" ... )

update_address_groupB

Update an address group.

Args: adom: ADOM name name: Group name members: New member list (replaces existing) comment: New comment

Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message

delete_address_groupA

Delete an address group.

WARNING: This will fail if the group is in use by policies.

Args: adom: ADOM name name: Group name to delete

Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message

list_servicesA

List custom service objects in an ADOM.

Service objects define network protocols and ports used in policies.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match) protocol_filter: Filter by protocol ("TCP/UDP/SCTP", "ICMP", "IP")

Returns: dict: Service list with keys: - status: "success" or "error" - count: Number of services - services: List of service objects - message: Error message if failed

get_serviceC

Get detailed information about a service object.

Args: adom: ADOM name name: Service object name

Returns: dict: Service details with keys: - status: "success" or "error" - service: Full service configuration - message: Error message if failed

create_service_tcp_udpA

Create a TCP/UDP/SCTP/UDP-Lite service object.

Args: adom: ADOM name name: Service object name tcp_portrange: TCP port range (e.g., "80", "8080-8090", "80 443 8080") udp_portrange: UDP port range (e.g., "53", "500-502") sctp_portrange: SCTP port range (e.g., "3868", "2905-2907") udplite_portrange: UDP-Lite port range (e.g., "1234") comment: Optional comment

Returns: dict: Create result with keys: - status: "success" or "error" - name: Created service name - message: Status or error message

Example: >>> # Create HTTP/HTTPS service >>> result = await create_service_tcp_udp( ... adom="root", ... name="Custom-Web", ... tcp_portrange="80 443 8080", ... comment="Custom web ports" ... )

>>> # Create DNS service
>>> result = await create_service_tcp_udp(
...     adom="root",
...     name="Custom-DNS",
...     tcp_portrange="53",
...     udp_portrange="53"
... )

>>> # Create SCTP service (Diameter)
>>> result = await create_service_tcp_udp(
...     adom="root",
...     name="Diameter",
...     sctp_portrange="3868",
...     comment="Diameter protocol"
... )
create_service_icmpA

Create an ICMP service object.

Args: adom: ADOM name name: Service object name icmp_type: ICMP type (0-255, optional for all types) icmp_code: ICMP code (0-255, optional) comment: Optional comment

Returns: dict: Create result with keys: - status: "success" or "error" - name: Created service name - message: Status or error message

Example: >>> # Create ping service (ICMP echo request) >>> result = await create_service_icmp( ... adom="root", ... name="Custom-Ping", ... icmp_type=8, ... comment="ICMP Echo Request" ... )

update_serviceB

Update a service object.

Args: adom: ADOM name name: Service name tcp_portrange: New TCP port range udp_portrange: New UDP port range comment: New comment

Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message

delete_serviceA

Delete a service object.

WARNING: This will fail if the service is in use by policies.

Args: adom: ADOM name name: Service name to delete

Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message

list_service_groupsA

List service groups in an ADOM.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match)

Returns: dict: Group list with keys: - status: "success" or "error" - count: Number of groups - groups: List of service group objects - message: Error message if failed

get_service_groupA

Get detailed information about a service group.

Args: adom: ADOM name name: Service group name

Returns: dict: Group details with keys: - status: "success" or "error" - group: Full group configuration including members - message: Error message if failed

create_service_groupA

Create a service group.

Args: adom: ADOM name name: Group name members: List of service names to include comment: Optional comment

Returns: dict: Create result with keys: - status: "success" or "error" - name: Created group name - message: Status or error message

Example: >>> result = await create_service_group( ... adom="root", ... name="Web-Services", ... members=["HTTP", "HTTPS", "DNS"], ... comment="Common web services" ... )

update_service_groupB

Update a service group.

Args: adom: ADOM name name: Group name members: New member list (replaces existing) comment: New comment

Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message

delete_service_groupA

Delete a service group.

WARNING: This will fail if the group is in use by policies.

Args: adom: ADOM name name: Group name to delete

Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message

search_objectsA

Search across all firewall objects by name.

Searches addresses, address groups, services, and service groups for objects matching the search term.

Args: adom: ADOM name search_term: Search term (partial match)

Returns: dict: Search results with keys: - status: "success" or "error" - addresses: Matching address objects - address_groups: Matching address groups - services: Matching service objects - service_groups: Matching service groups - total_count: Total matches - message: Error message if failed

Example: >>> result = await search_objects("root", "web")

find_object_usageA

Find everywhere an ADOM object is referenced (where-used).

Runs FortiManager's cache-daemon where-used search: policies, groups, and other objects that reference the given object. This is a usage-graph lookup, distinct from object_tools.search_objects (which matches objects BY NAME, not by reference).

Args: adom: ADOM name (use "global" to search a Global ADOM object) object_name: Name of the object to trace usage for object_type: One of "address", "address_group", "service", "service_group" (default: "address") timeout: Maximum seconds to wait for the search to complete (default: 60, capped at 300) poll_interval: Seconds between progress checks (default: 2, clamped to 1-30)

Returns: dict: Usage result with keys: - status: "success" or "error" - object_name: The object searched for - object_type: The object type searched - total_matches: Number of referencing locations - used_in: List of where-used entries, each with the referencing object's root ADOM/package and the matching attr/mapping_name/mkey detail FortiManager returned - message: Error message if failed

Example: >>> result = await find_object_usage("root", "WebServer") >>> for entry in result["used_in"]: ... print(entry["root"], entry["data"])

find_duplicate_objectsA

Find objects with identical content configured under different names.

Runs FortiManager's "find duplicates" option against every object of the given type in the ADOM. FortiManager compares the requested fields across all objects of that type and adds a "duplicate entries" list to each object that shares its content with at least one other object -- the field itself, not something computed client-side.

Args: adom: ADOM name object_type: One of "address", "address_group", "service", "service_group" (default: "address") fields: Object fields to compare for duplication (e.g. ["name", "type", "subnet"] for addresses). Omit to let FortiManager use its default field set for the type.

Returns: dict: Duplicate scan result with keys: - status: "success" or "error" - object_type: The object type scanned - objects: Full per-object list FortiManager returned (each entry has a "duplicate entries" list when it has a match) - duplicate_count: Number of objects that have at least one duplicate entry - message: Error message if failed

Example: >>> result = await find_duplicate_objects("root", object_type="address") >>> for obj in result["objects"]: ... if obj.get("duplicate entries"): ... print(obj["name"], "duplicates:", obj["duplicate entries"])

policy_lookupA

Simulate a firewall policy lookup for a traffic 5-tuple against a managed device.

Proxies FortiGate's policy-lookup monitor API through FortiManager to ask "which policy would match this traffic right now" -- the same operation the FortiManager UI's Policy Lookup dialog performs. This queries the device's currently installed/active policy; it does not take a policy-package argument (the documented endpoint has none -- see module docstring).

Args: adom: ADOM name device: Managed device name (e.g. "FGT-HQ"). Accepts the "device[vdom]" bracket suffix used elsewhere in this codebase to target a specific VDOM. dest: Destination IPv4 address to test (e.g. "8.8.8.8") protocol: IP protocol number to test -- 6=TCP, 17=UDP, 1=ICMP, 47=GRE, 50=ESP, 51=AH, 58=ICMPv6, 132=SCTP. Sent to the device as both the protocol and protocol_number query fields, mirroring the one documented request/response pair in the How-To Guide where both fields carried the same value; the guide does not show a case where they differ. srcintf: Ingress interface name the traffic would arrive on (e.g. "internal"). Optional -- omitted from the query when not given, matching the FortiGate monitor API default. sourceip: Source IPv4 address to test (optional; omitted from the query when not given -- the How-To Guide's own example queried with this field empty) sourceport: Source port to test (optional; omitted when not given) destport: Destination port to test (optional; 0 -- "any port", per the How-To Guide example -- when not given) package: Informational only, NOT sent to the device. The FortiGate policy-lookup endpoint has no package selector; it always reflects whatever package is currently installed. Pass this if you want it echoed back (as package_hint) in the result to cross-check against what you expect to be installed.

Returns: dict: Lookup result with keys: - status: "success" or "error" - adom, device - package_hint: the caller-supplied package argument, echoed back unverified -- NOT confirmation that this package is what actually matched (named package_hint, not package, so it can't be misread as verified match data) - query: the traffic-tuple fields actually sent to the device - matched: bool from the device's results.success field when present, else None (the How-To Guide only documents the no-match case: {"success": false}; a positive match's full field set is not documented here, so it is not reshaped -- see result) - result: the raw results payload returned by the device, passed through unchanged - message: Error message if failed

Example: >>> result = await policy_lookup( ... adom="root", ... device="branch1_fgt", ... srcintf="vl_lan", ... protocol=6, ... dest="8.8.8.8", ... ) >>> result["matched"] False

create_packageA

Create a new policy package.

Policy packages contain firewall policies, security profiles, and other configurations to be deployed to managed devices.

Args: adom: ADOM name name: Package name ngfw_mode: NGFW mode - "profile-based" (default) or "policy-based" central_nat: Enable central NAT (default: False)

Returns: dict: Create result with keys: - status: "success" or "error" - package: Created package name - message: Status or error message

Example: >>> result = await create_package( ... adom="root", ... name="Branch-Policy", ... ngfw_mode="profile-based" ... )

delete_packageA

Delete a policy package.

WARNING: This will delete the package and all its policies. This operation cannot be undone.

Args: adom: ADOM name package: Package name to delete

Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message

clone_packageA

Clone a policy package.

Creates a copy of an existing package with all its policies.

Args: adom: ADOM name package: Source package name new_name: Name for the cloned package

Returns: dict: Clone result with keys: - status: "success" or "error" - package: New package name - message: Status or error message

Example: >>> result = await clone_package( ... adom="root", ... package="default", ... new_name="default-copy" ... )

assign_packageA

Assign a policy package to devices.

Associates a policy package with specific devices/VDOMs. The package can then be installed to these devices.

Args: adom: ADOM name package: Package name devices: Target devices [{"name": "FGT1", "vdom": "root"}, ...]

Returns: dict: Assignment result with keys: - status: "success" or "error" - message: Status or error message

Example: >>> result = await assign_package( ... adom="root", ... package="Branch-Policy", ... devices=[ ... {"name": "FGT-Branch1", "vdom": "root"}, ... {"name": "FGT-Branch2", "vdom": "root"} ... ] ... )

list_firewall_policiesA

List firewall policies in a policy package.

Args: adom: ADOM name package: Policy package name fields: Specific fields to return (optional) limit: Maximum number of policies to return (optional) offset: Starting position for pagination (default: 0)

Returns: dict: Policy list with keys: - status: "success" or "error" - count: Number of policies returned - total: Total number of policies in package - policies: List of policy objects - message: Error message if failed

Example: >>> # Get all policies >>> result = await list_firewall_policies("root", "default")

>>> # Get first 10 policies with specific fields
>>> result = await list_firewall_policies(
...     adom="root",
...     package="default",
...     fields=["policyid", "name", "srcaddr", "dstaddr", "action"],
...     limit=10
... )
get_firewall_policyA

Get detailed information about a specific firewall policy.

Args: adom: ADOM name package: Policy package name policyid: Policy ID number

Returns: dict: Policy details with keys: - status: "success" or "error" - policy: Full policy configuration - message: Error message if failed

Example: >>> result = await get_firewall_policy("root", "default", 1) >>> print(f"Policy name: {result['policy']['name']}")

create_firewall_policyA

Create a new firewall policy.

Creates a firewall policy in the specified policy package. The policy won't be active until the package is installed to devices.

Args: adom: ADOM name package: Policy package name name: Policy name srcintf: Source interfaces (e.g., ["internal"]) dstintf: Destination interfaces (e.g., ["wan1"]) srcaddr: Source addresses (e.g., ["all"]) dstaddr: Destination addresses (e.g., ["all"]) service: Services (e.g., ["ALL", "HTTP", "HTTPS"]) action: Policy action - "accept" or "deny" (default: "accept") schedule: Schedule object name (default: "always") nat: Enable NAT (default: False) logtraffic: Log mode - "all", "utm", or "disable" (default: "utm") status: Policy status - "enable" or "disable" (default: "enable") comments: Policy comments (optional) policyid: Specific policy ID (optional, auto-assigned if not set) srcaddr_negate: Match all sources EXCEPT srcaddr (optional; omitted from the payload when not set, leaving the FortiManager default) dstaddr_negate: Match all destinations EXCEPT dstaddr (optional) service_negate: Match all services EXCEPT service (optional) utm_status: Enable/disable security profile inspection on this policy (optional). Required (True) for any of the profile fields below to actually apply. av_profile: Antivirus profile name, e.g. "default" (optional) ips_sensor: IPS sensor name, e.g. "default" (optional) webfilter_profile: Web filter profile name (optional) dnsfilter_profile: DNS filter profile name (optional) application_list: Application control list name (optional) file_filter_profile: File filter profile name (optional) ssl_ssh_profile: SSL/SSH inspection profile name, e.g. "certificate-inspection" (optional) profile_protocol_options: Protocol options profile name (optional). Mutually exclusive with profile_group -- it is itself a member of the FortiOS firewall profile-group object, so FortiOS rejects setting both profile_group: Security profile group name (optional). Mutually exclusive with av_profile, ips_sensor, webfilter_profile, dnsfilter_profile, application_list, file_filter_profile, ssl_ssh_profile, and profile_protocol_options -- FortiOS rejects setting both

Returns: dict: Create result with keys: - status: "success" or "error" - policyid: Created policy ID - message: Status or error message

Example: >>> result = await create_firewall_policy( ... adom="root", ... package="default", ... name="Allow-Web-Traffic", ... srcintf=["internal"], ... dstintf=["wan1"], ... srcaddr=["LAN-Subnet"], ... dstaddr=["all"], ... service=["HTTP", "HTTPS"], ... action="accept", ... nat=True, ... utm_status=True, ... av_profile="default", ... ips_sensor="default", ... )

update_firewall_policyA

Update an existing firewall policy.

Only the specified fields will be updated; other fields remain unchanged.

Args: adom: ADOM name package: Policy package name policyid: Policy ID to update name: New policy name (optional) srcintf: New source interfaces (optional) dstintf: New destination interfaces (optional) srcaddr: New source addresses (optional) dstaddr: New destination addresses (optional) service: New services (optional) action: New action - "accept" or "deny" (optional) schedule: New schedule (optional) nat: Enable/disable NAT (optional) logtraffic: New log mode (optional) status: New status (optional) comments: New comments (optional) global_label: Policy section label (optional) global_label_color: Policy section color ID 0-31 (optional) srcaddr_negate: Match all sources EXCEPT srcaddr (optional; None leaves the field untouched, like every other optional field) dstaddr_negate: Match all destinations EXCEPT dstaddr (optional) service_negate: Match all services EXCEPT service (optional) utm_status: Enable/disable security profile inspection (optional) av_profile: Antivirus profile name (optional) ips_sensor: IPS sensor name (optional) webfilter_profile: Web filter profile name (optional) dnsfilter_profile: DNS filter profile name (optional) application_list: Application control list name (optional) file_filter_profile: File filter profile name (optional) ssl_ssh_profile: SSL/SSH inspection profile name (optional) profile_protocol_options: Protocol options profile name (optional). Mutually exclusive with profile_group in the same call -- it is itself a member of the FortiOS firewall profile-group object profile_group: Security profile group name (optional). Mutually exclusive with av_profile, ips_sensor, webfilter_profile, dnsfilter_profile, application_list, file_filter_profile, ssl_ssh_profile, and profile_protocol_options in the same call

Note: Sending any individual security-profile field (av_profile, ssl_ssh_profile, profile_protocol_options, etc.) on a policy that is currently in group mode flips profile-type to "single" on the appliance. The previously-stored profile-group value becomes dormant (not deleted) rather than staying active alongside the new field -- FortiOS only honors one profile-type at a time.

Returns: dict: Update result with keys: - status: "success" or "error" - policyid: Updated policy ID - message: Status or error message

Example: >>> # Disable a policy >>> result = await update_firewall_policy( ... adom="root", ... package="default", ... policyid=10, ... status="disable" ... )

>>> # Update source addresses
>>> result = await update_firewall_policy(
...     adom="root",
...     package="default",
...     policyid=10,
...     srcaddr=["New-Subnet", "Other-Subnet"]
... )
delete_firewall_policyA

Delete a firewall policy.

WARNING: This operation cannot be undone.

Args: adom: ADOM name package: Policy package name policyid: Policy ID to delete

Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message

delete_firewall_policies_bulkA

Delete multiple firewall policies at once.

WARNING: This operation cannot be undone.

Args: adom: ADOM name package: Policy package name policyids: List of policy IDs to delete

Returns: dict: Delete result with keys: - status: "success", "partial", or "error" - deleted_count: Number of policies deleted - deleted: Policy IDs that were deleted - failed: Per-item failures [{"policyid", "message", "error_code"}, ...] - message: Status or error message

Example: >>> result = await delete_firewall_policies_bulk( ... adom="root", ... package="default", ... policyids=[5, 6, 7, 8] ... )

move_firewall_policyA

Move a firewall policy to a new position.

Reorders a policy relative to another policy in the rule list. Policy order determines evaluation priority.

Args: adom: ADOM name package: Policy package name policyid: Policy ID to move target_policyid: Reference policy ID position: Where to place - "before" or "after" (default: "before")

Returns: dict: Move result with keys: - status: "success" or "error" - message: Status or error message

Example: >>> # Move policy 10 before policy 5 >>> result = await move_firewall_policy( ... adom="root", ... package="default", ... policyid=10, ... target_policyid=5, ... position="before" ... )

search_firewall_policiesA

Search firewall policies with filters.

Args: adom: ADOM name package: Policy package name name_filter: Filter by policy name (partial match) srcaddr_filter: Filter by source address (partial match) dstaddr_filter: Filter by destination address (partial match) service_filter: Filter by service (partial match) action_filter: Filter by action ("accept" or "deny") status_filter: Filter by status ("enable" or "disable")

Returns: dict: Search results with keys: - status: "success" or "error" - count: Number of matching policies - policies: List of matching policy objects - message: Error message if failed

Example: >>> # Find all deny policies >>> result = await search_firewall_policies( ... adom="root", ... package="default", ... action_filter="deny" ... )

>>> # Find policies using a specific address
>>> result = await search_firewall_policies(
...     adom="root",
...     package="default",
...     srcaddr_filter="Server-Subnet"
... )
get_policy_servicesA

Get services configured on a firewall policy with optional group resolution.

Retrieves the service list from a firewall policy and optionally resolves each service/group into its detailed definition (ports, protocols, group members).

Useful for policy hardening workflows where you need to compare actual traffic against configured services.

Args: adom: ADOM name package: Policy package name policy_id: Policy ID number resolve: If True, resolve each service to its definition including port ranges and group members (default: True)

Returns: dict: Service information with keys: - status: "success" or "error" - policy_id: The policy ID queried - policy_name: Name of the policy - service_names: List of raw service names from the policy - services: Resolved service details (if resolve=True) - message: Error message if failed

Example: >>> # Get resolved services for policy 10 >>> result = await get_policy_services("root", "default", 10)

>>> # Get just the service names without resolution
>>> result = await get_policy_services("root", "default", 10, resolve=False)
preview_installA

Preview installation changes before applying.

Shows what configuration changes would be made to devices without actually installing the package. Use this to verify changes before deployment.

Args: adom: ADOM name package: Policy package name (optional, preview device settings if None) devices: Target devices [{"name": "FGT1", "vdom": "root"}, ...]

Returns: dict: Preview result with keys: - status: "success" or "error" - task_id: Task ID for retrieving preview results - message: Status or error message

Example: >>> # Start preview >>> result = await preview_install( ... adom="root", ... package="default", ... devices=[{"name": "FGT-HQ", "vdom": "root"}] ... ) >>> # Wait for preview to complete, then get results >>> if result["status"] == "success": ... from fortimanager_mcp.tools.system_tools import wait_for_task ... await wait_for_task(result["task_id"]) ... preview = await get_preview_result(...)

get_preview_resultA

Get the result of an installation preview.

Call this after preview_install() task completes to see the detailed configuration changes.

Args: adom: ADOM name devices: Target devices [{"name": "FGT1", "vdom": "root"}, ...]

Returns: dict: Preview results with keys: - status: "success" or "error" - preview: Preview data with configuration changes - message: Error message if failed

list_local_in_policiesA

List IPv4 local-in policies in a policy package.

Local-in policies control access to the FortiGate's own management plane (GUI, SSH, ping, API, SNMP, etc.), not through-traffic.

Args: adom: ADOM name package: Policy package name fields: Specific fields to return (optional) limit: Maximum number of policies to return (optional) offset: Starting position for pagination (default: 0)

Returns: dict: Policy list with keys: - status: "success" or "error" - count: Number of policies returned - total: Total number of local-in policies in package - policies: List of local-in policy objects - message: Error message if failed

Example: >>> result = await list_local_in_policies("root", "default")

get_local_in_policyB

Get detailed information about a specific IPv4 local-in policy.

Args: adom: ADOM name package: Policy package name policyid: Local-in policy ID number

Returns: dict: Policy details with keys: - status: "success" or "error" - policy: Full local-in policy configuration - message: Error message if failed

Example: >>> result = await get_local_in_policy("root", "default", 1)

create_local_in_policyA

Create a new IPv4 local-in policy.

Local-in policies control traffic destined to the FortiGate's own management plane (GUI, SSH, ping, API, SNMP, etc.) -- a distinct object space from the regular firewall/policy table (create_firewall_policy), which governs traffic passing through the device. There is no srcintf/dstintf pair here: intf is the single incoming interface, since local-in traffic terminates on the FortiGate itself rather than being forwarded. A local-in policy only takes effect once its package is installed to a device (see preview_install / install_package).

Args: adom: ADOM name package: Policy package name intf: Incoming interface(s) this policy applies to, e.g. ["port1"] srcaddr: Source addresses this policy matches (e.g. ["Admin-Subnet"]) dstaddr: Destination addresses on the FortiGate itself (e.g. ["all"]) service: Services this policy matches, e.g. ["HTTPS", "SSH"] action: Policy action - "accept" or "deny" (default: "deny", the FortiOS field default -- unlike create_firewall_policy this defaults closed since it governs management-plane access) schedule: Schedule object name(s) (default: ["always"] if omitted) status: Policy status - "enable" or "disable" (default: "enable") logtraffic: Local-in traffic logging - "enable" or "disable" (optional) comments: Policy comments (optional) policyid: Specific policy ID (optional, auto-assigned if not set) srcaddr_negate: Match all sources EXCEPT srcaddr (optional) dstaddr_negate: Match all destinations EXCEPT dstaddr (optional) service_negate: Match all services EXCEPT service (optional) ha_mgmt_intf_only: Restrict this policy to the HA management interface only (optional). IPv4-only field -- there is no equivalent on create_local_in_policy6. virtual_patch: Enable/disable virtual patching on this policy (optional)

Returns: dict: Create result with keys: - status: "success" or "error" - policyid: Created policy ID - message: Status or error message

Example: >>> result = await create_local_in_policy( ... adom="root", ... package="default", ... intf=["port1"], ... srcaddr=["Admin-Subnet"], ... dstaddr=["all"], ... service=["HTTPS", "SSH"], ... action="accept", ... )

update_local_in_policyA

Update an existing IPv4 local-in policy.

Only the specified fields will be updated; other fields remain unchanged.

Args: adom: ADOM name package: Policy package name policyid: Local-in policy ID to update action: New action - "accept" or "deny" (optional) intf: New incoming interface(s) (optional) srcaddr: New source addresses (optional) dstaddr: New destination addresses (optional) service: New services (optional) schedule: New schedule (optional) status: New status - "enable" or "disable" (optional) logtraffic: New local-in traffic logging setting (optional) comments: New comments (optional) srcaddr_negate: Match all sources EXCEPT srcaddr (optional) dstaddr_negate: Match all destinations EXCEPT dstaddr (optional) service_negate: Match all services EXCEPT service (optional) ha_mgmt_intf_only: Restrict this policy to the HA management interface only (optional). IPv4-only field. virtual_patch: Enable/disable virtual patching on this policy (optional)

Returns: dict: Update result with keys: - status: "success" or "error" - policyid: Updated policy ID - message: Status or error message

Example: >>> # Disable a local-in policy >>> result = await update_local_in_policy( ... adom="root", ... package="default", ... policyid=1, ... status="disable" ... )

delete_local_in_policyB

Delete an IPv4 local-in policy.

WARNING: This operation cannot be undone.

Args: adom: ADOM name package: Policy package name policyid: Local-in policy ID to delete

Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message

list_local_in_policies6A

List IPv6 local-in policies in a policy package.

Local-in policies control access to the FortiGate's own management plane (GUI, SSH, ping, API, SNMP, etc.) over IPv6, not through-traffic.

Args: adom: ADOM name package: Policy package name fields: Specific fields to return (optional) limit: Maximum number of policies to return (optional) offset: Starting position for pagination (default: 0)

Returns: dict: Policy list with keys: - status: "success" or "error" - count: Number of policies returned - total: Total number of local-in policies6 in package - policies: List of local-in policy6 objects - message: Error message if failed

Example: >>> result = await list_local_in_policies6("root", "default")

get_local_in_policy6B

Get detailed information about a specific IPv6 local-in policy.

Args: adom: ADOM name package: Policy package name policyid: Local-in policy6 ID number

Returns: dict: Policy details with keys: - status: "success" or "error" - policy: Full local-in policy6 configuration - message: Error message if failed

Example: >>> result = await get_local_in_policy6("root", "default", 1)

create_local_in_policy6A

Create a new IPv6 local-in policy.

IPv6 counterpart of create_local_in_policy -- see that tool's docstring for the local-in-policy concept. srcaddr/dstaddr reference IPv6 address objects here. There is no ha_mgmt_intf_only field on this object type (it is IPv4-only on the appliance).

Args: adom: ADOM name package: Policy package name intf: Incoming interface(s) this policy applies to, e.g. ["port1"] srcaddr: Source IPv6 addresses this policy matches dstaddr: Destination IPv6 addresses on the FortiGate itself (e.g. ["all"]) service: Services this policy matches, e.g. ["HTTPS", "SSH"] action: Policy action - "accept" or "deny" (default: "deny", the FortiOS field default) schedule: Schedule object name(s) (default: ["always"] if omitted) status: Policy status - "enable" or "disable" (default: "enable") logtraffic: Local-in traffic logging - "enable" or "disable" (optional) comments: Policy comments (optional) policyid: Specific policy ID (optional, auto-assigned if not set) srcaddr_negate: Match all sources EXCEPT srcaddr (optional) dstaddr_negate: Match all destinations EXCEPT dstaddr (optional) service_negate: Match all services EXCEPT service (optional) virtual_patch: Enable/disable virtual patching on this policy (optional)

Returns: dict: Create result with keys: - status: "success" or "error" - policyid: Created policy ID - message: Status or error message

Example: >>> result = await create_local_in_policy6( ... adom="root", ... package="default", ... intf=["port1"], ... srcaddr=["Admin-Subnet-v6"], ... dstaddr=["all"], ... service=["HTTPS", "SSH"], ... action="accept", ... )

update_local_in_policy6A

Update an existing IPv6 local-in policy.

Only the specified fields will be updated; other fields remain unchanged.

Args: adom: ADOM name package: Policy package name policyid: Local-in policy6 ID to update action: New action - "accept" or "deny" (optional) intf: New incoming interface(s) (optional) srcaddr: New source IPv6 addresses (optional) dstaddr: New destination IPv6 addresses (optional) service: New services (optional) schedule: New schedule (optional) status: New status - "enable" or "disable" (optional) logtraffic: New local-in traffic logging setting (optional) comments: New comments (optional) srcaddr_negate: Match all sources EXCEPT srcaddr (optional) dstaddr_negate: Match all destinations EXCEPT dstaddr (optional) service_negate: Match all services EXCEPT service (optional) virtual_patch: Enable/disable virtual patching on this policy (optional)

Returns: dict: Update result with keys: - status: "success" or "error" - policyid: Updated policy ID - message: Status or error message

Example: >>> result = await update_local_in_policy6( ... adom="root", ... package="default", ... policyid=1, ... status="disable" ... )

delete_local_in_policy6A

Delete an IPv6 local-in policy.

WARNING: This operation cannot be undone.

Args: adom: ADOM name package: Policy package name policyid: Local-in policy6 ID to delete

Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message

list_device_revisionsA

List the device DB revision history for a managed device.

Every install, auto-update, retrieve, or revert creates a new device DB revision. base_ver is the revision FortiManager currently considers in sync with the real device configuration.

Note: Requires the device to have at least one device DB revision on record (i.e. it has been installed to or retrieved from at least once). A freshly added device that has never gone through either will make FortiManager return a generic internal-server error here rather than an empty list -- that is FMG's own handling of an empty revision table, not a sign this tool or the underlying API is broken.

Args: device: Managed device name

Returns: dict: status, device, base_ver, count, revisions (each entry has revision, comments, instime/instusr, modtime/modusr, status, tag)

Example: >>> result = await list_device_revisions("hub2") >>> for rev in result["revisions"]: ... print(rev["revision"], rev["comments"])

get_device_revisionA

Check out one device DB revision's stored configuration text.

Note: Requires the device to have at least one device DB revision on record -- see the note on list_device_revisions.

Args: device: Managed device name revision: Revision number (see list_device_revisions), or -1 (default) for the latest revision

Returns: dict: status, device, revision, content (device DB config text with known secret-bearing directives redacted -- see redact_config_text_secrets. Structured dict-field masking elsewhere in this codebase can't see inside this multi-line CLI text, so this applies a separate directive-name-based redaction.)

Example: >>> result = await get_device_revision("hub2", revision=8) >>> print(result["content"])

diff_device_revisionA

Diff a past device DB revision against the CURRENT device DB.

FortiManager has no native diff endpoint for device DB revisions (unlike ADOM DB revisions -- see diff_adom_revision). This instead fetches both the past revision's config text (checkout) and the current device DB export, then computes a client-side unified line diff. Read-only.

Note: The checkout half of this requires the device to have at least one device DB revision on record -- see the note on list_device_revisions.

Args: device: Managed device name revision: Revision number to compare against current (see list_device_revisions)

Returns: dict: status, device, revision, changed (bool), diff (unified diff text, empty string if unchanged; both sides are redacted of known secret-bearing directives before diffing -- see redact_config_text_secrets -- so a line whose only change is its secret value will not show as changed)

Example: >>> result = await diff_device_revision("hub2", revision=7) >>> if result["changed"]: ... print(result["diff"])

revert_device_revisionA

Revert a device's device DB to a past revision.

This rewrites FortiManager's stored device DB copy for device only -- the live FortiGate is not touched. Push the reverted device DB to the device the same way any other device DB change is pushed: preview_install then install_device_settings.

Safety: governed by FMG_REVERT_SAFETY (default "strict"), same pattern as revert_adom_revision -- refused unless confirm=True is also passed. Set FMG_REVERT_SAFETY=disabled to remove it.

Note: Requires the device to have at least one device DB revision on record -- see the note on list_device_revisions. Not live-tested (reverting a device's config is a real, consequential write); the request shape matches the FNDN-documented, non-deprecated /deployment/revert command exactly (see client.py), which the sibling get/checkout calls confirm works correctly on this FMG generation for devices that have revision history.

Args: device: Managed device name revision: Revision number to revert to (see list_device_revisions) confirm: Must be True to proceed under FMG_REVERT_SAFETY=strict (default). Ignored when FMG_REVERT_SAFETY=disabled.

Returns: dict: status, device, revision, message

list_adom_revisionsB

List the ADOM DB revision history for an ADOM.

Args: adom: ADOM name fields: Specific fields to return (optional, returns all if omitted)

Returns: dict: status, adom, count, revisions (each entry has version, name, desc, created_by, created_time, locked)

Example: >>> result = await list_adom_revisions("demo") >>> for rev in result["revisions"]: ... print(rev["version"], rev["desc"])

get_adom_revisionA

Get one ADOM DB revision's metadata.

Args: adom: ADOM name revision: Revision number (the version field from list_adom_revisions)

Returns: dict: status, adom, revision (name, desc, created_by, created_time, locked, version)

diff_adom_revisionA

Diff a past ADOM DB revision against the CURRENT live ADOM.

Starts a FortiManager cache-diff job (the revision as source, the live ADOM as destination), polls it to completion, and returns the summary. Read-only -- nothing is written to the ADOM.

Args: adom: ADOM name revision: Revision number to compare against current (see list_adom_revisions) timeout: Maximum wait for the diff job in seconds (default 60, capped at 300) poll_interval: Seconds between poll attempts (default 2, capped at 30)

Returns: dict: status, adom, revision, obj (object-level change summary, keyed by category), pkg (per-package change summary)

revert_adom_revisionA

Revert the live ADOM DB to a past revision.

FortiManager has no dedicated revert-ADOM-revision call. The documented mechanism (How-To 013, "How to revert an ADOM Revision?") is to clone the target past revision: this both restores the live ADOM DB to that historical state and records the clone itself as a brand-new revision. This writes to the ADOM DB (a pure ADOM object, not a device) -- push any restored policy/object changes to devices separately with preview_install

  • install_package/install_device_settings as needed.

Safety: governed by FMG_REVERT_SAFETY (default "strict"), same pattern as trigger_fmg_restore -- refused unless confirm=True is also passed. A clone-revert has no dry-run or content-screening equivalent (unlike the firewall-policy revert, which is gated by content instead): it replaces the entire live ADOM DB in one call, so the gate is a plain explicit confirmation. Set FMG_REVERT_SAFETY=disabled to remove it.

Args: adom: ADOM name revision: Revision number to revert to (the version field from list_adom_revisions) name: Name for the newly-created revision (default: "Restored-rev_{revision}") desc: Description for the newly-created revision (default: "Revert of ADOM Revision #{revision}") locked: Protect the newly-created revision from deletion (default False) confirm: Must be True to proceed under FMG_REVERT_SAFETY=strict (default). Ignored when FMG_REVERT_SAFETY=disabled.

Returns: dict: status, adom, reverted_from, new_revision, message

list_policy_revisionsA

List the change log for a policy package's firewall policies.

Args: adom: ADOM name package: Policy package name policyid: Limit to one policy's change history (optional; all policies in the package if omitted)

Returns: dict: status, adom, package, policyid, count, changes -- each entry has act (1=created, 2=deleted, 3=modified), key (the policyid), config (a JSON-encoded policy snapshot at that change -- pass straight to revert_firewall_policy), timestamp, user

Example: >>> result = await list_policy_revisions("demo", "ppkg_001", policyid=14) >>> first_change = result["changes"][0] >>> await revert_firewall_policy("demo", "ppkg_001", first_change["config"])

diff_policy_packageA

Diff a policy package's objects at a past ADOM revision against its CURRENT live state.

Package-level changes are tracked as part of the ADOM DB revision history, not separately, so this reuses the same cache-diff job as diff_adom_revision, scoped to one package's summary view. Read-only.

Args: adom: ADOM name package: Policy package name revision: ADOM revision number to compare against current (see list_adom_revisions) timeout: Maximum wait for the diff job in seconds (default 60, capped at 300) poll_interval: Seconds between poll attempts (default 2, capped at 30)

Returns: dict: status, adom, package, revision, summary (change summary for this package only, keyed by category -- e.g. 181 = firewall policy)

revert_firewall_policyA

Restore a firewall policy to a past change-log snapshot.

FortiManager has no dedicated revert-firewall-policy endpoint. The documented mechanism (How-To 008, "How to revert a firewall policy from a past changes?") is to capture a past change's config snapshot from list_policy_revisions and update the live policy with it -- this tool does exactly that. This writes a policy OBJECT inside the ADOM's policy package; it does not touch any device. Push the reverted policy to devices the same way any other policy edit is pushed: preview_install then install_package.

Args: adom: ADOM name package: Policy package name config: A snapshot from list_policy_revisions' "config" field -- either the JSON string as returned, or the already-decoded dict. Must include "policyid". Any "oid" key is stripped automatically (FortiManager rejects the write if it is present). revision_note: Optional note recorded against this change

Returns: dict: status, adom, package, policyid, message

Example: >>> revisions = await list_policy_revisions("demo", "ppkg_001", policyid=14) >>> await revert_firewall_policy( ... "demo", "ppkg_001", revisions["changes"][0]["config"], ... revision_note="Revert from create time", ... )

list_scriptsA

List CLI scripts in an ADOM.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") script_type: Filter by type (cli, tcl, cligrp, tclgrp, jinja) target: Filter by target (device_database, remote_device, adom_database) limit: Maximum number of scripts to return

Returns: List of scripts with name, type, target, and description

get_scriptA

Get details of a specific CLI script.

Args: adom: ADOM name name: Script name

Returns: Script details including content

create_scriptB

Create a new CLI script.

Args: adom: ADOM name name: Script name content: Script content (CLI commands) script_type: Script type - cli, tcl, cligrp, tclgrp, jinja (default: cli) target: Execution target (default: device_database) - device_database: Device Database - adom_database: Policy Package or ADOM Database - remote_device: Remote FortiGate Directly (via CLI) description: Script description

Returns: Created script details

update_scriptA

Update an existing CLI script.

Args: adom: ADOM name name: Script name to update content: New script content description: New description script_type: New script type target: New target

Returns: Updated script details

delete_scriptB

Delete a CLI script.

Args: adom: ADOM name name: Script name to delete

Returns: Deletion result

execute_script_on_deviceA

Execute a CLI script on a specific device.

The script must have target=remote_device to run directly on the device. Script execution creates a task that should be monitored for completion.

IMPORTANT: Keep the session alive while script executes on remote devices.

Args: adom: ADOM name script: Script name to execute device: Target device name

Returns: Task ID for monitoring execution progress

execute_script_on_devicesB

Execute a CLI script on multiple devices.

Args: adom: ADOM name script: Script name to execute devices: List of device names

Returns: Task ID for monitoring execution progress

execute_script_on_device_groupA

Execute a CLI script on a device group.

Args: adom: ADOM name script: Script name to execute group: Device group name

Returns: Task ID for monitoring execution progress

execute_script_on_packageA

Execute a CLI script against a policy package or ADOM database.

The script must have target=adom_database. This modifies the package/ADOM DB, not a live device.

Args: adom: ADOM name script: Script name to execute package: Policy package name

Returns: Task ID for monitoring execution progress

get_script_log_latestB

Get the latest script execution log.

Args: adom: ADOM name device: Optional device name to filter logs

Returns: Latest script execution log with content and execution time

get_script_log_summaryA

Get script execution log summary.

Lists all script executions with log IDs for detailed retrieval.

Args: adom: ADOM name device: Optional device name to filter logs

Returns: List of script execution summaries with log_id, script_name, exec_time

get_script_log_outputA

Get specific script execution output by log ID.

Log ID can be derived from task ID:

  • Scripts on Device DB or Package: log_id = str(task_id) + "1"

  • Scripts on remote device: log_id = str(task_id) + "0"

Args: adom: ADOM name log_id: Log ID from execution task or log summary device: Optional device name (required for device-specific logs)

Returns: Script execution output content

list_sdwan_templatesA

List SD-WAN templates in an ADOM.

SD-WAN templates define WAN interface configurations, performance SLAs, and traffic steering rules for SD-WAN deployments.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") limit: Maximum number of templates to return

Returns: List of SD-WAN templates with name, type, and assigned devices

get_sdwan_templateA

Get details of a specific SD-WAN template.

Args: adom: ADOM name name: SD-WAN template name

Returns: SD-WAN template details including interfaces, SLAs, and rules

get_device_sdwanA

Get a managed device's SD-WAN configuration from FortiManager's device DB.

Unlike get_sdwan_template (which reads wanprof templates in an ADOM), this reads the SD-WAN config local to the device itself -- members/zones, health-checks and service (steering) rules -- from the device database FortiManager keeps in sync with the FortiGate. Use this when a device runs SD-WAN but has no wanprof template assigned (list_sdwan_templates returns nothing) yet you still need its members, zones and steering rules.

Args: device: Managed device name (e.g. "myfw01") vdom: VDOM name (default: "root")

Returns: dict with keys: - device, vdom - sdwan: raw system/sdwan config object - summary: condensed members/zones/health-checks/service rules - error / error_code: on failure

get_device_sdwan_monitorA

Read a device's LIVE SD-WAN Monitor status through the FortiManager proxy.

Unlike get_device_sdwan (which reads the device-DB SD-WAN config), this proxies the device's live monitor API to return current SD-WAN runtime state -- the data the FortiManager "SD-WAN Monitor" view shows:

  • per-member link status and up/down bandwidth (virtual-wan/members)

  • per-member SLA health for each health-check: latency, jitter, packet loss and which SLA targets are met (virtual-wan/health-check)

Use it to answer "how are the uplinks doing right now / which member is breaching SLA", which the config read cannot answer.

Args: adom: ADOM name (defaults to DEFAULT_ADOM / "root" when omitted) device: Managed device name (e.g. "FGT-HQ"). Defaults to DEFAULT_DEVICE when omitted; required if that is not configured.

Returns: dict with keys: - adom, device - members: raw virtual-wan/members proxy response - health_check: raw virtual-wan/health-check proxy response - summary: condensed member links/bandwidth + per-member SLA - error / error_code: on failure

resolve_datasourceA

Resolve the objects a config attribute is allowed to reference.

Generic config-DB introspection built on FortiManager's option: datasrc mechanism: given a cdb table url and an attribute name attr, FMG returns every object attr may reference. For example, the objects an SD-WAN service (steering) rule can point at -- internet-service-names, address groups, etc. Documented generically in the swagger cdb get-params (params.cdb.get.table.option.opts -> datasrc, requires attr).

Only pm/config endpoints are accepted -- arbitrary URLs are rejected.

Args: url: A cdb config path, e.g. "pm/config/adom/root/obj/system/sdwan" attr: The attribute whose referenceable objects to resolve, e.g. "internet-service-name" or "service" adom: ADOM name (informational; default: DEFAULT_ADOM env var or "root")

Returns: dict with keys: - url, attr, adom - datasource: raw list/dict of referenceable objects - error / error_code: on failure

create_sdwan_templateB

Create a new SD-WAN template.

Creates an empty SD-WAN template that can be configured with interfaces, SLAs, and routing rules.

Args: adom: ADOM name name: SD-WAN template name description: Optional description

Returns: Created SD-WAN template

delete_sdwan_templateB

Delete an SD-WAN template.

Note: Template must not be assigned to any devices before deletion.

Args: adom: ADOM name name: SD-WAN template name

Returns: Deletion result

assign_sdwan_templateC

Assign an SD-WAN template to a device.

Args: adom: ADOM name template: SD-WAN template name device: Device name to assign vdom: VDOM name (default: root)

Returns: Assignment result

assign_sdwan_template_bulkA

Assign an SD-WAN template to multiple devices.

Args: adom: ADOM name template: SD-WAN template name devices: List of devices [{"name": "dev1", "vdom": "root"}, ...]

Returns: dict: Assignment result with keys: - success: True - requested_count: Number of devices in the request. NOT a count of assignments actually made: FortiManager reports no per-device result for this scope-member ADD, and the client raises on any non-zero code, so everything reaching this line is a code 0 that says nothing about individual devices (same shape as #78's device-group bulk tools). - result: Raw client result

unassign_sdwan_templateB

Unassign an SD-WAN template from a device.

Args: adom: ADOM name template: SD-WAN template name device: Device name to unassign vdom: VDOM name (default: root)

Returns: Unassignment result

list_ips_sensorsA

List IPS sensors in an ADOM.

IPS sensors are collections of intrusion-prevention signature filters and overrides, referenced by firewall policies for the "IPS" security profile.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match)

Returns: dict: Sensor list with keys: - status: "success" or "error" - count: Number of sensors - sensors: List of IPS sensor objects - message: Error message if failed

get_ips_sensorB

Get detailed information about an IPS sensor.

Args: adom: ADOM name name: IPS sensor name

Returns: dict: Sensor details with keys: - status: "success" or "error" - sensor: Full sensor configuration, including its "entries" list of signature filters/overrides - message: Error message if failed

create_ips_sensorA

Create an IPS sensor.

The sensor is created empty (no signature filters/overrides). Use add_ips_sensor_signature_override to populate it afterward.

Args: adom: ADOM name name: Sensor name comment: Optional comment block_malicious_url: Enable/disable malicious URL blocking ("enable" or "disable") scan_botnet_connections: Botnet-server connection handling ("disable", "block", or "monitor") extended_log: Enable/disable extended logging ("enable" or "disable")

Returns: dict: Create result with keys: - status: "success" or "error" - name: Created sensor name - message: Status or error message

Example: >>> result = await create_ips_sensor( ... adom="root", ... name="Custom-IPS", ... scan_botnet_connections="block", ... comment="Baseline IPS sensor", ... )

update_ips_sensorA

Update an IPS sensor's top-level settings.

Only specified fields will be updated. To change signature filters/overrides use add_ips_sensor_signature_override / remove_ips_sensor_signature_override instead.

Args: adom: ADOM name name: Sensor name comment: New comment block_malicious_url: Enable/disable malicious URL blocking scan_botnet_connections: Botnet-server connection handling ("disable", "block", or "monitor") extended_log: Enable/disable extended logging

Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message

delete_ips_sensorA

Delete an IPS sensor.

WARNING: This will fail if the sensor is in use by policies.

Args: adom: ADOM name name: Sensor name to delete

Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message

list_ips_sensor_signature_overridesA

List the signature-filter/override entries of an IPS sensor.

Each entry is either a broad filter (matched by severity/protocol/OS/...) or a specific signature override (matched by an explicit "rule" list of signature names). Use the returned "id" values with remove_ips_sensor_signature_override.

Args: adom: ADOM name sensor: IPS sensor name

Returns: dict: Entry list with keys: - status: "success" or "error" - count: Number of entries - entries: List of entry objects - message: Error message if failed

add_ips_sensor_signature_overrideA

Add a signature filter/override entry to an IPS sensor.

FortiManager exposes an IPS sensor's "entries" list as its own addressable nested collection (its own add/get/set/update/delete/move endpoints under .../sensor/{sensor}/entries), so this calls that sub-resource's ADD endpoint directly rather than reading the whole sensor, splicing the list in Python, and writing it back -- unlike update_device_wtp_profile_radio's device-DB radio field, which has no such sub-resource and must be read-modify-written as a whole object.

Pass rule with explicit signature names to override specific signatures (e.g. ["trojan.generic"]); omit it and use severity / default_action / default_status to add a broad filter entry instead (matches every signature meeting those criteria).

Args: adom: ADOM name sensor: IPS sensor name (must already exist -- see create_ips_sensor) rule: Specific signature names to override (optional; omit for a broad filter entry) entry_id: Explicit entry ID (0-4294967295). If omitted, FortiManager assigns the next available ID. status: Status of the entry ("disable", "enable", or "default"; default: "enable") action: Action for matched traffic ("pass", "block", "reset", or "default"; default: "default") log: Enable/disable logging for this entry ("enable" or "disable") log_packet: Enable/disable packet logging for this entry ("enable" or "disable") severity: Signature severities to match (e.g. ["critical", "high"]) default_action: Broad-filter action filter ("block", "pass", or "all") default_status: Broad-filter status filter ("disable", "enable", or "all")

Returns: dict: Add result with keys: - status: "success" or "error" - sensor: Sensor name - message: Status or error message

Example: >>> # Override a specific signature to block+log >>> result = await add_ips_sensor_signature_override( ... adom="root", ... sensor="Custom-IPS", ... rule=["trojan.generic"], ... status="enable", ... action="block", ... log="enable", ... )

>>> # Add a broad "block all critical signatures" filter
>>> result = await add_ips_sensor_signature_override(
...     adom="root",
...     sensor="Custom-IPS",
...     severity=["critical"],
...     default_action="block",
...     status="enable",
...     action="block",
... )
remove_ips_sensor_signature_overrideA

Remove a signature filter/override entry from an IPS sensor.

Use list_ips_sensor_signature_overrides to find the entry's "id" first if it is not already known.

Args: adom: ADOM name sensor: IPS sensor name entry_id: ID of the entry to remove (as returned in its "id" field)

Returns: dict: Remove result with keys: - status: "success" or "error" - message: Status or error message

list_ssl_ssh_profilesA

List SSL/SSH inspection profiles in an ADOM.

SSL/SSH inspection profiles control how a FortiGate decrypts and inspects TLS/SSH traffic, referenced by firewall policies for the "SSL/SSH Inspection" security profile.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match)

Returns: dict: Profile list with keys: - status: "success" or "error" - count: Number of profiles - profiles: List of SSL/SSH profile objects - message: Error message if failed

get_ssl_ssh_profileA

Get detailed information about an SSL/SSH inspection profile.

Args: adom: ADOM name name: SSL/SSH profile name

Returns: dict: Profile details with keys: - status: "success" or "error" - profile: Full profile configuration - message: Error message if failed

create_ssl_ssh_profileB

Create an SSL/SSH inspection profile.

Args: adom: ADOM name name: Profile name comment: Optional comment ssl_inspection: Generic SSL inspection level ("disable", "certificate-inspection", or "deep-inspection") https_inspection: HTTPS inspection level ("disable", "certificate-inspection", or "deep-inspection") caname: CA certificate(s) used for SSL inspection (datasource names) untrusted_caname: Untrusted CA certificate(s) (datasource names) server_cert: Certificate(s) used to replace the server certificate (datasource names; only used when server_cert_mode="replace") server_cert_mode: Re-sign or replace the server's certificate ("re-sign" or "replace") mapi_over_https: Enable/disable inspection of MAPI over HTTPS rpc_over_https: Enable/disable inspection of RPC over HTTPS

Returns: dict: Create result with keys: - status: "success" or "error" - name: Created profile name - message: Status or error message

Example: >>> result = await create_ssl_ssh_profile( ... adom="root", ... name="Custom-Deep-Inspection", ... ssl_inspection="deep-inspection", ... https_inspection="deep-inspection", ... server_cert_mode="re-sign", ... comment="Deep inspection for outbound web traffic", ... )

update_ssl_ssh_profileA

Update an SSL/SSH inspection profile.

Only specified fields will be updated.

Args: adom: ADOM name name: Profile name comment: New comment ssl_inspection: Generic SSL inspection level ("disable", "certificate-inspection", or "deep-inspection") https_inspection: HTTPS inspection level ("disable", "certificate-inspection", or "deep-inspection") caname: New CA certificate(s) (datasource names, replaces existing) untrusted_caname: New untrusted CA certificate(s) (replaces existing) server_cert: New server certificate(s) (replaces existing) server_cert_mode: Re-sign or replace the server's certificate mapi_over_https: Enable/disable inspection of MAPI over HTTPS rpc_over_https: Enable/disable inspection of RPC over HTTPS

Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message

delete_ssl_ssh_profileA

Delete an SSL/SSH inspection profile.

WARNING: This will fail if the profile is in use by policies.

Args: adom: ADOM name name: Profile name to delete

Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message

list_dlp_profilesA

List DLP (Data Loss Prevention) profiles in an ADOM.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match)

Returns: dict: Profile list with keys: - status: "success" or "error" - count: Number of profiles - profiles: List of DLP profile objects - message: Error message if failed

get_dlp_profileB

Get detailed information about a DLP profile.

Args: adom: ADOM name name: DLP profile name

Returns: dict: Profile details with keys: - status: "success" or "error" - profile: Full profile configuration - message: Error message if failed

create_dlp_profileA

Create a DLP (Data Loss Prevention) profile.

The profile is created without rules. Rule management (config dlp profile / rule) is out of scope for this tool; add rules via the FortiManager GUI/CLI or a follow-up tool once this profile exists.

Args: adom: ADOM name name: Profile name comment: Optional comment feature_set: Flow or proxy-based inspection ("flow" or "proxy") dlp_log: Enable/disable DLP logging ("enable" or "disable") extended_log: Enable/disable extended logging ("enable" or "disable")

Returns: dict: Create result with keys: - status: "success" or "error" - name: Created profile name - message: Status or error message

Example: >>> result = await create_dlp_profile( ... adom="root", ... name="Custom-DLP", ... feature_set="flow", ... dlp_log="enable", ... )

update_dlp_profileB

Update a DLP profile's top-level settings.

Only specified fields will be updated.

Args: adom: ADOM name name: Profile name comment: New comment feature_set: Flow or proxy-based inspection ("flow" or "proxy") dlp_log: Enable/disable DLP logging extended_log: Enable/disable extended logging

Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message

delete_dlp_profileA

Delete a DLP profile.

WARNING: This will fail if the profile is in use by policies.

Args: adom: ADOM name name: Profile name to delete

Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message

list_waf_profilesA

List WAF (Web Application Firewall) profiles in an ADOM.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match)

Returns: dict: Profile list with keys: - status: "success" or "error" - count: Number of profiles - profiles: List of WAF profile objects - message: Error message if failed

get_waf_profileB

Get detailed information about a WAF profile.

Args: adom: ADOM name name: WAF profile name

Returns: dict: Profile details with keys: - status: "success" or "error" - profile: Full profile configuration - message: Error message if failed

create_waf_profileA

Create a WAF (Web Application Firewall) profile.

The profile is created with FortiManager/FortiOS defaults for its nested signature/constraint/method/url-access sections. Fine-grained control of those sections is out of scope for this tool; adjust them via the FortiManager GUI/CLI or a follow-up tool once this profile exists.

Args: adom: ADOM name name: Profile name comment: Optional comment external: Enable/disable external HTTP inspection ("enable" or "disable") extended_log: Enable/disable extended logging ("enable" or "disable")

Returns: dict: Create result with keys: - status: "success" or "error" - name: Created profile name - message: Status or error message

Example: >>> result = await create_waf_profile( ... adom="root", ... name="Custom-WAF", ... extended_log="enable", ... comment="Baseline WAF profile", ... )

update_waf_profileA

Update a WAF profile's top-level settings.

Only specified fields will be updated.

Args: adom: ADOM name name: Profile name comment: New comment external: Enable/disable external HTTP inspection extended_log: Enable/disable extended logging

Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message

delete_waf_profileA

Delete a WAF profile.

WARNING: This will fail if the profile is in use by policies.

Args: adom: ADOM name name: Profile name to delete

Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message

list_antivirus_profilesA

List antivirus profiles in an ADOM.

Antivirus profiles define virus/malware scanning behavior per protocol (HTTP, FTP, SMTP, etc.) and are applied to firewall policies.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match)

Returns: dict: Profile list with keys: - status: "success" or "error" - count: Number of profiles - profiles: List of antivirus profile objects - message: Error message if failed

get_antivirus_profileB

Get detailed information about an antivirus profile.

Args: adom: ADOM name name: Antivirus profile name

Returns: dict: Profile details with keys: - status: "success" or "error" - profile: Full antivirus profile configuration - message: Error message if failed

create_antivirus_profileA

Create an antivirus profile.

Args: adom: ADOM name name: Profile name comment: Optional comment feature_set: Flow/proxy feature set - "proxy" or "flow" (optional) scan_mode: Scan mode - "default" or "legacy" (optional) av_virus_log: Enable/disable AntiVirus logging - "enable" or "disable" (optional)

Returns: dict: Create result with keys: - status: "success" or "error" - name: Created profile name - message: Status or error message

Example: >>> result = await create_antivirus_profile( ... adom="root", ... name="strict-av", ... feature_set="flow", ... av_virus_log="enable", ... )

update_antivirus_profileA

Update an existing antivirus profile.

Only specified fields will be updated.

Args: adom: ADOM name name: Profile name comment: New comment (optional) feature_set: New flow/proxy feature set - "proxy" or "flow" (optional) scan_mode: New scan mode - "default" or "legacy" (optional) av_virus_log: New AntiVirus logging setting - "enable" or "disable" (optional)

Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message

delete_antivirus_profileB

Delete an antivirus profile.

WARNING: This will fail if the profile is in use by firewall policies.

Args: adom: ADOM name name: Profile name to delete

Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message

list_webfilter_profilesA

List web filter profiles in an ADOM.

Web filter profiles define URL category rating actions, FortiGuard web filtering behavior, and related web-content controls.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match)

Returns: dict: Profile list with keys: - status: "success" or "error" - count: Number of profiles - profiles: List of web filter profile objects - message: Error message if failed

get_webfilter_profileB

Get detailed information about a web filter profile.

Args: adom: ADOM name name: Web filter profile name

Returns: dict: Profile details with keys: - status: "success" or "error" - profile: Full web filter profile configuration - message: Error message if failed

create_webfilter_profileB

Create a web filter profile.

Args: adom: ADOM name name: Profile name comment: Optional comment feature_set: Flow/proxy feature set - "proxy" or "flow" (optional) log_all_url: Enable/disable logging all URLs visited - "enable" or "disable" (optional) web_content_log: Enable/disable logging blocked web content - "enable" or "disable" (optional)

Returns: dict: Create result with keys: - status: "success" or "error" - name: Created profile name - message: Status or error message

Example: >>> result = await create_webfilter_profile( ... adom="root", ... name="strict-web", ... feature_set="flow", ... log_all_url="enable", ... )

update_webfilter_profileA

Update an existing web filter profile.

Only specified fields will be updated.

Args: adom: ADOM name name: Profile name comment: New comment (optional) feature_set: New flow/proxy feature set - "proxy" or "flow" (optional) log_all_url: New "log all URLs" setting - "enable" or "disable" (optional) web_content_log: New blocked-content logging setting - "enable" or "disable" (optional)

Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message

delete_webfilter_profileB

Delete a web filter profile.

WARNING: This will fail if the profile is in use by firewall policies.

Args: adom: ADOM name name: Profile name to delete

Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message

list_dnsfilter_profilesB

List DNS filter profiles in an ADOM.

DNS filter profiles control FortiGuard DNS category rating actions, botnet C&C domain blocking, and DNS-level safe search.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match)

Returns: dict: Profile list with keys: - status: "success" or "error" - count: Number of profiles - profiles: List of DNS filter profile objects - message: Error message if failed

get_dnsfilter_profileB

Get detailed information about a DNS filter profile.

Args: adom: ADOM name name: DNS filter profile name

Returns: dict: Profile details with keys: - status: "success" or "error" - profile: Full DNS filter profile configuration - message: Error message if failed

create_dnsfilter_profileB

Create a DNS filter profile.

Args: adom: ADOM name name: Profile name comment: Optional comment block_action: Action for blocked domains - "block", "redirect", or "block-sevrfail" (optional) block_botnet: Enable/disable blocking botnet C&C DNS lookups - "enable" or "disable" (optional) safe_search: Enable/disable Google/Bing/YouTube/etc. safe search - "enable" or "disable" (optional)

Returns: dict: Create result with keys: - status: "success" or "error" - name: Created profile name - message: Status or error message

Example: >>> result = await create_dnsfilter_profile( ... adom="root", ... name="strict-dns", ... block_botnet="enable", ... safe_search="enable", ... )

update_dnsfilter_profileA

Update an existing DNS filter profile.

Only specified fields will be updated.

Args: adom: ADOM name name: Profile name comment: New comment (optional) block_action: New blocked-domain action - "block", "redirect", or "block-sevrfail" (optional) block_botnet: New botnet C&C blocking setting - "enable" or "disable" (optional) safe_search: New safe search setting - "enable" or "disable" (optional)

Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message

delete_dnsfilter_profileB

Delete a DNS filter profile.

WARNING: This will fail if the profile is in use by firewall policies.

Args: adom: ADOM name name: Profile name to delete

Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message

list_application_listsA

List application-control lists in an ADOM.

Application control lists define pass/block/monitor actions for application signatures and categories, applied to firewall policies.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match)

Returns: dict: List result with keys: - status: "success" or "error" - count: Number of application-control lists - lists: List of application-control list objects - message: Error message if failed

get_application_listB

Get detailed information about an application-control list.

Args: adom: ADOM name name: Application-control list name

Returns: dict: List details with keys: - status: "success" or "error" - list: Full application-control list configuration - message: Error message if failed

create_application_listB

Create an application-control list.

Args: adom: ADOM name name: List name comment: Optional comment deep_app_inspection: Enable/disable deep application inspection - "enable" or "disable" (optional) unknown_application_action: Pass or block traffic from unknown applications - "pass" or "block" (optional) other_application_action: Action for other applications - "pass" or "block" (optional)

Returns: dict: Create result with keys: - status: "success" or "error" - name: Created list name - message: Status or error message

Example: >>> result = await create_application_list( ... adom="root", ... name="strict-appctrl", ... unknown_application_action="block", ... other_application_action="block", ... )

update_application_listA

Update an existing application-control list.

Only specified fields will be updated.

Args: adom: ADOM name name: List name comment: New comment (optional) deep_app_inspection: New deep-inspection setting - "enable" or "disable" (optional) unknown_application_action: New unknown-application action - "pass" or "block" (optional) other_application_action: New other-application action - "pass" or "block" (optional)

Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message

delete_application_listA

Delete an application-control list.

WARNING: This will fail if the list is in use by firewall policies.

Args: adom: ADOM name name: List name to delete

Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message

get_system_statusA

Get FortiManager system status and version information.

Returns comprehensive system status including:

  • FortiManager version and build

  • System hostname

  • Serial number

  • Admin domain mode

  • Platform information

  • HA status

Returns: dict: System status with keys: - status: "success" or "error" - data: System status information - message: Error message if failed

Example: >>> result = await get_system_status() >>> print(f"Version: {result['data']['Version']}") >>> print(f"Hostname: {result['data']['Hostname']}")

get_ha_statusA

Get FortiManager High Availability (HA) health.

Returns what FortiManager reports at /sys/ha/status. On a standalone unit that is the HA health alone, {"HA Health Status": "OK"} (measured on 7.6.8 and 8.0.2): no HA mode, no peer list and no primary/secondary role. The HA mode is in get_system_status, under "HA Mode".

Returns: dict: HA status with keys: - status: "success" or "error" - data: The HA status FortiManager returned - message: Error message if failed

Example: >>> result = await get_ha_status() >>> print(result["data"].get("HA Health Status"))

list_adomsA

List all Administrative Domains (ADOMs) in FortiManager.

ADOMs partition FortiManager into separate management domains, each with its own devices, policies, and configurations.

Args: fields: Specific fields to return (optional, returns all if not specified)

Returns: dict: ADOM list with keys: - status: "success" or "error" - count: Number of ADOMs - adoms: List of ADOM objects with name, desc, state, etc. - message: Error message if failed

Example: >>> result = await list_adoms() >>> for adom in result["adoms"]: ... print(f"{adom['name']}: {adom.get('desc', 'No description')}")

get_adomA

Get detailed information about a specific ADOM.

Args: name: ADOM name (e.g., "root", "customer-a") include_details: Include sub-objects (default: False)

Returns: dict: ADOM details with keys: - status: "success" or "error" - adom: ADOM object with full configuration - message: Error message if failed

Example: >>> result = await get_adom("root") >>> print(f"State: {result['adom']['state']}")

list_devicesA

List all managed devices in an ADOM.

FortiManager manages FortiGate and other Fortinet devices. This lists all devices registered in the specified ADOM.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") fields: Specific fields to return (optional)

Returns: dict: Device list with keys: - status: "success" or "error" - count: Number of devices - devices: List of device objects with name, ip, os_ver, etc. - message: Error message if failed

Example: >>> result = await list_devices("root") >>> for device in result["devices"]: ... print(f"{device['name']}: {device.get('ip', 'N/A')}")

get_deviceA

Get detailed information about a specific managed device.

Args: name: Device name adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") include_details: Include sub-objects like VDOMs (default: False)

Returns: dict: Device details with keys: - status: "success" or "error" - device: Device object with full configuration - message: Error message if failed

Example: >>> result = await get_device("FGT-HQ", "root") >>> print(f"Version: {result['device']['os_ver']}") >>> print(f"Platform: {result['device']['platform_str']}")

list_device_groupsA

List all device groups in an ADOM.

Device groups organize managed devices for bulk operations like policy installation or configuration deployment.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root")

Returns: dict: Device groups with keys: - status: "success" or "error" - count: Number of groups - groups: List of device group objects - message: Error message if failed

list_tasksA

List all tasks in FortiManager.

Tasks represent background operations like policy installation, device provisioning, and other long-running processes.

Args: filter_state: Filter by task state (optional): - "pending": Not started - "running": Currently executing - "done": Completed - "error": Failed - "cancelling": Being cancelled - "cancelled": Cancelled

Returns: dict: Task list with keys: - status: "success" or "error" - count: Number of tasks - tasks: List of task objects with id, state, progress, etc. - message: Error message if failed

Example: >>> # Get all tasks >>> result = await list_tasks()

>>> # Get only running tasks
>>> result = await list_tasks(filter_state="running")
get_taskA

Get detailed status of a specific task.

Args: task_id: Task ID number include_details: Include task line details (default: False)

Returns: dict: Task details with keys: - status: "success" or "error" - task: Task object with id, state, progress, result, etc. - lines: Task line details (if include_details=True) - message: Error message if failed

Example: >>> result = await get_task(12345) >>> print(f"State: {result['task']['state']}") >>> print(f"Progress: {result['task'].get('percent', 0)}%")

wait_for_taskA

Wait for a task to complete.

Polls the task status until it completes or times out. Useful for waiting on installation or provisioning operations.

Args: task_id: Task ID number timeout: Maximum wait time in seconds (default: 300, capped at 3600) poll_interval: Seconds between status checks (default: 5)

Returns: dict: Final task status with keys: - status: "success" or "error" - task: Final task object - completed: Whether task completed (vs timeout) - message: Status or error message

Example: >>> # Wait for policy installation >>> result = await wait_for_task(12345, timeout=600) >>> if result['completed']: ... print("Installation finished!")

list_packagesA

List all policy packages in an ADOM.

Policy packages contain firewall policies, security profiles, and other configurations to be installed on managed devices.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root")

Returns: dict: Package list with keys: - status: "success" or "error" - count: Number of packages - packages: List of package objects - message: Error message if failed

Example: >>> result = await list_packages("root") >>> for pkg in result["packages"]: ... print(f"{pkg['name']}: {pkg.get('type', 'policy')}")

get_packageA

Get detailed information about a policy package.

Args: name: Package name adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") include_details: Include policies and settings (default: False)

Returns: dict: Package details with keys: - status: "success" or "error" - package: Package object - message: Error message if failed

install_packageA

Install a policy package to managed devices.

Deploys firewall policies and configurations from a policy package to the specified devices. This is an asynchronous operation - use wait_for_task() to monitor completion.

By default (FMG_INSTALL_SAFETY=strict) a real install requires a verified preview first: run preview_install for the same ADOM, package, and devices, wait for its task to finish, then install. Each preview authorizes one install, and only while the package is unchanged — if the package was edited after the preview, the install is refused (preview_stale) and a fresh preview is required.

Args: adom: ADOM name package: Policy package name devices: Target devices [{"name": "FGT1", "vdom": "root"}, ...] preview: If True, only preview changes without applying (default: False)

Returns: dict: Installation result with keys: - status: "success" or "error" - task_id: Task ID for monitoring (if successful) - message: Status or error message

Example: >>> # Install to single device >>> result = await install_package( ... adom="root", ... package="default", ... devices=[{"name": "FGT-HQ", "vdom": "root"}] ... ) >>> if result["status"] == "success": ... await wait_for_task(result["task_id"])

>>> # Preview installation first
>>> result = await install_package(
...     adom="root",
...     package="default",
...     devices=[{"name": "FGT-HQ", "vdom": "root"}],
...     preview=True
... )
install_device_settingsA

Install device settings only (without policy package).

Deploys device-level configurations like interfaces, DNS, NTP without reinstalling the full policy package.

Args: adom: ADOM name devices: Target devices [{"name": "FGT1", "vdom": "root"}, ...]

Returns: dict: Installation result with keys: - status: "success" or "error" - task_id: Task ID for monitoring - message: Status or error message

lock_adomA

Lock an ADOM for editing (workspace mode).

In workspace mode, ADOMs must be locked before making changes. This prevents conflicts from multiple administrators editing the same ADOM simultaneously.

Args: adom: ADOM name to lock

Returns: dict: Lock result with keys: - status: "success" or "error" - message: Status or error message

Example: >>> result = await lock_adom("root") >>> if result["status"] == "success": ... # Make changes... ... await commit_adom("root") ... await unlock_adom("root")

unlock_adomA

Unlock an ADOM (workspace mode).

Release the lock on an ADOM. Changes should be committed before unlocking to persist them.

Args: adom: ADOM name to unlock

Returns: dict: Unlock result with keys: - status: "success" or "error" - message: Status or error message

commit_adomA

Commit changes to an ADOM (workspace mode).

Saves all pending changes made to the ADOM. Must be called before unlocking to persist changes.

Args: adom: ADOM name to commit

Returns: dict: Commit result with keys: - status: "success" or "error" - message: Status or error message

list_templatesA

List all provisioning templates in an ADOM.

Returns all template types: IPsec, BGP, system, etc.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") limit: Maximum number of templates to return

Returns: List of templates with name, type, and settings

get_templateB

Get details of a specific provisioning template.

Args: adom: ADOM name name: Template name

Returns: Template details including settings and scope

list_system_templatesA

List system templates (device profiles) in an ADOM.

System templates configure device settings like DNS, NTP, logging, etc.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") limit: Maximum number to return

Returns: List of system templates with name, type, and assigned devices

get_system_templateB

Get details of a specific system template.

Args: adom: ADOM name name: System template name

Returns: System template details including widgets and assigned devices

assign_system_templateB

Assign a system template to a device.

Args: adom: ADOM name template: System template name device: Device name to assign vdom: VDOM name (default: root)

Returns: Assignment result

assign_system_template_bulkA

Assign a system template to multiple devices.

Args: adom: ADOM name template: System template name devices: List of devices [{"name": "dev1", "vdom": "root"}, ...]

Returns: dict: Assignment result with keys: - success: True - requested_count: Number of devices in the request. NOT a count of assignments actually made: FortiManager reports no per-device result for this scope-member ADD, and the client raises on any non-zero code, so everything reaching this line is a code 0 that says nothing about individual devices (same shape as #78's device-group bulk tools). - result: Raw client result

unassign_system_templateA

Unassign a system template from a device.

Args: adom: ADOM name template: System template name device: Device name to unassign vdom: VDOM name (default: root)

Returns: Unassignment result

list_cli_template_groupsA

List CLI template groups in an ADOM.

CLI template groups contain CLI commands to be executed on devices.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") limit: Maximum number to return

Returns: List of CLI template groups

get_cli_template_groupA

Get details of a CLI template group.

Args: adom: ADOM name name: CLI template group name

Returns: CLI template group details

create_cli_template_groupB

Create a new CLI template group.

Args: adom: ADOM name name: CLI template group name description: Optional description

Returns: Created CLI template group

delete_cli_template_groupB

Delete a CLI template group.

Args: adom: ADOM name name: CLI template group name

Returns: Deletion result

list_template_groupsA

List template groups in an ADOM.

Template groups combine multiple templates (system, CLI, SD-WAN, etc.) into a single package for device assignment.

Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") limit: Maximum number to return

Returns: List of template groups

get_template_groupB

Get details of a template group.

Args: adom: ADOM name name: Template group name

Returns: Template group details including member templates

assign_template_groupC

Assign a template group to a device.

Args: adom: ADOM name template_group: Template group name device: Device name vdom: VDOM name (default: root)

Returns: Assignment result

validate_templateA

Validate a template group for a device.

Checks that all metadata variables are resolved for the device.

Args: adom: ADOM name template_group: Template group name device: Target device name vdom: VDOM name (default: root)

Returns: Task ID for monitoring validation progress

list_device_ipsec_phase1_interfacesA

List IPsec phase1-interface (remote gateway) definitions in a device's device DB.

Args: device: Managed device name vdom: VDOM to read (default "root")

Returns: dict with device, vdom, count and interfaces (credential fields stripped -- see _PHASE1_SECRET_FIELDS)

get_device_ipsec_phase1_interfaceA

Get one IPsec phase1-interface (remote gateway) from a device's device DB.

Args: device: Managed device name name: Phase1-interface (remote gateway) name vdom: VDOM it lives in (default "root")

Returns: dict with device, vdom and the stored object (credential fields stripped -- see _PHASE1_SECRET_FIELDS), or a not_found error

create_device_ipsec_phase1_interfaceA

Create an IPsec phase1-interface (remote gateway / IKE SA) in a device's device DB.

Route-based (interface-mode) tunnel only -- this always writes an interface (the outgoing local interface), matching the phase1-interface table this tool targets rather than the legacy policy-based phase1 table.

Args: device: Managed device name name: New phase1-interface (remote gateway) name interface: Local physical/aggregate/loopback/VLAN outgoing interface type: Remote gateway type: "static", "dynamic" or "ddns"; default "static" remote_gw: Remote gateway's external IPv4 address. Required when type is "static" -- a static gateway with no address cannot connect psksecret: Pre-shared key for PSK authentication, 6-128 characters ike_version: IKE protocol version, "1" or "2" mode: IKEv1 ID protection mode: "main" or "aggressive" (ignored on IKEv2) peertype: Peer ID acceptance: "any", "one", "dialup", "peer" or "peergrp". Defaults to "any" when type is "static" and no value is given -- live-verified against FMG 7.6.7: a static PSK gateway created without peertype is rejected with "peer invalid value", so this tool supplies the working default rather than surfacing that error to the caller proposal: Phase1 encryption/authentication proposal list (e.g. ["aes256-sha256"]). Values are passed through to FortiManager largely unvalidated -- see module docstring dhgrp: Phase1 Diffie-Hellman group list (e.g. ["14", "21"]). Same pass-through as proposal nattraversal: NAT traversal: "disable", "enable" or "forced" dpd: Dead Peer Detection mode: "disable", "on-idle" or "on-demand" keylife: Phase1 key life in seconds comments: Free-text comment vdom: VDOM the gateway lives in (default "root")

Returns: Creation result with any echoed credential stripped

update_device_ipsec_phase1_interfaceA

Update fields on a device-DB IPsec phase1-interface. Only provided fields change.

Args: device: Managed device name name: Phase1-interface (remote gateway) name to update interface: New local outgoing interface type: New remote gateway type: "static", "dynamic" or "ddns" remote_gw: New remote gateway IPv4 address psksecret: New pre-shared key, 6-128 characters ike_version: New IKE protocol version, "1" or "2" mode: New IKEv1 ID protection mode: "main" or "aggressive" peertype: New peer ID acceptance mode proposal: Replacement phase1 proposal list dhgrp: Replacement phase1 DH group list nattraversal: New NAT traversal setting dpd: New Dead Peer Detection mode keylife: New phase1 key life in seconds comments: New free-text comment vdom: VDOM the gateway lives in (default "root")

Returns: Update result with any echoed credential stripped

delete_device_ipsec_phase1_interfaceA

Delete an IPsec phase1-interface from a device's device DB.

Any phase2-interface still pointing at this phase1name should be deleted first (delete_device_ipsec_phase2_interface) -- FortiManager rejects deleting a phase1-interface a phase2-interface still references.

Args: device: Managed device name name: Phase1-interface (remote gateway) name to delete vdom: VDOM it lives in (default "root")

Returns: Deletion result

list_device_ipsec_phase2_interfacesA

List IPsec phase2-interface (tunnel/selector) definitions in a device's device DB.

Args: device: Managed device name vdom: VDOM to read (default "root")

Returns: dict with device, vdom, count and interfaces

get_device_ipsec_phase2_interfaceA

Get one IPsec phase2-interface (tunnel/selector) from a device's device DB.

Args: device: Managed device name name: Phase2-interface (tunnel) name vdom: VDOM it lives in (default "root")

Returns: dict with device, vdom and the stored object, or a not_found error

create_device_ipsec_phase2_interfaceA

Create an IPsec phase2-interface (tunnel/traffic selector) in a device's device DB.

Args: device: Managed device name name: New phase2-interface (tunnel) name phase1name: Parent phase1-interface (remote gateway) name this tunnel is negotiated under src_subnet: Local proxy ID subnet, CIDR ("192.168.1.0/24") or "address netmask" form. Omitted means the FortiOS default (0.0.0.0/0, the whole local network) dst_subnet: Remote proxy ID subnet, same accepted forms. Omitted means the FortiOS default (0.0.0.0/0) proposal: Phase2 encryption/authentication proposal list (e.g. ["aes256-sha256"]). Pass-through, see module docstring dhgrp: Phase2 PFS Diffie-Hellman group list. Pass-through, see module docstring pfs: Perfect Forward Secrecy: "disable" or "enable" replay: Replay detection: "disable" or "enable" keepalive: Keep-alive: "disable" or "enable" auto_negotiate: IPsec SA auto-negotiation: "disable" or "enable" keylifeseconds: Phase2 key life in seconds (120-172800) comments: Free-text comment vdom: VDOM the tunnel lives in (default "root")

Returns: Creation result

update_device_ipsec_phase2_interfaceA

Update fields on a device-DB IPsec phase2-interface. Only provided fields change.

Args: device: Managed device name name: Phase2-interface (tunnel) name to update phase1name: New parent phase1-interface name src_subnet: New local proxy ID subnet, CIDR or "address netmask" form dst_subnet: New remote proxy ID subnet, same accepted forms proposal: Replacement phase2 proposal list dhgrp: Replacement phase2 DH group list pfs: New Perfect Forward Secrecy setting replay: New replay detection setting keepalive: New keep-alive setting auto_negotiate: New IPsec SA auto-negotiation setting keylifeseconds: New phase2 key life in seconds (120-172800) comments: New free-text comment vdom: VDOM the tunnel lives in (default "root")

Returns: Update result

delete_device_ipsec_phase2_interfaceB

Delete an IPsec phase2-interface from a device's device DB.

Args: device: Managed device name name: Phase2-interface (tunnel) name to delete vdom: VDOM it lives in (default "root")

Returns: Deletion result

get_device_sslvpn_settingsA

Get the SSL-VPN (Agentless VPN) settings object from a device's device DB.

This is a per-vdom singleton -- there is exactly one, so no list/create/ delete tools exist for it, matching the FNDN schema's "object" (not "table") classification.

Args: device: Managed device name vdom: VDOM to read (default "root")

Returns: dict with device, vdom and the stored settings object

update_device_sslvpn_settingsA

Update the SSL-VPN (Agentless VPN) settings object in a device's device DB.

Only provided fields change -- see get_device_sslvpn_settings for the current state to base a partial update on.

Args: device: Managed device name status: Enable/disable SSL-VPN: "disable" or "enable" port: SSL-VPN access port (1-65535) source_interface: Source interface names of incoming traffic default_portal: Default web portal name (see update_device_sslvpn_web_portal) servercert: Server certificate name to present to clients idle_timeout: Idle timeout in seconds before disconnect auth_timeout: Authentication timeout in seconds (1-259200, 0 = no timeout) dns_server1: Primary DNS server handed to clients dns_server2: Secondary DNS server handed to clients reqclientcert: Require client certificates: "disable" or "enable" https_redirect: Redirect port 80 to the SSL-VPN port: "disable" or "enable" ssl_min_proto_ver: Minimum TLS version: "tls1-0", "tls1-1", "tls1-2" or "tls1-3" vdom: VDOM the settings apply to (default "root")

Returns: Update result

get_device_sslvpn_web_portalA

Get an SSL-VPN web portal from a device's device DB.

Args: device: Managed device name name: Web portal name vdom: VDOM it lives in (default "root")

Returns: dict with device, vdom and the stored portal object (bookmark credentials stripped -- see _PORTAL_SECRET_FIELDS), or a not_found error

update_device_sslvpn_web_portalA

Update an SSL-VPN web portal in a device's device DB.

The portal itself must already exist in the device DB (created via the FortiOS CLI/GUI or a prior install) -- this tool only updates an existing entry, matching the "get/update" scope of this module. Only provided fields change.

Args: device: Managed device name name: Web portal name to update tunnel_mode: Enable/disable full-tunnel mode: "disable" or "enable" split_tunneling: Enable/disable split tunneling: "disable" or "enable" ip_pools: Firewall address/address-group names to draw tunnel-mode client IPs from web_mode: Enable/disable web-mode (clientless HTML5) access: "disable" or "enable" dns_server1: Primary DNS server handed to portal clients dns_server2: Secondary DNS server handed to portal clients vdom: VDOM the portal lives in (default "root")

Returns: Update result

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

TDQS

B3.2/5.0

Scored across 233 tools

Disambiguation2/5

The set has many large familes with overlapping boundaries: list_templates/list_system_templates/list_sdwan_templates/list_cli_template_groups/list_template_groups, get_device_interfaces vs get_device_interface_config, device sniffer vs on-demand sniffer vs FortiManager packet capture, and several revision/diff tools. Descriptions usually explain the difference, but with 233 tools an agent will frequently face plausible near-matches.

Naming Consistency4/5

Names are consistently snake_case and mostly follow a verb_noun pattern (list_*, get_*, create_*, update_*, delete_*). Minor deviations exist where add_* is used instead of create_* for devices/groups/templates, and IPv6/local-in variants introduce suffixes like local_in_policies6, but the overall convention remains readable.

Tool Count1/5

233 tools is far beyond a practical MCP tool surface; even for a broad platform like FortiManager, the set is too large to select from reliably and should be consolidated into fewer domain-level tools. The count alone creates a severe context and misselection burden.

Completeness4/5

Coverage is extensive across ADOMs, devices, device-DB objects, firewall policies, local-in policies, security profiles, templates, SD-WAN, revisions, scripts, firmware, and tasks. There are still lifecycle gaps, such as limited create/update/delete coverage for some template families and missing single-object getters or deletes for a few device-DB objects, but core workflows are well covered.

Maintenance

ActivityMaintained
ResponsivenessUsually handled