Automating FortiGate Configuration at Scale with Python and Netmiko
Stop wasting time with manual CLI updates. Learn how to use Python and Netmiko to push standardized configuration blocks to multiple FortiGate firewalls efficiently.
Let’s cut to the chase. If you manage more than a handful of FortiGate firewalls, logging into each one manually via the GUI or SSH to paste the exact same CLI commands is a massive waste of your time. Worse, it’s a fast track to introducing human error into your network.
Whether you need to update an NTP server across 50 branches, add a new administrative sub-interface, or push a standardized alias to your WAN ports, doing it manually isn't scalable.
Today, we're going to solve this operational issue. We aren't going to build a complex orchestration pipeline or wrestle with APIs just yet. We are going to use plain, reliable SSH automation with Python to push configuration blocks to multiple firewalls.
Here is a practical, engineer-focused guide to getting it done.
Prerequisites
You don't need a massive development environment for this. You just need Python installed on your machine and a single third-party library.
Open your terminal and run:
pip install netmiko
And honestly, that’s it. We are using Netmiko because it handles all the underlying SSH logic, prompt detection, and timing specifically for network devices. You don't have to worry about buffer sizes or waiting for the device to return a # prompt—Netmiko manages the heavy lifting.
File Structure
A golden rule of scripting for network engineering: never hardcode your target IPs or your commands into your main script. Keep your logic separate from your data. This makes your tool reusable for the next ticket that comes your way.
Create a new folder for your project and set up the following file structure:
project/
│
├── script.py
├── ips.txt
└── commands.txt
Data Configuration
ips.txt
This file contains the management IP addresses of your target FortiGates. Just list them out, one per line:
192.168.1.1
192.168.1.2
192.168.1.3
commands.txt
This is the exact sequence of commands you would normally type into the FortiGate CLI. For this example, we are simply adding an alias to port1.
config system interface
edit port1
set alias WAN
end
Note: Always ensure you include
endornextin your FortiGate command blocks to properly save and exit the configuration hierarchy.
The Python Script
Now for the engine. Open script.py and paste the following code.
This script reads your IPs, reads your commands, iterates through the list of firewalls, logs in, pushes the config, and prints the output so you can verify it worked.
from netmiko import Netmiko
device = {
'username': 'yourusernamehere', # Replace with your SSH username
'password': 'yourpasswordhere', # Replace with your SSH password
'device_type': 'fortinet',
}
def main():
with open('ips.txt', 'r') as ip_file:
ips = ip_file.read().splitlines()
with open('commands.txt', 'r') as cmd_file:
commands = cmd_file.read().splitlines()
for ip in ips:
print(f"Configuring device with IP: {ip}")
device['host'] = ip
net_connect = Netmiko(
host=device['host'],
username=device['username'],
password=device['password'],
device_type=device['device_type']
)
output = net_connect.send_config_set(commands)
print(output)
net_connect.disconnect()
if __name__ == "__main__":
main()
Execution
- Make sure you have updated the
usernameandpasswordvariables in script.py with your administrative credentials. - Ensure your workstation has SSH reachability to the IPs listed in ips.txt.
- Open your terminal, navigate to your project/ directory, and execute the script:
python script.py
Watch the terminal. You will see Netmiko log into each device sequentially, apply the interface alias, and output the FortiGate's CLI response.
Final Thoughts
This is a foundational script. It solves an immediate problem and saves time. Once you get comfortable with this workflow, you can start expanding it—perhaps adding a pre-check command to verify the interface state before making changes, or writing the output to a log file instead of the terminal.