A FortiGate comes with several tools built in to help us with automation and responding to events. In this post, I want to explore these options a bit more and show what you can do with them.
So, dear reader, let’s see what we can do with these tools!
The setup:
- 2x FortiGate 70G running 7.6.6
- A FortiAnalyzer VM running 7.6.6
Automation stitches
The biggest, most powerful, and most dynamic option a FortiGate offers are automation stitches. You combine actions, which do something, and triggers, which respond to something, in a stitch, where you handle the logic and the flow, like sequential and parallel execution of actions and delays.
Before I get more into this part, please check out Yuri Slobodyanyuk’s blog post on automation stitches because he already did a lot of work on this topic.
Automation actions
Actions in stitches are the things actually being done. You can send notifications to Slack, Teams, send mails, trigger functions in cloud environments, execute CLI scripts, or execute webhooks.
There isn’t much to say about actions because they are rather self-explanatory, but one thing I want to mention is that CLI scripts are limited to 1023 characters, and you can use some variables in them.

Automation triggers
Triggers are the second thing we need to care about, because if we don’t know what event interests us, we won’t do anything in response.
FortiOS comes with some default triggers, like Configuration Change, HA Failover, and Conserve Mode, but I will focus on FortiOS Event Log, FortiAnalyzer Event Handler, and Incoming Webhook, because they offer the most flexibility.
FortiOS Event Log triggers
You cannot trigger on every event log, but the ones we can choose from are vast and knowing how to work with them is important.
Let’s take a look at two practical examples.
Example: Link monitor events
First, assume that you are monitoring a server using a link monitor and you need to respond to it being down, like enabling a static route.
The Link monitor status event produces the following log entry:
date=2026-05-02 time=16:19:29 eventtime=1777738768978397230 logid="0100022922" type="event" subtype="system" level="notice" vd="root" logdesc="Link monitor status" name="WIN-AD" msg="Link monitor state is changed from 0 to 1, please check if this triggers HA failover."

In this case, it is a down event (changed from 0 to 1). There is also a Link monitor status warning event, which makes this particular scenario clearer and easier to work with, but I won’t focus on that for a reason I will get to later.
In the log details, we can see the message (msg) and the link monitor name (name). This information is enough to build an automation trigger specific to this link monitor using Field filters.

config system automation-trigger
edit "LINK-MONITOR-DOWN"
set event-type event-log
set logid 22922
config fields
edit 1
set name "name"
set value "WIN-AD"
next
edit 2
set name "msg"
set value "*from 0 to 1*"
next
end
next
end
The wildcard symbol * does the magic here, because it allows us to focus on only what we need in the message and the name field covers the specific link monitor.
Use the trigger in a stitch and add whatever action you need. You can also build the reverse trigger, i.e. a link monitor coming alive, using the same event and name, but the message *from 1 to 0*.
The reason I am not using the Link monitor status warning event here is that a dead link monitor uses the warning event (ID 0100022932) in addition to the Link monitor status event (ID 0100022922). A link monitor coming alive uses only Link monitor status (ID 0100022922). I don’t want to use two different IDs for one process (link monitor alive and dead), but you can choose to do that.
Here are all the log messages for these scenarios:
date=2026-05-02 time=16:19:41 eventtime=1777738780479592552 logid="0100022922" type="event" subtype="system" level="notice" vd="root" logdesc="Link monitor status" name="WIN-AD" msg="Link monitor state is changed from 1 to 0, please check if this triggers HA failover."
date=2026-05-02 time=16:19:41 eventtime=1777738780479544752 logid="0100022922" type="event" subtype="system" level="notice" vd="root" logdesc="Link monitor status" name="WIN-AD" interface="undefined" probeproto="ping" msg="Link Monitor changed state from dead to alive, protocol: ping."
date=2026-05-02 time=16:19:29 eventtime=1777738768978397230 logid="0100022922" type="event" subtype="system" level="notice" vd="root" logdesc="Link monitor status" name="WIN-AD" msg="Link monitor state is changed from 0 to 1, please check if this triggers HA failover."
date=2026-05-02 time=16:19:29 eventtime=1777738768978357571 logid="0100022932" type="event" subtype="system" level="warning" vd="root" logdesc="Link monitor status warning" name="WIN-AD" interface="undefined" probeproto="ping" msg="Link Monitor changed state from alive to dead, protocol: ping."

Example: BGP neighborship events
The second example is around BGP neighborships. Assume that you want to create a trigger for a specific BGP neighbor going down and enable a static route if such an event happens. If we look at the logs for a BGP neighborship down event, we see the following:
date=2026-05-02 time=16:23:18 eventtime=1777738998290854913 logid="0103020300" type="event" subtype="router" level="warning" vd="root" logdesc="BGP neighbor status changed" msg="BGP: %BGP-5-ADJCHANGE: VRF 0 neighbor 198.51.100.1 Down BGP Notification FSM-ERR"

In this case, we only have the message to work with, so a trigger to respond to such an event can look like this:

config system automation-trigger
edit "BGP-DOWN"
set event-type event-log
set logid 20300
config fields
edit 1
set name "msg"
set value "*neighbor 198.51.100.1 Down*"
next
end
next
end
A full debug of the event being generated and the automation stitch firing can be seen here (in my case, I am simply disabling port2):
BGP down event debug
autod(pid:1622) log packet: total sz:470 data sz:230 fld_num:8
autod(pid:1622) log datetime: 2026-05-01 11:56:27
autod(pid:1622) log header: logid:20300 vfid:0 sever:4 cat:1 subcat:3 key:0 flags:0404 reqlen:110 timestamp:1777636586845355006
fields:
id:10 name:(9)eventtime value:(19)1777636586845355006
id:2 name:(5)logid value:(10)0103020300
id:3 name:(4)type value:(5)event
id:4 name:(7)subtype value:(6)router
id:5 name:(5)level value:(7)warning
id:6 name:(2)vd value:(4)root
id:38 name:(7)logdesc value:(27)BGP neighbor status changed
id:24 name:(3)msg value:(80)BGP: %BGP-5-ADJCHANGE: VRF 0 neighbor 198.51.100.1 Down BGP Notification FSM-ERR
pid:1622-__subscr_close_cur_pkg()-141: close package size:1536 logs:2
__action_cli_script_open()-169: cli script action:PORT2-DOWN is called. svc ctx:0x559d57c000
accprof:super_admin script:
config system interface
edit port2
set status down
next
end
__read_cli_script_result()-117: cli script:
autod.10
output:
========== #1, 2026-05-01 11:56:27 ==========
70G-02 config system interface
70G-02 (interface) edit port2
70G-02 (port2) set status down
70G-02 (port2) next
70G-02 (interface) end
======= end of #1, 2026-05-01 11:56:27 ======
__action_cli_script_close()-219: cli script action is done. script:
config system interface
edit port2
set status down
next
end
output:
========== #1, 2026-05-01 11:56:27 ==========
70G-02 config system interface
70G-02 (interface) edit port2
70G-02 (port2) set status down
70G-02 (port2) next
70G-02 (interface) end
======= end of #1, 2026-05-01 11:56:27 ======
pid:1622-__handle_msg()-428: Subscriber:4 received package. pubid:0 pkgid:1206 pkg_index:0
pid:1622-__handle_pkg_logs()-370: Subscriber:4 processing package size:2253 logs:3 pickup:3
autod(pid:1622) log packet: total sz:1194 data sz:252 fld_num:12
autod(pid:1622) log datetime: 2026-05-01 11:56:28
autod(pid:1622) log header: logid:46600 vfid:0 sever:5 cat:1 subcat:0 key:0 flags:0484 reqlen:131 timestamp:1777636587533074284
fields:
id:10 name:(9)eventtime value:(19)1777636587533074284
id:2 name:(5)logid value:(10)0100046600
id:3 name:(4)type value:(5)event
id:4 name:(7)subtype value:(6)system
id:5 name:(5)level value:(6)notice
id:6 name:(2)vd value:(4)root
id:38 name:(7)logdesc value:(27)Automation stitch triggered
id:321 name:(6)stitch value:(8)BGP-DOWN
id:322 name:(7)trigger value:(8)BGP-DOWN
id:377 name:(12)stitchaction value:(10)PORT2-DOWN
id:226 name:(4)from value:(3)log
id:24 name:(3)msg value:(29)stitch:BGP-DOWN is triggered.
autod(pid:1622) log packet: total sz:626 data sz:298 fld_num:14
autod(pid:1622) log datetime: 2026-05-01 11:56:28
autod(pid:1622) log header: logid:44547 vfid:0 sever:6 cat:1 subcat:0 key:0 flags:0404 reqlen:180 timestamp:1777636587767735898
fields:
id:10 name:(9)eventtime value:(19)1777636587767735898
id:2 name:(5)logid value:(10)0100044547
id:3 name:(4)type value:(5)event
id:4 name:(7)subtype value:(6)system
id:5 name:(5)level value:(11)information
id:6 name:(2)vd value:(4)root
id:38 name:(7)logdesc value:(27)Object attribute configured
id:57 name:(2)ui value:(11)auto-script
id:12 name:(6)action value:(4)Edit
id:59 name:(6)cfgtid value:(9)600113152
id:62 name:(7)cfgpath value:(16)system.interface
id:63 name:(6)cfgobj value:(5)port2
id:64 name:(7)cfgattr value:(16)status[up->down]
id:24 name:(3)msg value:(27)Edit system.interface port2
autod(pid:1622) log packet: total sz:433 data sz:193 fld_num:8
autod(pid:1622) log datetime: 2026-05-01 11:56:28
autod(pid:1622) log header: logid:32549 vfid:0 sever:6 cat:1 subcat:0 key:0 flags:0404 reqlen:67 timestamp:1777636587778632118
fields:
id:10 name:(9)eventtime value:(19)1777636587778632118
id:2 name:(5)logid value:(10)0100032549
id:3 name:(4)type value:(5)event
id:4 name:(7)subtype value:(6)system
id:5 name:(5)level value:(11)information
id:6 name:(2)vd value:(4)root
id:38 name:(7)logdesc value:(29)Autoscript stop automatically
id:24 name:(3)msg value:(37)script autod.10 stopped automatically
You can also create a reverse trigger and use the BGP neighbor up message.
Here are all the log messages for these scenarios:
date=2026-05-02 time=16:23:47 eventtime=1777739026657108610 logid="0103020300" type="event" subtype="router" level="warning" vd="root" logdesc="BGP neighbor status changed" msg="BGP: %BGP-5-ADJCHANGE: VRF 0 neighbor 198.51.100.1 Up "
date=2026-05-02 time=16:23:47 eventtime=1777739026657075206 logid="0103020300" type="event" subtype="router" level="warning" vd="root" logdesc="BGP neighbor status changed" msg="BGP: %BGP-5-ADJCHANGE: VRF 0 neighbor 198.51.100.1 Down Peer being deleted"
date=2026-05-02 time=16:23:47 eventtime=1777739026657040239 logid="0103020304" type="event" subtype="router" level="warning" vd="root" logdesc="Routing log warning" msg="BGP: %BGP-3-NOTIFICATION: sending to 198.51.100.1 6/0 (CeaseUnspecified Error Subcode) 0 data-bytes []"
date=2026-05-02 time=16:23:18 eventtime=1777738998290854913 logid="0103020300" type="event" subtype="router" level="warning" vd="root" logdesc="BGP neighbor status changed" msg="BGP: %BGP-5-ADJCHANGE: VRF 0 neighbor 198.51.100.1 Down BGP Notification FSM-ERR"
date=2026-05-02 time=16:23:18 eventtime=1777738998290842322 logid="0103020304" type="event" subtype="router" level="warning" vd="root" logdesc="Routing log warning" msg="BGP: %BGP-3-NOTIFICATION: received from 198.51.100.1 4/0 (Hold Timer Expired/Unspecified Error Subcode) 0 data-bytes []"

config system automation-trigger
edit "BGP-UP"
set event-type event-log
set logid 20300
config fields
edit 1
set name "msg"
set value "*neighbor 198.51.100.1 Up*"
next
end
next
end
Incoming Webhook
This option is powerful, but boring. You create the trigger, and you get a URL to send HTTP POST requests to. Moving on…
An automation debugging aside
If you ever need to debug automation stitches, you do that using the autod application and a triggered stitch looks like this:
Automation stitch executed
__action_cli_script_open()-169: cli script action:PORT2-DOWN is called. svc ctx:0x55a8894cf0
accprof:super_admin script:
config system interface
edit port2
set status down
next
end
__read_cli_script_result()-117: cli script:
autod.0
output:
========== #1, 2026-05-02 17:28:27 ==========
70G-02 config system interface
70G-02 (interface) edit port2
70G-02 (port2) set status down
70G-02 (port2) next
70G-02 (interface) end
======= end of #1, 2026-05-02 17:28:27 ======
__action_cli_script_close()-219: cli script action is done. script:
config system interface
edit port2
set status down
next
end
output:
========== #1, 2026-05-02 17:28:27 ==========
70G-02 config system interface
70G-02 (interface) edit port2
70G-02 (port2) set status down
70G-02 (port2) next
70G-02 (interface) end
======= end of #1, 2026-05-02 17:28:27 ======
Some more options regarding debugging are in Yuri’s blog post (again, check it out), like testing, statistics, etc.
One thing I think is interesting is the diagnose test application autod 1 option. In Yuri’s blog, the description is “Enable automation stitches logging.” but the description of the option is actually “Enable/disable log dumping”, and this really does mean that. It dumps all logs straight to the CLI.
Turn it on, and you get a live feed of every log being created, and since not only the examples of link monitors or BGP events create log entries, but also configuration changes, we can get some nice information quickly. Here is the output of a new firewall policy being created, as well as the regular log entry, for example:
autod(pid:1622) log header: logid:44547 vfid:0 sever:6 cat:1 subcat:0 key:0 flags:0404 reqlen:359 timestamp:1777637024413047347
fields:
id:10 name:(9)eventtime value:(19)1777637024413047347
id:2 name:(5)logid value:(10)0100044547
id:3 name:(4)type value:(5)event
id:4 name:(7)subtype value:(6)system
id:5 name:(5)level value:(11)information
id:6 name:(2)vd value:(4)root
id:38 name:(7)logdesc value:(27)Object attribute configured
id:22 name:(4)user value:(5)admin
id:57 name:(2)ui value:(20)https(192.168.1.101)
id:12 name:(6)action value:(3)Add
id:59 name:(6)cfgtid value:(8)96927855
id:58 name:(4)uuid value:(36)cc0608f6-4555-51f1-0501-0fe328bf84a4
id:62 name:(7)cfgpath value:(15)firewall.policy
id:63 name:(6)cfgobj value:(1)6
id:64 name:(7)cfgattr value:(150)name[DEBUG-LOG-POLICY]srcintf[port1]dstintf[SDWAN-OUTSIDE]action[accept]srcaddr[all]dstaddr[all]schedule[always]service[ALL]logtraffic[all]nat[enable]
id:24 name:(3)msg value:(21)Add firewall.policy 6

Cool, right?
FortiAnalyzer Event Handlers
The FortiOS Event Log triggers are nice, but they aren’t flexible. If there is no log for the scenario you need, if you need something more specific, or if you need some logic, they hit their limit. FortiAnalyzer takes care of this. Now, this is not strictly “onboard” because you do need another product, but I would be remiss not to mention it.
If you integrate FortiAnalyzer as a Fabric Connector, you can leverage the Event Handlers you create on FortiAnalyzer as a trigger on a FortiGate. Let’s take a practical example.
FortiAnalyzer comes with the Default-Brute-Force-Account-Login-Attack-FGT event handler, which does exactly what it says. It recognizes brute force administrator logins on a FortiGate. It uses a correlation sequence, where failed logins are checked, and if they aren’t followed by a successful login after 5 minutes, the event triggers.
If we want to use this event handler on our FortiGate, we clone it, because event handlers of the Built-in origin cannot be used for this, enable the Automation Stitch option, change the Threshold Duration to be the same as the NOT_FOLLOWED_BY value and then we go onto the FortiGate and create our trigger.


config system automation-trigger
edit "FAZ-BRUTE-FORCE"
set event-type faz-event
set faz-event-name "Custom-Brute-Force-Account-Login-Attack-FGT"
next
end
Debugging this communication is done on FortiAnalyzer using the oftpd application, like such (192.168.1.201 is my FortiGate):
FAZVM64 # diagnose debug application oftpd 8 192.168.1.201
oftpd debug filter: filter(string)==192.168.1.201
FAZVM64 # diagnose debug enable
The full debugs for such an event handler triggering, with the regular communication removed, look like this:
FortiAnalyzer oftpd debug output
[T2313:oftp_restapi_util.c:1110] [FGT70GSERIAL] OFTP_RESTAPI_GENERIC_REQ sched success, uuid=e0a31dd8-4647-11f1-8ce6-000c29563bff, u
rl=/api/v2/monitor/log/event/generate?vdom=root, data={ "log_type": "security-event", "fields": { "ackflag": "no", "alertid": "202605021
000010016", "logcount": "5", "alerttime": "1777740995", "devid": "FGT70GSERIAL", "devname": "70G-01", "eventtype": "", "groupby1": "
FGT70GSERIAL", "groupby2": "", "groupby3": "", "readflag": "no", "severity": "medium", "subject": "Brute force account login attack
on FGT device FGT70GSERIAL detected", "tag": "Credential Access,login,attack", "triggername": "Custom-Brute-Force-Account-Login-Atta
ck-FGT", "vdom": "root", "epid": "3", "euid": "3", "epip": "192.168.1.231", "srcip": "192.168.1.231", "dstip": "192.168.1.201", "epname"
: "192.168.1.231", "euname": "N\/A", "extrainfo": "", "ephostname": "", "epmac": "", "eposname": "", "eposversion": "", "fctuid": "", "m
itre_info": "{\"enterprise\": [\"T1110.001\"]}" } }.
[T2320:oftp_restapi.c:592] [FGT70GSERIAL] req :
POST /api/v2/monitor/log/event/generate?vdom=root HTTP/1.1
Host: 127.0.0.1
Connection: close
User-Agent: FortiAnalyzer/7.6 (FortiAnalyzer-VM64; FAZ-VMTMSERIAL; v7.6.6-build3654 260127 (GA.M))
Accept: */*
Cookie: __fake_cookie_authorized_device;
X-CSRFTOKEN: __fake_cookie_authorized_device
Content-Length: 813
Content-Type: application/x-www-form-urlencoded
{ "log_type": "security-event", "fields": { "ackflag": "no", "alertid": "202605021000010016", "logcount": "5", "alerttime": "1777740995"
, "devid": "FGT70GSERIAL", "devname": "70G-01", "eventtype": "", "groupby1": "FGT70GSERIAL", "groupby2": "", "groupby3": "", "re
adflag": "no", "severity": "medium", "subject": "Brute force account login attack on FGT device FGT70GSERIAL detected", "tag": "Cred
ential Access,login,attack", "triggername": "Custom-Brute-Force-Account-Login-Attack-FGT", "vdom": "root", "epid": "3", "euid": "3", "ep
ip": "192.168.1.231", "srcip": "192.168.1.231", "dstip": "192.168.1.201", "epname": "192.168.1.231", "euname": "N\/A", "extrainfo": "",
"ephostname": "", "epmac": "", "eposname": "", "eposversion": "", "fctuid": "", "mitre_info": "{\"enterprise\": [\"T1110.001\"]}" } }
[T2301:oftp_restapi_resp.c:1946] [FGT70GSERIAL] OFTP_TASK_T_GENERIC_RESP uuid=e0a31dd8-4647-11f1-8ce6-000c29563bff, url=/api/v2/moni
tor/log/event/generate?vdom=root, req_data={ "log_type": "security-event", "fields": { "ackflag": "no", "alertid": "202605021000010016",
"logcount": "5", "alerttime": "1777740995", "devid": "FGT70GSERIAL", "devname": "70G-01", "eventtype": "", "groupby1": "FGT70GSERIAL",
"groupby2": "", "groupby3": "", "readflag": "no", "severity": "medium", "subject": "Brute force account login attack on FGT devi
ce FGT70GSERIAL detected", "tag": "Credential Access,login,attack", "triggername": "Custom-Brute-Force-Account-Login-Attack-FGT", "v
dom": "root", "epid": "3", "euid": "3", "epip": "192.168.1.231", "srcip": "192.168.1.231", "dstip": "192.168.1.201", "epname": "192.168.
1.231", "euname": "N\/A", "extrainfo": "", "ephostname": "", "epmac": "", "eposname": "", "eposversion": "", "fctuid": "", "mitre_info":
"{\"enterprise\": [\"T1110.001\"]}" } }
First, we get the requests for a REST API action, which includes all information from the log that generated the event.
Then we get the actual HTTP POST request towards the FortiGate, which includes the log information in JSON.
Lastly, we get the response to the task.
On the FortiGate, this communication can be seen in the httpsd application, because FortiAnalyzer sends a POST request after all.
Enable the debugs and check the output.
diagnose debug application httpsd -1
diagnose debug enable
[httpsd 4590 - 1777740996 info] fweb_debug_init[614] -- New POST request for "/api/v2/monitor/log/event/generate" from "/tmp/httpsd-faz.sock:0"
[httpsd 4590 - 1777740996 info] fweb_debug_init[617] -- User-Agent: "FortiAnalyzer/7.6 (FortiAnalyzer-VM64; FAZ-VMTMSERIAL; v7.6.6-build3654 260127 (GA.M))"
[httpsd 4590 - 1777740996 info] fweb_debug_init[622] -- Handler "api_monitor_v2-handler" assigned to request
[httpsd 4590 - 1777740996 info] api_access_check_for_http_authd_session[51] -- Session key request authorized for daemon_admin.
[httpsd 4590 - 1777740996 info] api_store_parameter[298] -- add API parameter 'vdom' (type=string)
[httpsd 4590 - 1777740996 info] api_store_parameter[298] -- add API parameter 'vdom' (type=string)
[httpsd 4590 - 1777740996 info] api_store_parameter[298] -- add API parameter 'log_type' (type=string)
[httpsd 4590 - 1777740996 info] api_store_parameter[298] -- add API parameter 'fields' (type=object)
[httpsd 4590 - 1777740996 info] api_endpoint_execute_handler[922] -- new API request (action='generate',path='log',name='event',vdom='root',user='daemon_admin')
[httpsd 4590 - 1777740996 info] generate_event[1026] -- Created an internal event: ackflag="no" alertid="202605021000010016" logcount="5" alerttime="1777740995" devid="FGT70GSERIAL" devname="70G-01" eventtype="" groupby1="FGT70GSERIAL" groupby2="" groupby3="" readflag="no" severity="medium" subject="Brute force account login attack on FGT device FGT70GSERIAL detected" tag="Credential Access,login,attack" triggername="Custom-Brute-Force-Account-Login-Attack-FGT" vdom="root" epid="3" euid="3" epip="192.168.1.231" srcip="192.168.1.231" dstip="192.168.1.201" epname="192.168.1.231" euname="N\/A" extrainfo="" ephostname="" epmac="" eposname="" eposversion="" fctuid="" mitre_info="{\"enterprise\": [\"T1110.001\"]}"
[httpsd 4590 - 1777740996 info] api_endpoint_execute_handler[937] -- completed API request (mem_start=55268, mem_end=55332, mem_change=64)
[httpsd 4590 - 1777740996 info] fweb_debug_final[455] -- Completed POST request for "/api/v2/monitor/log/event/generate" (HTTP 200)
There isn’t a lot going on here that we haven’t already seen, but we know it’s there.
A FortiGate dynamically pulls the available event handlers every time you try to select one in the trigger, by the way. This is done using an OFTP JSONRPC request. This also shows up on FortiAnalyzer in the oftpd debugs:
FortiGate requesting FortiAnalyzer Event Handlers
[T2321:oftps.c:2148 :192.168.1.201] SSL clienthello incoming on sockfd[26]
[T2321:oftps.c:1436 :192.168.1.201] dft-idx=0 inited=1.
[T2321:oftps.c:1927 :192.168.1.201] SSL socket[26] pid[1688] ssl[0x7f3e50041820] SSL_new() success.
[T2321:oftps.c:1804 :192.168.1.201] ssl verify peer cert
[T2321:oftps.c:1826 :192.168.1.201] Peer is using a fortinet certificate. ON=Fortinet
[T2321:oftps.c:1839 :192.168.1.201] Peer cert info, CommonName(CN=FGT70GSERIAL).
[T2321:oftps.c:2160 :192.168.1.201] SSL_accept one client SUCCESS [ protocol : (772) TLS 1.3 ]
[T2321:oftps.c:2199 :192.168.1.201] SSL socket[26] pid[1688] ssl[0x7f3e50041820] SSL_accepted
[T2322:oftps.c:2257 :192.168.1.201] SSL socket[26] pid[1688] ssl[0x7f3e50041820] received [512] bytes:
[T2322:oftps.c:2257 :192.168.1.201] SSL socket[26] pid[1688] ssl[0x7f3e50041820] received [83] bytes:
[T2322:main.c:5147 :192.168.1.201] handle LOGIN_REQUEST_LEGACY
[T2323:login.c:3539 :192.168.1.201] login-ver: 0.0
[T2323:login.c:3443 :192.168.1.201] host = 'FGT-70G01'
[T2323:login.c:3489 :192.168.1.201] Version: FortiGate-70G v8.0.0,build0167,260420 (GA.F)
Virus-DB: 1.00001(2026-04-02 13:48)
IPS-DB: 36.00208(2026-04-23 01:22)
APP-DB: 36.00206(2026-04-21 03:52)
FMWP-DB: 26.00040(2026-04-10 09:35)
Industrial-DB: 6.00741(2015-12-01 02:30)
Serial-Number: FGT70GSERIAL
Virtual domain configuration: disable
Current HA mode: a-p
Current HA group: labcluster
[T2323:login.c:348 :192.168.1.201] os_type(0) os_ver(8) mr(0) patch(0) build(167) beta(-1)
[T2323:login.c:398 :192.168.1.201] ha_group_name:labcluster, ha_mode:1.
[T2323:login.c:3448 :192.168.1.201] vdom = 1
[T2323:login.c:3468 :192.168.1.201] tz-offset = 7200, tz-name = Europe/Vienna
[T2323:oftps.c:2323 FGT70GSERIAL:192.168.1.201] SSL socket[26] pid[1688] ssl[0x7f3e50041820] sent [103] bytes:
[T2323:login.c:3943 FGT70GSERIAL:192.168.1.201] login succeed
[T2322:oftps.c:2257 FGT70GSERIAL:192.168.1.201] SSL socket[26] pid[1688] ssl[0x7f3e50041820] received [236] bytes:
[T2322:main.c:5147 FGT70GSERIAL:192.168.1.201] handle RESTAPI REQUEST
[T2329:oftp_svc_fwd.c:812 FGT70GSERIAL:192.168.1.201] handle_oftp_jsonrpc_request entry
[T2329:oftp_svc_fwd.c:726 FGT70GSERIAL:192.168.1.201] cannot find display-timezone for FGT70GSERIAL
[T2329:oftp_svc_fwd.c:898 FGT70GSERIAL:192.168.1.201] sent req={ "id": 1777742118, "params": [ { "url": "eventmgmt\/dev\/FGT70GSERIAL
\/vdom\/root\/config\/trigger", "apiver": 3, "filter": [ "handlertype", "==", "handler-type-remote" ] } ], "jsonrpc": "2.0", "metho
d": "get" }
response '{ "jsonrpc": "2.0", "id": 1777742118, "result": { "status": { "code": 0, "message": "OK" }, "data": [ { "auto-raise-incident"
: 0, "automation-stitch": 1, "creation-time": 0, "description": "Default event handler to detect botnet communication and report to Fort
iGate", "enable": 1, "enable-time": 0, "handler-id": "30000", "mitre-domain": "enterprise", "mitre-info": "{\"enterprise\": [\"T1584.005
\", \"T1071\"]}", "mitre-tech-id": "T1584.005,T1071", "name": "Default-Botnet-Communication-Detection", "protected": 1, "rule": [ { "agg
regate-expr": "COUNT(*) >= 1", "devtype": 0, "enable": 1, "eventstatus": "open", "eventtype": null, "extrainfo": "C&C: ${dstip}:${dstpor
t}, Reference: ${ref}", "extrainfo-type": 1, "filter": null, "filter-expr": "logid==0202009248", "filter-relation": 0, "groupby1": "endp
oint", "groupby2": "virus", "groupby3": null, "indicator": null, "logtype": "virus", "name": "Traffic to Botnet CnC blocked in virus log
", "risk-severity": 4, "rule-id": "1", "severity": 1, "subject": "Traffic to Botnet C&C $groupby2 blocked", "tags": "Default,Botnet,IP,C
&C", "thres-duration": 1440, "utmevent": null }, { "aggregate-expr": "COUNT(*) >= 1", "devtype": 0, "enable": 1, "eventstatus": "open",
"eventtype": null, "extrainfo": "C&C: ${dstip}:${dstport}, Reference: ${ref}", "extrainfo-type": 1, "filter": null, "filter-expr": "logi
d==0202009249", "filter-relation": 0, "groupby1": "endpoint", "groupby2": "virus", "groupby3": null, "indicator": null, "logtype": "viru
s", "name": "Traffic to Botnet CnC detected in virus log", "risk-severity": 4, "rule-id": "2", "severity": 0, "subject": "Traffic to Bot
net C&C $groupby2 detected", "tags": "Default,Botnet,IP,C&C", "thres-duration": 1440, "utmevent": null }, { "aggregate-expr": "COUNT(*)
>= 1", "devtype": 0, "enable": 1, "eventstatus": "open", "eventtype": null, "extrainfo": "Traffic path: ${devname} (Policy ID:${policyid
})\\${dstintf}\\${dstip}", "extrainfo-type": 1, "filter": null, "filter-expr": "logid==1501054601 OR logid==1501054600", "filter-relatio
n": 0, "groupby1": "endpoint", "groupby2": "qname", "groupby3": null, "indicator": null, "logtype": "dns", "name": "DNS traffic to Botne
t CnC blocked", "risk-severity": 4, "rule-id": "3", "severity": 1, "subject": "DNS traffic to Botnet C&C $groupby2 blocked", "tags": "De
fault,Botnet,Domain,C&C", "thres-duration": 1440, "utmevent": null }, { "aggregate-expr": "COUNT(*) >= 1", "devtype": 0, "enable": 1, "e
ventstatus": "open", "eventtype": null, "extrainfo": "C&C: ${dstip}:${dstport}, Traffic path: ${devname} (Policy ID:${policyid})\\${dsti
ntf}, Reference: ${ref}", "extrainfo-type": 1, "filter": null, "filter-expr": "attack ~ Botnet and direction=incoming and (action=='dete
cted' or action=='pass session')", "filter-relation": 0, "groupby1": "endpoint", "groupby2": "attack", "groupby3": null, "indicator": nu
ll, "logtype": "ips", "name": "Traffic to Botnet CnC detected in ips log 1", "risk-severity": 4, "rule-id": "4", "severity": 0, "subject
": "Traffic to Botnet C&C $groupby2 detected", "tags": "Default,Botnet,Signature,C&C,Incoming", "thres-duration": 1440, "utmevent": null
}, { "aggregate-expr": "COUNT(*) >= 1", "devtype": 0, "enable": 1, "eventstatus": "open", "eventtype": null, "extrainfo": "C&C: ${dstip
}:${dstport}, Traffic path: ${devname} (Policy ID:${policyid})\\${dstintf}, Reference: ${ref}", "extrainfo-type": 1, "filter": null, "fi
lter-expr": "attack ~ Botnet and direction=incoming and action!='detected' and action!='pass session'", "filter-relation": 0, "groupby1"
: "endpoint", "groupby2": "attack", "groupby3": null, "indicator": null, "logtype": "ips", "name": "Traffic to Botnet CnC blocked in ips
log 1", "risk-severity": 4, "rule-id": "5", "severity": 1, "subject": "Traffic to Botnet C&C $groupby2 blocked", "tags": "Default,Botne
t,Signature,C&C,Incoming", "thres-duration": 1440, "utmevent": null }, { "aggregate-expr": "COUNT(*) >= 1", "devtype": 0, "enable": 1, "
eventstatus": "open", "eventtype": null, "extrainfo": "C&C: ${srcip}:${srcport}, Traffic path: ${devname} (Policy ID:${policyid})\\${src
intf}, Reference: ${ref}", "extrainfo-type": 1, "filter": null, "filter-expr": "attack ~ Botnet and direction=outgoing and (action=='det
ected' or action=='pass session')", "filter-relation": 0, "groupby1": "dstendpoint", "groupby2": "attack", "groupby3": null, "indicator"
: null, "logtype": "ips", "name": "Traffic from Botnet CnC detected in ips log", "risk-severity": 4, "rule-id": "6", "severity": 0, "sub
ject": "Traffic from Botnet C&C $groupby2 detected", "tags": "Default,Botnet,Signature,C&C,Outgoing", "thres-duration": 1440, "utmevent"
: null }, { "aggregate-expr": "COUNT(*) >= 1", "devtype": 0, "enable": 1, "eventstatus": "open", "eventtype": null, "extrainfo": "C&C: $
{srcip}:${srcport}, Traffic path: ${devname} (Policy ID:${policyid})\\${srcintf}, Reference: ${ref}", "extrainfo-type": 1, "filter": nul
l, "filter-expr": "attack ~ Botnet and direction=outgoing and action!='detected' and action!='pass session'", "filter-relation": 0, "gro
upby1": "dstendpoint", "groupby2": "attack", "groupby3": null, "indicator": null, "logtype": "ips", "name": "Traffic from Botnet CnC blo
cked in ips log", "risk-severity": 4, "rule-id": "7", "severity": 1, "subject": "Traffic from Botnet C&C $groupby2 blocked", "tags": "De
fault,Botnet,Signature,C&C,Outgoing", "thres-duration": 1440, "utmevent": null }, { "aggregate-expr": "COUNT(*) >= 1", "devtype": 0, "en
able": 1, "eventstatus": "open", "eventtype": null, "extrainfo": "C&C: ${dstip}:${dstport}, Reference: ${ref}", "extrainfo-type": 1, "fi
lter": null, "filter-expr": "logid==0422016400", "filter-relation": 0, "groupby1": "endpoint", "groupby2": "attack", "groupby3": null, "
indicator": null, "logtype": "ips", "name": "Traffic to Botnet CnC blocked in ips log 2", "risk-severity": 4, "rule-id": "8", "severity"
: 1, "subject": "Traffic to Botnet C&C $groupby2 blocked", "tags": "Default,Botnet,IP,C&C", "thres-duration": 1440, "utmevent": null },
{ "aggregate-expr": "COUNT(*) >= 1", "devtype": 0, "enable": 1, "eventstatus": "open", "eventtype": null, "extrainfo": "C&C: ${dstip}:${
dstport}, Reference: ${ref}", "extrainfo-type": 1, "filter": null, "filter-expr": "logid==0422016401", "filter-relation": 0, "groupby1":
"endpoint", "groupby2": "attack", "groupby3": null, "indicator": null, "logtype": "ips", "name": "Traffic to Botnet CnC detected in ips
log 2", "risk-severity": 4, "rule-id": "9", "severity": 0, "subject": "Traffic to Botnet C&C $groupby2 detected", "tags": "Default,Botn
et,IP,C&C", "thres-duration": 30, "utmevent": null } ], "template-url": "\/fazcfg-template\/basic-handler\/fgt", "update-time": 0, "vers
ion": 2, "handlertype": 1, "correlationtype": "basic-handler" }, { "auto-raise-incident": 0, "automation-stitch": 1, "content-pack-id":
"", "content-pack-uuid": "", "creation-time": 1777736984, "data-selector": "", "description": "", "enable": 1, "enable-time": 0, "handle
r-id": "679_809_2dd_c09", "mitre-info": "", "name": "SINGLE-LOGIN-FAILED", "notification": "", "protected": 0, "rule": [ { "aggregate-ex
pr": "COUNT(*)>=1", "devtype": 0, "enable": 1, "eventstatus": "auto", "eventtype": null, "extrainfo": null, "extrainfo-type": 0, "filter
": [ { "id": 1, "key": "logid", "oper": 0, "value": "0100032002" } ], "filter-expr": "", "filter-relation": 1, "groupby1": "srcip", "gro
upby2": "", "groupby3": "", "indicator": null, "logtype": "event", "name": "SINGLE-LOGIN-FAILED", "risk-severity": 4, "rule-id": "401_4b
9_c06_724", "severity": 2, "tags": "", "thres-duration": 1, "utmevent": "system" } ], "template-url": "", "update-time": 1777742099, "uu
id": "", "version": 2, "handlertype": 1, "correlationtype": "basic-handler" }, { "auto-raise-incident": 0, "automation-stitch": 1, "cont
ent-pack-id": "", "content-pack-uuid": "", "creation-time": 1777735167, "data-selector": "", "description": "This handler is to detect i
f an account login failed many times not followed by a login success for FortiGate.", "enable": 1, "enable-time": 0, "eventstatus": "aut
o", "extrainfo-type": 0, "handler-id": "360_fba_9a7_67b", "indicator": null, "mitre-domain": "enterprise", "mitre-info": "{\"enterprise\
": [\"T1110.001\"]}", "mitre-tech-id": "T1110.001", "name": "Custom-Brute-Force-Account-Login-Attack-FGT", "notification": "", "protecte
d": 0, "risk-severity": 4, "rule": [ { "aggregate-expr": "COUNT(*) >= 5", "devtype": 0, "filter": [ { "id": 1, "key": "logid", "oper": 0
, "value": "0100032002" } ], "filter-expr": "", "filter-relation": 1, "groupby1": "devid", "groupby2": "", "groupby3": "", "logtype": "e
vent", "name": "Login Failed 5 times", "rule-id": "rule_1", "utmevent": "system" }, { "aggregate-expr": "COUNT(*)>=1", "devtype": 0, "fi
lter": [ { "id": 1, "key": "logid", "oper": 0, "value": "0100032001" } ], "filter-expr": "", "filter-relation": 1, "groupby1": "devid",
"groupby2": "", "groupby3": "", "logtype": "event", "name": "Login-Success", "rule-id": "rule_2", "utmevent": "system" } ], "rule-relati
on": "rule_1 NOT_FOLLOWED_BY[1m] rule_2", "rule-relation-constraint": "rule_1.devid=rule_2.devid", "severity": 2, "subject": "Brute forc
e account login attack on FGT device ${Login Failed 5 times.groupby1} detected", "tags": "Credential Access,login,attack", "template-url
": "\/fazcfg-template\/correlation-handler\/fgt", "thres-duration": 1, "update-time": 1777736344, "uuid": "", "version": 2, "handlertype
": 1, "correlationtype": "correlation-handler" } ] } }', len=9427.
[T2322:oftps.c:2235 FGT70GSERIAL:192.168.1.201] The SSL/TLS connection has been closed
[T2322:main.c:937 FGT70GSERIAL:192.168.1.201] Client connection closed. Reason 0(OK)
[T2322:oftps.c:2342 FGT70GSERIAL:192.168.1.201] SSL pid[1688] ssl[0x7f3e50041820] shuting down sockfd[26] ip[192.168.1.201] connecte
d[1]
[T2322:oftps.c:2361 FGT70GSERIAL:192.168.1.201] SSL_shutdown SUCCESS
[T2322:oftps.c:2369 FGT70GSERIAL:192.168.1.201] SSL socket[26] pid[1688] ssl[0x7f3e50041820] destroy_SSL_context
With FortiAnalyzer Event Handlers, the sky really is the limit. You can do practically everything your heart desires if you speak FortiAnalyzer.
Auto-scripts
The next automation option is auto-scripts, which are CLI-only in config system auto-script, and not that well-known, but they are powerful. Just look at the options:
70G-02 (LAB-SCRIPT) # set
interval Repeat interval in seconds.
repeat Number of times to repeat this script (0 = infinite).
start Script starting mode.
script List of FortiOS CLI commands to repeat.
output-size Number of megabytes to limit script output to (10 - 1024, default = 10).
timeout Maximum running time for this script in seconds (0 = no timeout).
The script option takes whatever FortiOS CLI commands you want, but there is a limit of 1023 characters, so you won’t be able to import certificates.
You can set the interval, how often the script runs and if it should start automatically or be executed manually.
If you ever need to escape some characters in the script, like double quotes (”), you can do that using backslashes \ as can be seen here:
config system auto-script
edit "LAB-SCRIPT"
set start auto
set script "config firewall address
edit \"TEST\"
set subnet 192.0.2.0/24
set comment \"Escape double quotes\"
next
end
"
next
end
Auto-scripts have the nice advantage of being able to be created by outside systems but run with local privileges on a FortiGate, which becomes important if you try to automate things that outside systems aren’t allowed to do. A good example is FortiManager. FortiManager cannot change some options on a FortiGate, like certain options related to HA or the central management options itself, meaning FortiManager cannot change the FortiManager options on a FortiGate.
To get around these limitations, you can create an auto-script on FortiManager and change whatever you need in that.
For debugging around auto-scripts, you use the execute auto-script command, and lots of information is available in this technical tip: Technical Tip: Automated scripts (auto-script). Execution, testing and verification explained with examples
Importantly, auto-scripts can be created and controlled in automation actions, which opens up a lot of possibilities.
One use case for auto-scripts I want to highlight is regarding the execute auto-script result option. Since this command shows the result of an auto-script and auto-scripts can run diagnose and get commands, you can use this as a crude information storage.
Imagine that you run into the dreaded conserve mode, and you want to gather some information during that time, like which process took the memory. FortiGates come with a Conserve Mode automation trigger, and if you create the following automation action, you can later check what the status of your processes was using diagnose sys top 1 99 1 when conserve mode was triggered.
config system auto-script
edit "LAB-SCRIPT-TOP"
set script "diagnose sys top 1 99 1"
next
end
The reason I am performing an execute auto-script start LAB-SCRIPT-TOP after creating the auto-script is because setting set start auto is, for some reason, not possible with an automation action.
========== #1, 2026-05-01 12:39:06 ==========
70G-02 config system auto-script
70G-02 (auto-script) edit "LAB-SCRIPT-TOP"
70G-02 (LAB-SCRIPT-TOP) set start auto
Script start-mode could not be changed from auto-script.
node_check_object fail! for start auto
value parse error before 'auto'
Command fail. Return code -37
70G-02 (LAB-SCRIPT-TOP) set script "diagnose sys top 1 99 1"
70G-02 (LAB-SCRIPT-TOP) next
70G-02 (auto-script) end
70G-02 execute auto-script start LAB-SCRIPT-TOP
======= end of #1, 2026-05-01 12:39:06 ======
Once your conserve mode problem has been solved, or it has solved itself, run an execute auto-script result LAB-SCRIPT-TOP and check your output.
Auto-script output
70G-02 # execute auto-script result LAB-SCRIPT-TOP
Script LAB-SCRIPT-TOP output:
========== #1, 2026-05-02 18:13:44 ==========
70G-02 diagnose sys top 1 99 1
06:13:46 PM up 0 days, 2 hours and 21 minutes
0U, 0N, 0S, 100I, 0WA, 0HI, 0SI, 0ST; 3708T, 2315F
miglogd 1714 S 0.5 1.3 2
node 1572 S 0.0 2.6 3
ipsengine 1893 S 0.0 2.1 3
ipshelper 1604 S 0.0 2.1 2
ipsengine 1895 S 0.0 2.1 2
ipsengine 1894 S 0.0 2.1 0
wad 1665 S 0.0 1.6 3
wad 1663 S 0.0 1.5 1
cmdbsvr 1484 S 0.0 1.5 0
cw_acd 1614 S 0.0 1.3 2
forticron 1558 S 0.0 1.3 0
extenderd 1631 S 0.0 1.2 1
miglogd 1570 S 0.0 1.2 0
csfd 1634 S 0.0 1.2 3
wad 1664 S 0.0 1.2 0
newcli 5113 S 0.0 1.2 3
newcli 2866 S 0.0 1.2 2
httpsd 5707 S 0.0 1.1 1
wad 1656 S 0.0 1.1 0
fgfmd 1613 S 0.0 1.1 2
initXXXXXXXXXXX 1 S 0.0 1.1 3
wad 1580 S 0.0 1.0 1
http_authd 1551 S 0.0 1.0 2
wad 1659 S 0.0 1.0 0
wad 1657 S 0.0 0.9 2
iked 1718 S 0.0 0.9 3
wad 1661 S 0.0 0.9 1
wad 1662 S 0.0 0.8 0
fgtlogd 1593 S 0.0 0.8 3
wad 1655 S 0.0 0.8 0
wad 1660 S 0.0 0.8 1
wad 1658 S 0.0 0.8 2
authd3 1726 S 0.0 0.8 1
csfd 0 1964 S 0.0 0.8 3
authd2 1725 S 0.0 0.8 3
eap_proxy_worke 1728 S 0.0 0.8 3
iked 1716 S 0.0 0.8 3
telemetryd 1633 S 0.0 0.8 1
iked 1717 S 0.0 0.7 0
fcnacd 1567 S 0.0 0.7 0
dnsproxy 1617 S 0.0 0.7 3
forticldd 1559 S 0.0 0.7 1
cu_acd 1623 S 0.0 0.7 0
httpsd 1550 S 0.0 0.7 2
imi 1546 S 0.0 0.7 3
bgpd 1543 S < 0.0 0.7 2
nsm 1542 S 0.0 0.7 3
updated 1589 S 0.0 0.7 0
authd 1560 S 0.0 0.7 2
fltund 1626 S 0.0 0.6 0
snmpd 1596 S 0.0 0.6 3
cid-config 1610 S N 0.0 0.6 2
fnbamd 1557 S 0.0 0.6 3
fortimq 1637 S 0.0 0.6 3
iotd 1598 S 0.0 0.6 1
voipd 1579 S 0.0 0.6 0
foauthd 1561 S 0.0 0.6 1
sshd 1600 S 0.0 0.6 3
wpad_ac 1618 S 0.0 0.6 2
ntpd 1599 S < 0.0 0.6 2
flcfgd 1624 S 0.0 0.6 2
http_authd 1554 S 0.0 0.5 2
radvd 1603 S 0.0 0.5 2
iked 1715 S 0.0 0.5 0
forticron 5778 S 0.0 0.5 2
lnkmtd 1601 S 0.0 0.5 3
autod 1635 S 0.0 0.5 2
zebos_launcher 1527 S 0.0 0.5 3
cloudapid 1636 S 0.0 0.5 3
sshd 2865 S 0.0 0.4 3
syslogd 1591 S 0.0 0.4 3
locallogd 1595 S 0.0 0.4 2
forticron 5779 R 0.0 0.4 3
forticron 5777 S 0.0 0.4 1
cid-scan 1620 S 0.0 0.4 0
fortilinkd 1622 S 0.0 0.4 2
vwl 1602 S 0.0 0.4 2
ikecryptd_dhw0 1583 S 0.0 0.4 3
ikecryptd_dhw2 1585 S 0.0 0.4 3
ikecryptd_dhw1 1584 S 0.0 0.4 3
ipsmonitor 1555 S 0.0 0.3 1
flpold 1625 S 0.0 0.3 2
cw_acd_helper 1733 S 0.0 0.3 2
dnsproxy 1606 S 0.0 0.3 1
ipmc_sensord 1548 S 0.0 0.3 1
dhcp6c 1630 S 0.0 0.3 1
ikecryptd 1581 S 0.0 0.3 1
getty 1552 S < 0.0 0.3 0
alertmail 1605 S 0.0 0.3 3
eap_proxy 1611 S 0.0 0.3 3
merged_daemons 1556 S 0.0 0.3 2
clearpass 1563 S 0.0 0.3 3
uploadd 1547 S 0.0 0.3 2
fsso_ldap 1569 S 0.0 0.3 3
fas 1566 S 0.0 0.3 1
httpclid 1565 S 0.0 0.3 2
fsd 1629 S 0.0 0.3 1
kmiglogd 1549 S 0.0 0.3 3
getty 1553 S 0.0 0.2 3
======= end of #1, 2026-05-02 18:13:47 ======
You can of course also send a mail with all this information, as is detailed in this technical tip: Technical Tip: Automation stitch for conserve mode
Batch mode
The last topic for automation I want to write about is batch mode. This isn’t a way to automate something, but you can use it in automation, so it gets a spot.
Batch mode is something you start, write down your CLI commands, and once you end the mode, the commands get executed. This allows you to make lots of changes in a single “transaction”, not to be confused with the actual config transactions, where the individual configurations might cut off your access at some point.
Imagine you are managing a FortiGate via a VPN, and you need to drastically change the VPN configuration, which would normally disconnect you somewhere along the configuration. With batch mode, you can do this all without disruption.
One very nice thing about batch mode is that you don’t have to worry about formatting, character limits or anything of the sort. Since you’re working like normal on the CLI, you can do just about every configuration. In the auto-script section, I mentioned how the character limit is 1023, so importing certificates won’t work. This is no problem with batch mode.
Batch mode certificate import
70G-02 # execute batch start
Enter batch mode...
70G-02 # config vpn certificate remote
70G-02 # edit "TEST-CERT"
70G-02 # set remote "-----BEGIN CERTIFICATE-----
> MIIHSzCCBTOgAwIBAgITMgAAADd86bYatj17eQACAAAANzANBgkqhkiG9w0BAQ0F
> ADBVMRMwEQYKCZImiZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbGFiZG9t
> YWluMRIwEAYKCZImiZPyLGQBGRYCYWQxDzANBgNVBAMTBldJTi1DQTAeFw0yNTA4
> MDkxOTU0MDdaFw0yNzA4MDkxOTU0MDdaMCMxITAfBgNVBAMMGGxhYnVzZXJAYWQu
> bGFiZG9tYWluLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALrJ
> EQqx06rKJ9OULN2155KGM31WjJvUDM+aPEiHgmAUE9pPmob/kLzQ97ylnj3AzY9e
> FF+coqgphfpxdLhlYEOUSAzQsK2B/Jn7MS8dhwfaYGtVvtfEjOZa833pXWmz1bQE
> Onfrnp1n3QZL6WYkSLHUbBESQY6tmfzOohcu9rfNfMVFEsPz/MPTHOFkqn5CfPMT
> p9gV3X3/UhoWrXBphUfKjHf5+NY2kBVySRIsaFxv/yTI/ypbc0Zk3a7Ux4dJRRcd
> jIGfHxoN6d9EphlMd/fEY27yJ0Ul1HZJh7skRsP9y0egMQtbEbsOmCxeyXysNuLe
> bTMYVxJ5w9DFB+bbmYUCAwEAAaOCA0QwggNAMCcGA1UdJQQgMB4GCCsGAQUFBwMC
> BggrBgEFBQcDBAYIKwYBBQUHAwEwMwYJKwYBBAGCNxUKBCYwJDAKBggrBgEFBQcD
> AjAKBggrBgEFBQcDBDAKBggrBgEFBQcDATAdBgNVHQ4EFgQUTQ/QuOQjE0fjDKCy
> ajTzs7KtenYwDgYDVR0PAQH/BAQDAgWgMCMGA1UdEQQcMBqCGGxhYnVzZXJAYWQu
> bGFiZG9tYWluLmNvbTAfBgNVHSMEGDAWgBTzzdZs1ME1aNfuqgd6qFpzuEhs1TCC
> AQcGA1UdHwSB/zCB/DCB+aCB9qCB84aBt2xkYXA6Ly8vQ049V0lOLUNBKDIpLENO
> PVdJTi1BRCxDTj1DRFAsQ049UHVibGljJTIwS2V5JTIwU2VydmljZXMsQ049U2Vy
> dmljZXMsQ049Q29uZmlndXJhdGlvbixEQz1hZCxEQz1sYWJkb21haW4sREM9Y29t
> P2NlcnRpZmljYXRlUmV2b2NhdGlvbkxpc3Q/YmFzZT9vYmplY3RDbGFzcz1jUkxE
> aXN0cmlidXRpb25Qb2ludIY3aHR0cDovL1dJTi1BRC5hZC5sYWJkb21haW4uY29t
> L0NlcnRFbnJvbGwvV0lOLUNBKDIpLmNybDCCAR8GCCsGAQUFBwEBBIIBETCCAQ0w
> ga0GCCsGAQUFBzAChoGgbGRhcDovLy9DTj1XSU4tQ0EsQ049QUlBLENOPVB1Ymxp
> YyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZpY2VzLENOPUNvbmZpZ3VyYXRpb24s
> REM9YWQsREM9bGFiZG9tYWluLERDPWNvbT9jQUNlcnRpZmljYXRlP2Jhc2U/b2Jq
> ZWN0Q2xhc3M9Y2VydGlmaWNhdGlvbkF1dGhvcml0eTBbBggrBgEFBQcwAoZPaHR0
> cDovL1dJTi1BRC5hZC5sYWJkb21haW4uY29tL0NlcnRFbnJvbGwvV0lOLUFELmFk
> LmxhYmRvbWFpbi5jb21fV0lOLUNBKDIpLmNydDA9BgkrBgEEAYI3FQcEMDAuBiYr
> BgEEAYI3FQiFrZZuhK/KeoeNhyWH9NB3gfixAQWDyMVZhdXVWQIBZAIBCTANBgkq
> hkiG9w0BAQ0FAAOCAgEAvQt0Mm/hcFZ4GX5KZj7mlj3Aqm4HyapaRSAHVp7eN+I2
> kZiWtzn8WP6fEcniHNXn4hs/5c/MEBzpIbQ4z0UvymH2AHijiZAEq/zOqOlGCOjy
> 8/lhRFY0cjGxg6GfpN7Pm9p0PEEOGKvapn92hxE/rAL4S5m4jxu9Nukfcw8mcb7g
> kaGr/3HAlRcsttU0VyJichmfBOYutpj3mhJ38Sc7eCxbTqj+U71NSjdrNx2WN2jh
> PAhS31tP1YTyNjW7Tgrn9unplxi2BqjDc0j72/247/E6DBdroV5HiQ2F3e2u1eXk
> Ss3IKYKtU1UixDAYsN28Nir4u87P4yo1xcpGTHr5Qne1E2tHNH1jb951MZPMuhh6
> bSIm+U0b0EzNgfQp6wI+a/9ZtliZfpxrvzyp7mPwhri9ZbNtDkWPTOjjWrw4yEVZ
> /e+UFt9ghYB0xSvllvd8fquzG5bgOv46EfwSw0J8a4PGwy4Wy7kiyjrsphimNY6/
> ql8WEeg48QTukluhULsKosO1JNx6RLe85gCcQ7Jb9Q77q2DJiTHjqu7OACns4DkV
> BKPtYE0+40/TOCc4GgsRHk/iLy0/vnh7KH5ITblokygKMFAjhBlmSmdhajK/7Vyl
> cWMKYzZMGk6FvNU8XXx/fjVc8dL2lj+P3E6UOvtYtX4Yb3TuVUp+NtJSJZ6eLiA=
> -----END CERTIFICATE-----"
70G-02 # next
70G-02 # end
70G-02 # execute batch end
Exit and run batch commands...
70G-02 # execute batch lastlog
0: config vpn certificate remote
0: edit "TEST-CERT"
0: set remote "-----BEGIN CERTIFICATE-----
0: MIIHSzCCBTOgAwIBAgITMgAAADd86bYatj17eQACAAAANzANBgkqhkiG9w0BAQ0F
0: ADBVMRMwEQYKCZImiZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbGFiZG9t
0: YWluMRIwEAYKCZImiZPyLGQBGRYCYWQxDzANBgNVBAMTBldJTi1DQTAeFw0yNTA4
0: MDkxOTU0MDdaFw0yNzA4MDkxOTU0MDdaMCMxITAfBgNVBAMMGGxhYnVzZXJAYWQu
0: bGFiZG9tYWluLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALrJ
0: EQqx06rKJ9OULN2155KGM31WjJvUDM+aPEiHgmAUE9pPmob/kLzQ97ylnj3AzY9e
0: FF+coqgphfpxdLhlYEOUSAzQsK2B/Jn7MS8dhwfaYGtVvtfEjOZa833pXWmz1bQE
0: Onfrnp1n3QZL6WYkSLHUbBESQY6tmfzOohcu9rfNfMVFEsPz/MPTHOFkqn5CfPMT
0: p9gV3X3/UhoWrXBphUfKjHf5+NY2kBVySRIsaFxv/yTI/ypbc0Zk3a7Ux4dJRRcd
0: jIGfHxoN6d9EphlMd/fEY27yJ0Ul1HZJh7skRsP9y0egMQtbEbsOmCxeyXysNuLe
0: bTMYVxJ5w9DFB+bbmYUCAwEAAaOCA0QwggNAMCcGA1UdJQQgMB4GCCsGAQUFBwMC
0: BggrBgEFBQcDBAYIKwYBBQUHAwEwMwYJKwYBBAGCNxUKBCYwJDAKBggrBgEFBQcD
0: AjAKBggrBgEFBQcDBDAKBggrBgEFBQcDATAdBgNVHQ4EFgQUTQ/QuOQjE0fjDKCy
0: ajTzs7KtenYwDgYDVR0PAQH/BAQDAgWgMCMGA1UdEQQcMBqCGGxhYnVzZXJAYWQu
0: bGFiZG9tYWluLmNvbTAfBgNVHSMEGDAWgBTzzdZs1ME1aNfuqgd6qFpzuEhs1TCC
0: AQcGA1UdHwSB/zCB/DCB+aCB9qCB84aBt2xkYXA6Ly8vQ049V0lOLUNBKDIpLENO
0: PVdJTi1BRCxDTj1DRFAsQ049UHVibGljJTIwS2V5JTIwU2VydmljZXMsQ049U2Vy
0: dmljZXMsQ049Q29uZmlndXJhdGlvbixEQz1hZCxEQz1sYWJkb21haW4sREM9Y29t
0: P2NlcnRpZmljYXRlUmV2b2NhdGlvbkxpc3Q/YmFzZT9vYmplY3RDbGFzcz1jUkxE
0: aXN0cmlidXRpb25Qb2ludIY3aHR0cDovL1dJTi1BRC5hZC5sYWJkb21haW4uY29t
0: L0NlcnRFbnJvbGwvV0lOLUNBKDIpLmNybDCCAR8GCCsGAQUFBwEBBIIBETCCAQ0w
0: ga0GCCsGAQUFBzAChoGgbGRhcDovLy9DTj1XSU4tQ0EsQ049QUlBLENOPVB1Ymxp
0: YyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZpY2VzLENOPUNvbmZpZ3VyYXRpb24s
0: REM9YWQsREM9bGFiZG9tYWluLERDPWNvbT9jQUNlcnRpZmljYXRlP2Jhc2U/b2Jq
0: ZWN0Q2xhc3M9Y2VydGlmaWNhdGlvbkF1dGhvcml0eTBbBggrBgEFBQcwAoZPaHR0
0: cDovL1dJTi1BRC5hZC5sYWJkb21haW4uY29tL0NlcnRFbnJvbGwvV0lOLUFELmFk
0: LmxhYmRvbWFpbi5jb21fV0lOLUNBKDIpLmNydDA9BgkrBgEEAYI3FQcEMDAuBiYr
0: BgEEAYI3FQiFrZZuhK/KeoeNhyWH9NB3gfixAQWDyMVZhdXVWQIBZAIBCTANBgkq
0: hkiG9w0BAQ0FAAOCAgEAvQt0Mm/hcFZ4GX5KZj7mlj3Aqm4HyapaRSAHVp7eN+I2
0: kZiWtzn8WP6fEcniHNXn4hs/5c/MEBzpIbQ4z0UvymH2AHijiZAEq/zOqOlGCOjy
0: 8/lhRFY0cjGxg6GfpN7Pm9p0PEEOGKvapn92hxE/rAL4S5m4jxu9Nukfcw8mcb7g
0: kaGr/3HAlRcsttU0VyJichmfBOYutpj3mhJ38Sc7eCxbTqj+U71NSjdrNx2WN2jh
0: PAhS31tP1YTyNjW7Tgrn9unplxi2BqjDc0j72/247/E6DBdroV5HiQ2F3e2u1eXk
0: Ss3IKYKtU1UixDAYsN28Nir4u87P4yo1xcpGTHr5Qne1E2tHNH1jb951MZPMuhh6
0: bSIm+U0b0EzNgfQp6wI+a/9ZtliZfpxrvzyp7mPwhri9ZbNtDkWPTOjjWrw4yEVZ
0: /e+UFt9ghYB0xSvllvd8fquzG5bgOv46EfwSw0J8a4PGwy4Wy7kiyjrsphimNY6/
0: ql8WEeg48QTukluhULsKosO1JNx6RLe85gCcQ7Jb9Q77q2DJiTHjqu7OACns4DkV
0: BKPtYE0+40/TOCc4GgsRHk/iLy0/vnh7KH5ITblokygKMFAjhBlmSmdhajK/7Vyl
0: cWMKYzZMGk6FvNU8XXx/fjVc8dL2lj+P3E6UOvtYtX4Yb3TuVUp+NtJSJZ6eLiA=
0: -----END CERTIFICATE-----"
0: next
0: end
You can, of course, use batch mode in combination with other automation options, like automation actions in stitches, but keep the 1023-character limit in mind.
In combination with automation actions, I want to note that batch mode does not display output of the command being executed, so if you do a get system status in batch mode, you don’t have any output of this directly. You have to do an execute batch lastlog to get the output.
A use case at the end: Push API feed
A little-known feature a FortiGate has is being able to host feeds itself using the Push API method. The idea is that you use API requests to send IPs, MAC addresses, domains, etc., to the FortiGate; it adds or removes the entries from the feed, and you use the feed wherever it makes sense, like in firewall policies.
One caveat is that the information in the feed is volatile and does not survive a reboot.
Let’s combine a few things from this post to create an example.
We start with our FortiAnalyzer Event Handler Custom-Brute-Force-Account-Login-Attack-FGT (this is the cloned event handler from above) as our trigger. If it gets triggered, we add the source IP from that event to the Push API feed using a webhook,ban the IP and also send an email notification with the log information.


Push API automation stitch configuration
config system automation-trigger
edit "FAZ-BRUTE-FORCE"
set event-type faz-event
set faz-event-name "Custom-Brute-Force-Account-Login-Attack-FGT"
next
end
config system automation-action
edit "PUSH-API-WEBHOOK"
set action-type webhook
set protocol https
set uri "192.168.1.201/api/v2/monitor/system/external-resource/dynamic"
set http-body "{ \"commands\": [{ \"name\": \"PUSH-FEED\", \"command\": \"add\", \"entries\": [ \"%%log.srcip%%\" ] } ]}"
set port 8443
config http-headers
edit 1
set key "Authorization"
set value "4xdzrjdhdrs3417b51wqt4cw05p0rd"
next
end
set verify-host-cert disable
next
end
config system automation-stitch
edit "FAZ-BRUTE-FORCE-PUSH-API"
set status disable
set trigger "FAZ-BRUTE-FORCE"
config actions
edit 3
set action "IP Ban"
set required enable
next
edit 2
set action "PUSH-API-WEBHOOK"
set required enable
next
edit 4
set action "Email Notification"
set required enable
next
end
next
end
Wrapping up
I realize that nothing I covered in this post is that advanced, but I saw some of the things I covered being asked a few times, and some things, like the log dumping, Push API feeds, and auto-scripts as information storage, are things I personally wanted to look at a bit more, and I turned it into a post.
So, dear reader, thank you for your time in indulging me.



















