Note: The description in this article is based on Terraform version 1.14.6, FortiOS Provider version 1.24.1 and FortiGate with FortiOS 7.4.11.
HashiCorp Terraform
In previous articles in the Infrastructure as Code - Terraform series, we described the basics of using Terraform. From installation, creating a project and files, using variables, blocks for resource configuration and loading data sources, creating multiple instances of a single resource, and importing existing resources.
FortiOS Provider
We demonstrated the use of Terraform in practice with the VMware Cloud Director Provider and created objects in VMware virtualization. In this article, we will look at the FortiOS Provider and the configuration of the FortiGate system. We will build on the information described earlier and simply apply it to a different solution.
The FortiOS provider fortinetdev/fortios is currently at version 1.24. It supports FortiGate with FortiOS 6.0 or higher (6.0, 6.2, 6.4, 7.0, 7.2, 7.4, 7.6) and FortiManager 6.0 and 6.2.
Note: I am not saying (nor do I dare to judge) that Terraform is the optimal Infrastructure as Code (IaC) tool for firewall configuration. However, we have a FortiOS provider available with a large number of resources and we can use it to configure most FortiOS functions.
Terraform Project for FortiGate
We will create a folder for our project (here d:\Terraform\FortiGate), where we will store configuration files in the HCL language. We will open the folder in Visual Studio Code.
In our example, we will use the following files:
terraform.tf- Terraform and FortiOS Provider configurationvariables.tf- variable name definitionsterraform.tfvars- assignment of values to variablesmain.tf- resource configuration (FortiGate settings)import.tf- import of existing objects (resources)
Terraform and Provider Configuration
We will create the file terraform.tf, into which we will insert the configuration of Terraform itself and the FortiOS provider.
# Version and providers requirement terraform { required_version = ">= 1.14.0" required_providers { fortios = { source = "fortinetdev/fortios" version = "~> 1.24" } } } # Configure the FortiOS Provider provider "fortios" { hostname = var.forti_hostname token = var.forti_token insecure = var.forti_allow_unverified_ssl # vdom = "root" }
Authentication to FortiGate
In the provider configuration, we must use credentials with sufficient privileges. We can provide static credentials (API token or username and password) or use environment variables.
The recommended approach is to use an API token, which can be created in the FortiGate GUI:
- (Global) > System > Administrators
- Create New - REST API Admin
- enter a Username for identification, select an Administrator Profile or create a new one (with minimum required privileges that are still sufficient for configuration), access can be restricted using Trusted Hosts
- after clicking OK, the API key will be displayed — make sure to copy it (it cannot be displayed again)

Variable Definitions
To define variables, we will create a variables.tf file.
# FortiOS variables
variable "forti_hostname" {
description = "hostname or IP address of FortiOS unit"
type = string
}
variable "forti_token" {
description = "token of FortiOS unit"
type = string
}
variable "forti_allow_unverified_ssl" {
description = "Allow perform insecure SSL requests / unverified SSL"
type = bool
}
Assigning Values to Variables
We will set the variable values in the terraform.tfvars file.
# Configure the FortiOS Provider
forti_hostname = "fortigate.domain.local"
forti_token = "xxx"
forti_allow_unverified_ssl = true
Terraform Workspace Initialization
We will use the Terraform CLI command to initialize the working directory and download the provider. In VS Code, we will use the terminal.
PS D:\Terraform\FortiGate> terraform init Initializing the backend... Initializing provider plugins... - Finding fortinetdev/fortios versions matching "~> 1.24"... - Installing fortinetdev/fortios v1.24.1... - Installed fortinetdev/fortios v1.24.1 (signed by a HashiCorp partner, key ID 325239133A112044) Partner and community providers are signed by their developers. If you'd like to know more about provider signing, you can read about it here: https://developer.hashicorp.com/terraform/cli/plugins/signing Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run "terraform init" in the future. Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary.
Creating a Firewall Policy
In this example, we will use Terraform to create a simple IPv4 policy on FortiGate. This is just a small demonstration of what is possible.
Policy Configuration
We will create a main.tf file in which we will define the configuration of the fortios_firewall_policy resource. This resource has a large number of arguments, so we can likely configure all options of a Firewall Policy.
# create IPv4 policy
resource "fortios_firewall_policy" "web_access" {
name = "Web Access to internet"
action = "accept"
schedule = "always"
logtraffic = "all"
nat = "enable"
inspection_mode = "flow"
status = "enable"
service {
name = "HTTP"
}
service {
name = "HTTPS"
}
srcaddr {
name = "all"
}
srcintf {
name = "LocalZone"
}
dstaddr {
name = "all"
}
dstintf {
name = "DMZzone"
}
}
Applying the Terraform Configuration
Using the Terraform CLI, we will execute the operations proposed by the Terraform plan. After confirmation, a new policy will be created.

PS D:\Terraform\FortiGate> terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
+ create
Terraform will perform the following actions:
# fortios_firewall_policy.web_access will be created
+ resource "fortios_firewall_policy" "web_access" {
+ action = "accept"
+ anti_replay = (known after apply)
...
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
fortios_firewall_policy.web_access: Creating...
fortios_firewall_policy.web_access: Creation complete after 0s [id=104]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Configuration Error
If we make an error in the policy configuration that relates to a FortiOS setting, the apply will fail. Here, for example, an incorrect interface name that did not exist was used.
fortios_firewall_policy.web_access: Creating...
- Error: Error creating FirewallPolicy resource: Internal Server Error - Internal error when processing the request (500)
- Cli response:
- [node_check_object fail! for name LocalZone value parse error before 'LocalZone' Command fail. Return code -651]
-
- with fortios_firewall_policy.web_access,
- on main.tf line 47, in resource "fortios_firewall_policy" "web_access":
- 47: resource "fortios_firewall_policy" "web_access" {
Dobrý den, podařilo se Vám zjistit jak lze nastavit pořadí pravidel v policies? Me se to povedlo pouze voláním python skriptu, který to srovná, ale to je takové reseni ohejbak na ohejbak. Diky
respond to [1]Dewpew: Pořadí záznamů je velký problém. V dalším článku budu popisovat takový workaround pro DNS záznamy (ale taky to není ideální). Tu Policy jsem si jentak zkoušel a moc neřešil. Nevím také jaké pořadí tam řešíte (zda je to stejný problém jako u sub-bloků). Fortinet má takovou oficiální informaci registry.terraform.io/providers/fortinetdev/fortios/latest/docs/guides/fgt_policymove.