Using Ansible to Configure FortiGate VLAN Interfaces to a Basic Standard

I had quite a few problems figuring out a basic configuration to assist with quickly creating new interfaces to a standard. The below configuration is working for me perfectly, allowing quick configuration of a new interface and DHCP server to a standard for easy micro-segmentation in my home network.

This method assumes a few things. All interfaces will be built the same way, on the same physical interface, fortilink in this case. All interfaces are VLAN, all networks are /24, and all have the same DNS and search space.

Inventory File (inventory):

Here is my Fortigate inventory file. This is using the HTTPS and API access method.

[fortigates]
fw01 ansible_host=172.17.33.1 fortios_access_token=<FORTIGATE RESTAPI TOKEN>

[fortigates:vars]
ansible_network_os=fortinet.fortios.fortios

[all:vars]
ansible_httpapi_use_ssl=yes
ansible_httpapi_validate_certs=no
ansible_httpapi_port=443

inventory

Interface Definitions (interfaces.yml)

This file contains two variables in a list, the name of the interface, always structered by <LOCATION>-<PURPOSE>-<VLAN>, and interface access.

fortigate_interfaces:
    #access: "ping, ssh, http, https, snmp"
...
  - name: SER-Kasm-103
    access: "ping"
  - name: SER-MGMT-104
    access: "ping,https,http,ssh"
...

interfaces.yml

Playbook (fortigate-interfaces.yml)

This Ansible playbook file loops through the interfaces in the interfaces.yml file, defined above.

- hosts: fortigates
  gather_facts: no
  hosts: fortigates
  connection: httpapi
  collections:
  - fortinet.fortios
  vars_files:
  - interfaces.yml

  tasks:
    - name: Configure Fortigate interfaces
      fortios_system_interface:
        vdom: "root"
        state: "present"
        access_token: "{{ fortios_access_token }}"
        system_interface:
          name: "{{ item.name }}"
          vdom: "root"
          type: "vlan"
          interface: "fortilink"
          vlanid: "{{ item.name.split('-')[-1] }}"
          ip: "192.168.{{ item.name.split('-')[-1] }}.1/24"
          role: "lan"
          allowaccess: "{{ item.access }}"
          device_identification: "enable"
      loop: "{{ fortigate_interfaces }}"

    - name: Configure DHCP Server on Fortigate
      fortios_system_dhcp_server:
        vdom: "root"
        state: "present"
        access_token: "{{ fortios_access_token }}"
        system_dhcp_server:
          id: "{{ item.name.split('-')[-1] | int }}"
          status: "enable"
          server_type: "regular"
          interface: "{{ item.name }}"
          default_gateway: "192.168.{{ item.name.split('-')[-1] }}.1"
          netmask: "255.255.255.0"
          ip_range:
            - id: 1
              start_ip: "192.168.{{ item.name.split('-')[-1] }}.100"
              end_ip: "192.168.{{ item.name.split('-')[-1] }}.254"
          dns_server1: "192.168.105.10"
          domain: "christianflatt.com"
      loop: "{{ fortigate_interfaces }}"

fortigate-interfaces.yml

A couple of notes here:
  • The VLAN ID is also the third octet for ease of use of managing my home network, so I pull out the value to define the IPs.
  • I use the VLAN ID for the id of the DHCP server. Fortinet supports auto-incrementing with 0, but the configuration will then not be idempotent on subsequent runs. Instead, it creates a new DHCP server, assigns it to the interface, then errors out on ip_range portion.