Closed cliftonvella closed 3 years ago
Before configuring terraform and FortiGate redundant interface, some basic knowledge needs to be mastered:
FortiGate Redundancy: https://docs.fortinet.com/document/fortigate/6.4.0/administration-guide/567758/aggregation-and-redundancy
Terraform basic syntax: Locals: https://www.terraform.io/docs/language/values/locals.html, https://learn.hashicorp.com/tutorials/terraform/locals Dynamic Blocks: https://www.terraform.io/docs/language/expressions/dynamic-blocks.html Import: https://www.terraform.io/docs/cli/import/index.html
Detailed Steps and Validation:
Step1 Prepare the prerequisites
Make sure that the interface to be added to member is not referenced by any other place, as described in https://docs.fortinet.com/document/fortigate/6.4.0/administration-guide/567758/aggregation-and-redundancy:
Redundancy In a redundant interface, traffic only goes over one interface at any time. This differs from an aggregated interface where traffic goes over all interfaces for increased bandwidth. This difference means redundant interfaces can have more robust configurations with fewer possible points of failure. This is important in a fully-meshed HA configuration.
An interface is available to be in a redundant interface if:
It is a physical interface and not a VLAN interface. It is not already part of an aggregated or redundant interface. It is in the same VDOM as the redundant interface. It does not have an IP address and is not configured for DHCP or PPPoE. It has no DHCP server or relay configured on it. It does not have any VLAN subinterfaces. It is not referenced in any security policy, VIP, or multicast policy. It is not monitored by HA. It is not one of the FortiGate-5000 series backplane interfaces. When an interface is included in a redundant interface, it is not listed on the Network > Interfaces page. You cannot configure the interface individually and it is not available for inclusion in security policies, VIPs, or routing.
Step2 Show TF Configuration:
provider "fortios" {
hostname = "192.168.52.177"
insecure = "true"
token = "GNH7r40H65GNb46kd4rG8rtrmn0fr1"
}
locals {
redunantmeb = [
{
interface_name = "port3"
},
{
interface_name = "port4"
},
]
}
resource "fortios_system_interface" "trname" {
algorithm = "L4"
bfd_desired_min_tx = 250
bfd_detect_mult = 3
bfd_required_min_rx = 250
defaultgw = "enable"
ip = "0.0.0.0 0.0.0.0"
mtu = 1500
mtu_override = "disable"
name = "testri"
status = "up"
type = "redundant"
vdom = "root"
dynamic_sort_subtable = true
dynamic "member" {
for_each = local.redunantmeb
content {
interface_name = member.value.interface_name
}
}
}
Step3 Terraform apply and plan:
# terraform apply
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# fortios_system_interface.trname will be created
+ resource "fortios_system_interface" "trname" {
+ ac_name = (known after apply)
+ aggregate = (known after apply)
+ algorithm = "L4"
+ alias = (known after apply)
+ allowaccess = (known after apply)
+ ap_discover = (known after apply)
+ arpforward = (known after apply)
+ auth_type = (known after apply)
+ auto_auth_extension_device = (known after apply)
+ autogenerated = (known after apply)
+ bandwidth_measure_time = (known after apply)
+ bfd = (known after apply)
+ bfd_desired_min_tx = 250
.................
+ username = (known after apply)
+ vdom = "root"
+ vindex = (known after apply)
+ vlan_protocol = (known after apply)
+ vlanforward = (known after apply)
+ vlanid = (known after apply)
+ vrf = (known after apply)
+ vrrp_virtual_mac = (known after apply)
+ wccp = (known after apply)
+ weight = (known after apply)
+ wins_ip = (known after apply)
+ member {
+ interface_name = "port3"
}
+ member {
+ interface_name = "port4"
}
}
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_system_interface.trname: Creating...
fortios_system_interface.trname: Creation complete after 1s [id=testri]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
# terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
fortios_system_interface.trname: Refreshing state... [id=testri]
------------------------------------------------------------------------
No changes. Infrastructure is up-to-date.
This means that Terraform did not detect any differences between your
configuration and real physical resources that exist. As a result, no
actions need to be performed.
Step4 Check result:
labfirewall # config system interface
labfirewall (interface) # show
config system interface
edit "testri"
set vdom "root"
set type redundant
set member "port3" "port4"
set snmp-index 7
next
end
We can find that it has been configured successfully.
BTW: When you come across a parameter that you don’t know how to configure, then you can try to configure a simple template on the FortiGate GUI/CLI, and use the terraform import function to import the configuration on the FGT into Terraform, and then use terraform show to see the contents of the exported terraform hcl format. This can also provide some help.
@frankshen01 Thanks a lot for the very detailed explanation. You’re a life saver!
At one point I did try defining the interface members using a dynamic block, but I was trying to assign the dynamic block as a value to the interface attribute, which obviously did not work. I got a bit confused because I have already done this on Ansible and over there the fortios interface module actually accepts a member attribute, while the Terraform module does things differently.
May I ask if what you wrote in your reply is documented somewhere? Because the official provider documentation on the Terraform website does not show any of this.
@cliftonvella You're welcome!
> At one point I did try defining the interface members using a dynamic block, but I was trying to assign the dynamic block as a value to the interface attribute, which obviously did not work. I got a bit confused because I have already done this on Ansible and over there the fortios interface module actually accepts a member attribute, while the Terraform module does things differently.
As described in the aforementioned https://www.terraform.io/docs/language/expressions/dynamic-blocks.html:
dynamic "origin_group" {
for_each = var.load_balancer_origin_groups
content {
name = origin_group.key
dynamic "origin" {
for_each = origin_group.value.origins
content {
hostname = origin.value.hostname
}
}
}
}
Here is a clear way to use dynamic. As for the Ansible you mentioned, Terraform's syntax itself has many differences with Ansible. Also, there is no concept of module you mentioned in Terraform. And terraform provides the concept of resource with state maintenance as the core.
> May I ask if what you wrote in your reply is documented somewhere? Because the official provider documentation on the Terraform website does not show any of this.
The reason why I spent 2 hours and described and answered your question in the most detailed way possible as I can is mainly to help you solve the problem quickly. I sincerely recommend to you a book "terraform up and running" (plus terraform official documents), from which you can master a lot of terraform software's own advantages, skills and usage methods.
Thanks!
Hello @frankshen01, thanks a lot for your detailed answer once again. I will definitely check out that book you recommended.
Terraform Version: 0.15.4 FortiOS Provider Version: 1.11.0
Hi,
I am trying to find a way to member interfaces to a redundant interface on a Fortigate using Terraform.
I am using the “fortios_system_interface” resource and I manage to create the redundant interface itself:
When I try to add the “member” attribute, I get an error saying that the resource does not expect that attribute, when it is in fact listed in the documentation. I took a look at the code for the “fortios_system_interface” resource and it seems that “member” is expected to be a list of maps, so I tried to define as a local variable like so:
Then, I tried adding the “interface” attribute to the resource, with “${local.member}” as its value, but I get an error saying that the “interface” attribute expects a value of type string. So out of frustration I tried defining the “interface” attribute as “port3,port4”, and while this time I got no errors, the Fortigate simply ignored that and no members were added to the redundant interface.
I’m honestly at a loss on how to do this so if someone with experience on this can help it would be greatly appreciated.
Thanks and Regards,
Clifton