Automating FortiManager Global and Local ADOMs using Python

Maintaining a FortiManager (FMG) environment at scale requires a precise understanding of the dependency hierarchy between the Global ADOM and the Local ADOMs. This article breaks down the programmatic logic used to safely automate schema upgrades while ensuring database integrity.

1. The Metadata Identity (Bitmasks & OIDs)

Every Administrative Domain (ADOM) is a distinct database instance. The API identifies these instances using two critical identifiers:

  • OID (Object ID): OID 10 is the reserved technical identifier for the Global Database.
  • restricted_prds (Product Bitmask): An integer defining which product engine the ADOM uses.

The Logic

We avoid filtering by name (which is user-configurable) and instead filter by the bitmask to ensure we only target “Upgradable” engines.

Note: The FortiProxy ADOM can be upgraded using this method but the syntax conversion will not happen because officially upgrading this ADOM is not supported as of now – refer to Fortinet Doc: FortiProxy ADOMs.

UPGRADABLE_ADOMS = {
    1: "FortiGate (FortiOS)",
    2: "FortiCarrier",
    8192: "FortiProxy",
    32768: "FortiFirewall",
    8388608: "FortiFirewallCarrier",
    4503599627370495: "Fabric"
}

2. Dynamic Target Discovery (The "FMG Ceiling")

The script is “Environment Aware.” Before any upgrade begins, it queries the FortiManager’s own system version. This creates a Maximum Ceiling. An ADOM cannot have a higher schema version than the Manager’s own firmware.

The Command Logic:

status_res = fmg_rpc("get", "/sys/status")
fmg_max_ver = f"{status['Major']}.{status['Minor']}"

If your FMG is on 7.6, the script identifies that it has the capacity to bring ADOMs up to 7.6, but it will respect the sequential path required to get there safely.

3. The Selective Exclusion (The Ignore List)

A FortiManager contains many “system” ADOMs and specialty product containers that do not follow the standard FortiOS upgrade path. Attempting to trigger an upgrade API on these would result in RPC errors or system instability.

The Logic: We define a comprehensive UPGRADE_IGNORE set. This list serves two purposes:

  • Safety: Prevents API calls to non-target containers like FortiAnalyzer or Syslog.
  • Reporting: Allows the script to categorize these ADOMs as “Ignored” in the final report rather than “Failed.”
UPGRADE_IGNORE = {
    "rootp", "Unmanaged_Devices", "Syslog", "others", 
    "FortiManager", "FortiMail", "FortiAnalyzer", "FortiWeb", 
    "FortiCache", "FortiSandbox", "FortiAuthenticator", "FortiClient",
    "FortiDDoS", "FortiDeceptor"
}

4. The n+2 Sequential Migration Path

FortiOS databases are sensitive to version jumps. Attempting a jump from to via API can lead to “orphan objects” or corrupted policy packages because the intermediary schema transformations were skipped.

The Logic:

We implement a mathematical step-up logic:

# Sequential target calculation: Global version + 0.2, capped at FMG max
target_v = min(float(fmg_max_ver), float(current_global_ver) + 0.2)
target_str = f"{target_v:.1f}"

If a Local ADOM is on 7.2, the script targets 7.4. It will only attempt the next jump to 7.6 in a subsequent execution pass, ensuring the database “check-sums” remain valid at every version milestone.

5. The Global-to-Local Synchronization Guard

This is the most critical architectural constraint. The Global ADOM provides shared objects (Address objects, Services, and Policy Headers) that are “assigned” into Local ADOMs.

The Constraint: If the Global ADOM is upgraded to a newer schema (e.g., 7.4) while a Local ADOM remains on an older schema (e.g., 7.2), you create a Schema Incompatibility. The Local ADOM database engine will be unable to process or “read” the newer attributes associated with global objects.

The Guardrail Logic: We introduce a strict sequential gatekeeper:

  • Iterate Local ADOMs: The script processes and upgrades all eligible Local ADOMs first.
  • Version Verification: After the local loop, the script re-verifies all versions.
  • Hold Global: The Global upgrade command is withheld until every Local ADOM has caught up to the target version.
# Verification loop before touching Global
all_ready = True
for name, vdata in version_map.items():
    if name in UPGRADE_IGNORE: continue
    # If any production ADOM is still below target, block the Global upgrade
    if float(vdata["curr"]) < target_v:
        all_ready = False

6. Asynchronous Task Monitoring (In-Line Telemetry)

FMG API calls are Asynchronous. When the script sends an _upgrade command, the FMG returns a task_id and processes the request as a background system job.

The Logic: The script uses a polling loop to monitor the Task Manager. We use a carriage return (\r) combined with the ADOM name to keep the terminal output clean and provide real-time telemetry:

def wait_for_task(task_id, session_id, adom_name):
    while True:
        # Querying the specific task ID for progress percentage
        task_res = fmg_rpc("get", f"/task/task/{task_id}", session=session_id)
        percent = task_res.get("result", [{}])[0].get("data", {}).get("percent", 0)
        
        # \r moves the cursor back to the start of the line to overwrite progress
        print(f"    >>> Upgrading '{adom_name}': {percent}%", end='\r')
        
        if int(percent) >= 100:
            print() # Print a newline to "lock in" the 100% status
            break
        time.sleep(2) # Prevent API rate-limiting

Only when the Task Manager reports 100% (Success) does the script move to the next ADOM. This prevents overwhelming the FMG system resources with multiple simultaneous database rebuilds.

7. Auditability and Comparative Reporting

The final stage of the orchestration is the generation of a comparative delta report. Using Python f-string padding ({name:<25}), the report is formatted into a clean grid. The <25 syntax instructs Python to reserve 25 characters of space and left-align the text, ensuring that the “Product Type” and “Version” columns remain perfectly vertical.

=================================================================
FORTIMANAGER SYSTEM VERSION: 7.6
CURRENT GLOBAL ADOM VERSION: 7.2
=================================================================
[2026-02-01 00:25:16 UTC] STEP 1: Upgrading local ADOMS first...
    >>> Upgrading Local 'Canada' (7.2 -> 7.4)
    Task Progress: 100%
    >>> Upgrading Local 'FortiFirewallCarrier' (7.2 -> 7.4)
    Task Progress: 100%
    >>> Upgrading Local 'FortiProxy' (7.2 -> 7.4)
    Task Progress: 100%
    >>> Upgrading Local 'United_States' (7.2 -> 7.4)
    Task Progress: 100%
    >>> Upgrading Local 'root' (7.2 -> 7.4)
    Task Progress: 100%
-----------------------------------------------------------------
[2026-02-01 00:25:55 UTC] STEP 2: All upgradable local ADOMs are upgraded to 7.4.
[2026-02-01 00:25:55 UTC] STEP 3: Now upgrading Global Database to 7.4...
    Task Progress: 100%

=======================================================================================================
                                       FINAL ADOM UPGRADE STATUS REPORT                                       
=======================================================================================================
ADOM Name                 | Product Type              | Prev Ver   | Curr Ver   | Status
-------------------------------------------------------------------------------------------------------
Canada                    | FortiGate (FortiOS)       | 7.2        | 7.4        | Upgraded
FortiCarrier              | FortiCarrier              | 7.4        | 7.4        | Not Upgraded
FortiFirewall             | FortiFirewall             | 7.4        | 7.4        | Not Upgraded
FortiFirewallCarrier      | FortiFirewallCarrier      | 7.2        | 7.4        | Upgraded
FortiProxy                | FortiProxy                | 7.2        | 7.4        | Upgraded
Oceania                   | Fabric                    | 7.4        | 7.4        | Not Upgraded
United_States             | FortiGate (FortiOS)       | 7.2        | 7.4        | Upgraded
root                      | FortiGate (FortiOS)       | 7.2        | 7.4        | Upgraded
rootp                     | Global Database           | 7.2        | 7.4        | Upgraded
FortiAnalyzer             | Non-Upgradable/System     | 7.6        | 7.6        | Ignored
FortiAuthenticator        | Non-Upgradable/System     | 6.6        | 6.6        | Ignored
FortiCache                | Non-Upgradable/System     | 4.2        | 4.2        | Ignored
FortiClient               | Non-Upgradable/System     | 7.4        | 7.4        | Ignored
FortiDDoS                 | Non-Upgradable/System     | 7.0        | 7.0        | Ignored
FortiDeceptor             | Non-Upgradable/System     | 6.1        | 6.1        | Ignored
FortiMail                 | Non-Upgradable/System     | 7.6        | 7.6        | Ignored
FortiManager              | Non-Upgradable/System     | 7.6        | 7.6        | Ignored
FortiSandbox              | Non-Upgradable/System     | 5.0        | 5.0        | Ignored
FortiWeb                  | Non-Upgradable/System     | 7.6        | 7.6        | Ignored
Syslog                    | Non-Upgradable/System     | 0.0        | 0.0        | Ignored
Unmanaged_Devices         | Non-Upgradable/System     | 7.6        | 7.6        | Ignored
others                    | Non-Upgradable/System     | 6.0        | 6.0        | Ignored
=======================================================================================================
[2026-02-01 00:26:04 UTC] SESSION CLOSED.

Explanation of the report above:

 

  • Initial Environment Baseline: The script identified a 7.6 Manager Ceiling but a 7.2 Global ADOM. To prevent database corruption or “orphan objects” caused by skipping major releases, it strictly enforced the path, selecting 7.4 as the mandatory intermediary target.
  • Locals-Lead Logic (Step 1): Five production ADOMs—Canada, United_States, root, FortiFirewallCarrier, and FortiProxy—were moved from 7.2 to 7.4 first. Upgrading them first ensures they possess the necessary database schema to recognize and process newer object attributes before the Global Database changes – if used.
  • The report shows root, Canada, and United_States as “Upgraded” because they required the jump to 7.4. Conversely, Oceania, FortiCarrier, and FortiFirewall were marked “Not Upgraded” because the script’s environmental awareness detected they were already at 7.4, bypassing them to save system resources.
  • Synchronized Progress Tracking: The script utilized Asynchronous Task Monitoring, polling the FortiManager Task Manager for each specific task_id. It provided real-time telemetry and refused to proceed to the next phase until every local task reached 100% success, ensuring no ADOM was left behind.
  • The Global Database (rootp) was upgraded from 7.2 to 7.4 only after Phase 1 was verified. 
  • System-level ADOMs like FortiMail, FortiAnalyzer, and FortiWeb were correctly bypassed. 
  • Convergence Report: The final summary provides a high-fidelity audit trail. It confirms that the environment has reached a Uniform 7.4 Baseline, neatly categorizing ADOMs by their transition status (Upgraded, Not Upgraded, or Ignored) to facilitate easy verification for change management records.
Scroll to Top