Hi,
I search how to do a "show" in the ADOM policy package. Is there a way to do that ?
#!
proc do_db {package cmd} { puts [exec_ondb "/adom/LAB_LBA/pkg/$package" "$cmd\n" "# "] } do_db "default" " show firewall policy "
-> output :
DEBUG INFO: TCL command exec_ondb: target = /adom/LAB_LBA/pkg/default #
the goal is to show all policy in a policy package.. Thanks in advance for your help !
Lucas
Hi Lucas,
So I'm not sure running a "show" script would work like that from the FMG, but you can view all policies of a particular ADOM and policy package using the example syntax below:
exec fmpolicy print-adom-package root 887 181 all
Where "root" would be your desired ADOM
Where "887" would be the ID assigned to you policy package
Where "181" would be firewall policy
You can use "all" to specify all policies of that type in your selected policy package or you can select an individual policy ID.
You can type a "?" at the four last entries to help you select your desired entries as well.
Best regards,
Alan
Hi Alan,
Thanks for your response, but it's look like the "execute ..." doesn't work in TCL script : DEBUG INFO: TCL command exec_ondb: target = /adom/LAB_LBA/pkg/default > execute fmpolicy print-adom-package LAB_LBA 887 181 all Failed to commit to DB, reason(> execute fmpolicy print-adom-package LAB_LBA 887 181 all ) ERROR INFO: __exec_ondb: Cannot execute the command in tcl script.
Lucas
Hi Lucas,
Sorry for the confusion, looks like I forgot to add that command would be run in the command line of FortiManager not in a script.
When you get to the command line, the ID of your policy package might be different than 887. So executing command "execute fmpolicy print-adom-package LAB_LBA ?" would give you the policy package ID number you seek. The rest (181 all) should be the same.
Regards,
Alan
Well, it's 2024 and this requirement still exists. The challenge is to get output from "show" commands inside of an FMG TCL script. As the request above asks, we want to be able to capture the firewall policy rules in a TCL script so that we can parse them in TCL and configure "stuff" on the basis of the policy rules that are inside an ADOM policy package.
Thus, I have tried the following:
puts [exec_ondb "/adom/./pkg/default" "config firewall policy\nshow full\n" "# "]
Which does not product any output (but does not error) but does show that command was executed.
Starting log (Run on device)
DEBUG INFO:
TCL command exec_ondb: target = /adom/./pkg/default
config firewall policy
#
----------------End of Log-------------------------
I have also tried:
puts [exec_ondb "/adom/./pkg/default" "show firewall policy\n" "# "]
Which produces no output and doesn't seem to show the command being executed at all (but there's no error either).
Starting log (Run on device)
DEBUG INFO:
TCL command exec_ondb: target = /adom/./pkg/default
#
----------------End of Log-------------------------
So how can we get the output of adom package objects from within a TCL script? Is it possible?
Thanks, /Katoomba
Created on 08-21-2024 11:29 AM Edited on 08-23-2024 08:21 AM
EUREKA!
Introduction
I found a way to run CLI commands on FMG from a TCL script. It requires using a FortiGate as a jump box to SSH back into the FMG CLI and to then run exec fmpolicy commands (or any FMG commands). The output from FMG can then be used inside the TCL script. This is a kind of "hairpin" where the FMG TCL script logs into a Fortigate which then SSH's back into the FMG to execute FMG CLI commands. It's a kludge/work around for TCL scripts because FMG TCL does not have any direct access to FMG CLI commands.
Where is this useful?
There are times where it is desirable to be able to change objects in the ADOM database (such as, for example: policy rules, address objects or security profile objects, to name just a few). CLI scripts can be used for this but a drawback exists that there is no way to programmatically adjust the configuration commands that are applied to the ADOM objects because CLI scripts (of the NON provisioning template type) do not support jinja and thus have no conditional logic available to them. You just run the CLI script and it applies the configuration as it appears in the script. The same also applies when you run CLI scripts against the device database. Add to that, CLI scripts don't have much support for meta variables.
On the other hand, TCL scripts are set up to run against FortiGates only. You cannot run a TCL script against the device database or against the "policy package or ADOM". But wait a second! There is a function within TCL called "exec_ondb" that allows configuration commands to be applied to the ADOM database and policy packages. OK great, we can apply changes to the ADOM database or policy packages but in order to make logic decisions about what should be changed and what the changes might be we need to know what's in the ADOM database or policy package. Without the ability to do that, many use cases become impossible.
An Example
For example, what if we could examine each rule in a firewall policy package and add the text "IPS ALERT" to the name (or comment) of the rule when we find that there is no IPS sensor configured on that rule? Given what we know about CLI and TCL scripts, there is no way to determine what the policy rules actually look like in the ADOM. Well, now we do have a way to determine that. Because the script below shows how it is possible to reach the FortiManager CLI to run "execute fmpolicy ..." commands, the output of which becomes available for processing by the TCL script. And then, "exec_ondb" commands may be used to apply changes to the ADOM or policy packages.
Summary
So in summary, two crucial functions are needed for a complete TCL script solution. First, it must be possible to determine the current configuration of items within the ADOM database and policy packages. The script below demonstrates this functionality by using a FortiGate to act as a gateway to reach the FortiManager CLI where FMG CLI commands can be executed. Second, it must be possible to make changes to the ADOM or policy packages. The "exec_ondb" command provides that functionality. With these two capabilities, and with the flexibility that TCL itself provides, it is now possible to open up a much broader set of use cases that can be implemented using TCL scripts.
#!/usr/bin/tclsh
############################################################
############################################################
# This script demonstrates how to build a TCL script that
# uses a FortiGate as a jump box to reach FortiManager (FMG)
# to then run "exec fmpolicy..." commands on the FMG itself.
# NOTE: You can run any valid FMG CLI command this way.
############################################################
############################################################
# set FMG variables
set FMG_HOST "172.16.31.31"
set FMG_USER "TCL_User"
set FMG_PASS "TCL_User"
############################################################
############################################################
# define procedures section
############################################################
# execute commands
proc do_cmd {cmd} {
# note that the prompt (#|\$) is special to handle non super user FMG users
puts [exec "$cmd\n" " (#|\$) " 10]
}
proc get_sys_status aname {
upvar $aname a
set a(vdom) true
set input [exec "get system status\n" "# " 15 ]
set linelist [split $input \n]
foreach line $linelist {
if {[regexp {Virtual domain configuration: disable} $line]} { set a(vdom) false }
if {![regexp {([^:]+):(.*)} $line dummy key value]} continue
switch -regexp -- $key {
Version { regexp {FortiGate-([^ ]+) ([^,]+),build([\d]+),.*} $value dummy a(platform) a(version) a(build) }
Serial-Number { set a(serial-number) [string trim $value] }
Hostname { set a(hostname) [string trim $value] }
}
}
}
############################################################
############################################################
# set verbose to true to increase output
set verbose true
############################################################
############################################################
# begin main script
############################################################
# get FortiGate information
get_sys_status status
if { ($verbose == true) } {
puts "This Fortigate is model: \[$status(platform)\]."
puts "It is running FortOS version: \[$status(version)\]."
puts "The firmware is build number: \[$status(build)\]."
puts "The device serial number is: \[$status(serial-number)\]."
puts "The machine hostname is: \[$status(hostname)\]."
}
############################################################
############################################################
# enter vdom if vdoms are enabled
if { ($status(vdom) == true) } {
# Enter VDOM if its enabled
if { ($verbose == true) } { puts "Entering vdom:\[root\]" }
do_cmd "config vdom"
do_cmd "edit root"
} else {
if { ($verbose == true) } { puts "No vdoms on this Fortigate" }
}
############################################################
############################################################
# SSH into FMG from the FortiGate
puts [exec "execute ssh $FMG_USER@$FMG_HOST\n" "Password: " 10]
do_cmd $FMG_PASS
############################################################
############################################################
# do FMG commands
do_cmd "execute fmpolicy print-adom-package 201 1 7202 181 all"
do_cmd "exit"
############################################################
############################################################
puts "Script Finished"
do_cmd "end"
# End of script
############################################################
Implementation Notes
Note that variables FMG_HOST, FMG_USER, and FMG_PASS all need to be replaced to match your own FMG setup. Also, note that the fmpolicy command parameters will need to be changed to match your own requirements. Also, you must make sure that your "gateway" "jump box" FortiGate has access to the FortiManager server on SSH (TCP/22). When you run the TCL script, you will choose (select) that FortiGate as the selected entry for the TCL script.
Security Considerations
CAUTION: this script requires use of FMG credentials appearing in clear text and that presents risks that should be considered and handled. It is best to create a new admin profile in FMG (below image shows "TCL_Profile"). To run the fmpolicy commands, the profile will only need Read-Only access to "Add/Delete/Edit Devices/Groups". WARNING: that specific permission is quite broad in scope!
Then create a new administrator (called say, TCL_User) and assign the TCL_Profile to that user. Then use the TCL_User credentials in the TCL script.
If you want to run other commands in FMG CLI from the TCL script then the profile permissions might need to be adjusted to allow those commands to be executed. Do NOT give the TCL admin user more rights than it needs and carefully consider the ramifications of the rights that you assign to the TCL user.
Wrap Up
The functionality that we have discussed provides extended capabilities for TCL scripts running within FortiManager. TCL itself is very powerful but it should be kept in mind that the version running under FortiManager is cut down and limited and only the following packages are included: zlib TclOO tcl::tommath Tcl. Nevertheless, the ability to run FortiManager CLI commands within TCL scripts enhances their capabilities enormously. Enjoy!
Other Resources
For Fortinet documentation regarding TCL scripts see here.
Do you want to send an email from a FortiManager TCL Script? See here.
This is the most useful comment I've found while trying to figure out why TCL scripting doesn't work like expected or has very good documentation. When I try to SSH back into the fortimanager I'm getting an error on my script that says
Starting log (Run on device)
ERROR INFO:
Cannot get the information from the device ...
----An error occured at line #30 of the script----
Here's line 30. I have tested this command manually on the fortigate directly and it works fine. Any ideas or have you run into this?
puts [exec "execute ssh $FMG_USER@$FMG_HOST\n" "Password: " 30]
Hi Mike, line #30 in your script doesn't line up with the same command in my example script. So perhaps you need to post your script here in full. Otherwise, it's going to be very hard to offer any help.
Created on 12-10-2024 04:01 AM Edited on 12-10-2024 04:03 AM
Note that this isn't fully tested and debugged so hopefully no-one uses it directly without their own testing. I ended up with a lot of address objects with dynamic maps that are an exact copy of the parent object so this checks if key fields match between the dynamic mapping and the address object and then deletes the map. I skipped the part you had about checking for VDOMs because I don't have any.
#!
set FMG_HOST "xxx.xxx.xxx.xxx"
set FMG_USER "TCL_SSH_USER"
set FMG_PASS "xxxxxxxxxxxxxxx"
proc reset_array array {
upvar $array a
unset a
# Set any value that needs to be compared later to whatever an unset value equals
# since an unset associated interface means any we'll use that
set a(associated-interface) {"any"}
set a(type) ""
set a(subnet) ""
set a(fqdn) ""
set a(start-ip) ""
set a(end-ip) ""
}
proc do_local_cmd {cmd} {
# note that the prompt (#|\$) is special to handle non super user FMG users
return [exec "$cmd\n" " (#|\$) " 10]
}
proc do_db_cmd {cmd} {
# puts $cmd\n
return [exec_ondb "/adom/FG7-2/pkg/default" "$cmd\n" "# "]
}
# Get firewall addesses via ssh to FMG, store as existing_config then split to lines
puts [exec "execute ssh $FMG_USER@$FMG_HOST\n" "Password: " 30]
do_local_cmd $FMG_PASS
set existing_config [do_local_cmd "execute fmpolicy print-adom-object FG7-2 "firewall address" all"]
do_local_cmd "exit"
set config_lines [split $existing_config \n]
# remove intro lines through config firewall address and final end
set config_lines [lrange $config_lines 3 end-1]
# setup variables for tracking where we are in the config
# objects for tracking place in file, empty between objects
reset_array address
reset_array mapping
# track if in a dynamic mapping sub block
set in_dynamic_mapping 0
# list of mappings to remove, reset every address object
set maps_to_remove [list]
# loop over lines and pick out needed data
foreach line $config_lines {
# if we aren't processing an address yet, look for the edit command to get name
if {![info exists address(name)]} {
if {[regexp {edit[ ]+(".+")} $line match address(name)]} {
# puts "Processing $address(name)"
# continue because saving the name was done at the same time as checking the regex
continue
}
}
# check if currently in an address but not dynamic mapping
if {[info exists address(name)] & $in_dynamic_mapping == 0} {
# find edit line and save name to start processing address
if {[regexp {set[ ]+([\w\-]+)[ ]+(.*)} $line match key value]} {
set address($key) $value
continue
} elseif {[regexp {config dynamic_mapping} $line]} {
set in_dynamic_mapping 1
continue
} elseif {[regexp {next} $line]} {
# leaving current address object make changes and reset variables
if {[llength $maps_to_remove] > 0} {
do_db_cmd "config firewall address"
do_db_cmd "edit $address(name)"
do_db_cmd "config dynamic_mapping"
foreach map $maps_to_remove {
do_db_cmd "delete $map"
}
# exit dynamic mapping config
do_db_cmd "end"
# exit address object
do_db_cmd "next"
# exit address config
do_db_cmd "end"
}
# reset maps to remove
set maps_to_remove [list]
# reset address array
reset_array address
continue
}
# steping into processing dynamic mappings
} elseif {[info exists address(name)] & $in_dynamic_mapping} {
if {[regexp {edit[ ]+(".+")} $line match mapping(name)]} {
# continue because saving the name was done at the same time as checking the regex
continue
} elseif {[regexp {set[ ]+([\w\-]+)[ ]+(.*)} $line match key value]} {
set mapping($key) $value
continue
} elseif {[regexp {next} $line match key value]} {
# leaving current mapping compare critical fields and add to cleanup list if match
if {
$address(associated-interface) eq $mapping(associated-interface) &
$address(type) eq $mapping(type) &
$address(subnet) eq $mapping(subnet) &
$address(fqdn) eq $mapping(fqdn) &
$address(start-ip) eq $mapping(start-ip) &
$address(end-ip) eq $mapping(end-ip)
} {
lappend maps_to_remove $mapping(name)
}
# reset mapping array
reset_array mapping
} elseif {[regexp {end} $line]} {
set in_dynamic_mapping 0
continue
}
}
}
Select Forum Responses to become Knowledge Articles!
Select the “Nominate to Knowledge Base” button to recommend a forum post to become a knowledge article.
User | Count |
---|---|
1741 | |
1109 | |
755 | |
447 | |
240 |
The Fortinet Security Fabric brings together the concepts of convergence and consolidation to provide comprehensive cybersecurity protection for all users, devices, and applications and across all network edges.
Copyright 2025 Fortinet, Inc. All Rights Reserved.