Skip to content

Unable to control policy order #85

@liftconfig

Description

@liftconfig

I'm trying to programmatically create address objects and a policy package using the Provider. I cannot work out how to effectively control policy order in a policy package. Without this, I can't use the provider to manage policy packages.

I'm using for_each to create fortimanager_packages_firewall_policy resources from a list of policies - this works but the policies are created in a random order due to there being no execution order with terraform for_each object even with parallelism set to 1 (ref: hashicorp/terraform#30841).

I'm then creating a fortimanager_packages_firewall_policy_move resource for each policy to order the policies in Fortimanager based on the order of policies in the list. This works and all policy_move resources are created as intended with the correct policy and target, but again due to no execution order within terraform for_each the order the policy_move resources are created and policies are moved to is random.

For example policy id 90 is moved after 100, then 100 is moved after 110 but because 110 could be above in the policy package and its policy_move resources hasn't been created yet 100 will move after 110 meaning it's now above 90. After running apply I can see in the state file that terraform knows the policies aren't where they should be according to the policy_move resource state_pos attribute, but on subsequent applies it doesn't try and fix it, it just shows no changes to infrastructure.

terraform {
  required_providers {
    fortimanager = {
      source  = "fortinetdev/fortimanager"
      version = "1.15.0"
    }
  }
}


locals {
  address_objects = yamldecode(file("${path.module}/firewall_address.yaml"))
  all_addresses = merge(
    { for k, v in local.address_objects.ipmask_addresses : k => merge(v, { type = "ipmask" }) },
    { for k, v in local.address_objects.fqdn_addresses : k => merge(v, { type = "fqdn" }) }
  )

  policy_list = yamldecode(file("${path.module}/firewall_policies.yaml")).policies
  policy_map  = { for p in local.policy_list : p.policyid => p }
}

resource "fortimanager_packages_pkg" "aws" {
  name = "aws"
  type = "pkg"
}

resource "fortimanager_object_firewall_address" "bulk_addresses" {
  for_each = local.all_addresses

  name     = each.key
  obj_type = "ip"
  type     = each.value.type
  subnet   = lookup(each.value, "subnet", null)
  fqdn     = lookup(each.value, "fqdn", null)
}

resource "fortimanager_packages_firewall_policy" "bulk_policies" {
  for_each = local.policy_map

  pkg      = fortimanager_packages_pkg.aws.name
  policyid = each.key
  name     = each.value.name

  srcintf = lookup(each.value, "srcintf", ["any"])
  dstintf = lookup(each.value, "dstintf", ["any"])
  srcaddr = [for addr in each.value.srcaddr : fortimanager_object_firewall_address.bulk_addresses[addr].name]
  dstaddr = [for addr in each.value.dstaddr : fortimanager_object_firewall_address.bulk_addresses[addr].name]
  service = each.value.service

  action     = lookup(each.value, "action", "accept")
  status     = lookup(each.value, "status", "enable")
  schedule   = lookup(each.value, "schedule", "always")
  logtraffic = lookup(each.value, "logtraffic", "all")

  nat               = lookup(each.value, "nat", null)
  utm_status        = lookup(each.value, "utm_status", null)
  av_profile        = lookup(each.value, "av_profile", null)
  ips_sensor        = lookup(each.value, "ips_sensor", null)
  webfilter_profile = lookup(each.value, "webfilter_profile", null)
  comments          = lookup(each.value, "comments", null)
}

resource "fortimanager_packages_firewall_policy_move" "order_policies" {
  for_each = {
    for i, policy in local.policy_list : 
    policy.policyid => i if i > 0
  }
  
  pkg    = fortimanager_packages_pkg.aws.name
  policy = fortimanager_packages_firewall_policy.bulk_policies[each.key].policyid
  target = fortimanager_packages_firewall_policy.bulk_policies[local.policy_list[each.value - 1].policyid].policyid
  option = "after"
}

firewall_address.yaml

ipmask_addresses:
  addr_192_168_0_1: {subnet: ["192.168.0.1", "255.255.255.255"]}
  addr_192_168_0_2: {subnet: ["192.168.0.2", "255.255.255.255"]}
  addr_192_168_0_3: {subnet: ["192.168.0.3", "255.255.255.255"]}

fqdn_addresses:
  addr_1_aws_com: {fqdn: "1.aws.com"}
  addr_2_aws_com: {fqdn: "2.aws.com"}
  addr_3_aws_com: {fqdn: "3.aws.com"}

firewall_policies.yaml

policies:
  - name: "policy_10"
    policyid: 10
    srcaddr: ["addr_192_168_0_1"]
    dstaddr: ["addr_1_aws_com"]
    service: ["HTTP"]
  - name: "policy_20"
    policyid: 20
    srcaddr: ["addr_192_168_0_2"]
    dstaddr: ["addr_2_aws_com"]
    service: ["HTTPS"]
  - name: "policy_30"
    policyid: 30
    srcaddr: ["addr_192_168_0_3"]
    dstaddr: ["addr_3_aws_com"]
    service: ["ALL"]
  - name: "policy_40"
    policyid: 40
    srcaddr: ["addr_192_168_0_4"]
    dstaddr: ["addr_4_aws_com"]
    service: ["SSH"]
  - name: "policy_50"
    policyid: 50
    srcaddr: ["addr_192_168_0_5"]
    dstaddr: ["addr_5_aws_com"]
    service: ["FTP"]
  - name: "policy_60"
    policyid: 60
    srcaddr: ["addr_192_168_0_6"]
    dstaddr: ["addr_6_aws_com"]
    service: ["DNS"]
  - name: "policy_70"
    policyid: 70
    srcaddr: ["addr_192_168_0_7"]
    dstaddr: ["addr_7_aws_com"]
    service: ["SMTP"]
  - name: "policy_80"
    policyid: 80
    srcaddr: ["addr_192_168_0_8"]
    dstaddr: ["addr_8_aws_com"]
    service: ["POP3"]
  - name: "policy_90"
    policyid: 90
    srcaddr: ["addr_192_168_0_9"]
    dstaddr: ["addr_9_aws_com"]
    service: ["IMAP"]
  - name: "policy_100"
    policyid: 100
    srcaddr: ["addr_192_168_0_10"]
    dstaddr: ["addr_10_aws_com"]
    service: ["SNMP"]
  - name: "policy_110"
    policyid: 110
    srcaddr: ["addr_192_168_0_11"]
    dstaddr: ["addr_11_aws_com"]
    service: ["TELNET"]
  - name: "policy_120"
    policyid: 120
    srcaddr: ["addr_192_168_0_12"]
    dstaddr: ["addr_12_aws_com"]
    service: ["HTTP", "HTTPS"]
  - name: "policy_130"
    policyid: 130
    srcaddr: ["addr_192_168_0_13"]
    dstaddr: ["addr_13_aws_com"]
    service: ["ALL_TCP"]
  - name: "policy_140"
    policyid: 140
    srcaddr: ["addr_192_168_0_14"]
    dstaddr: ["addr_14_aws_com"]
    service: ["ALL_UDP"]
  - name: "policy_150"
    policyid: 150
    srcaddr: ["addr_192_168_0_15"]
    dstaddr: ["addr_15_aws_com"]
    service: ["ALL_ICMP"]
  - name: "policy_160"
    policyid: 160
    srcaddr: ["addr_192_168_0_16"]
    dstaddr: ["addr_16_aws_com"]
    service: ["RDP"]
  - name: "policy_170"
    policyid: 170
    srcaddr: ["addr_192_168_0_17"]
    dstaddr: ["addr_17_aws_com"]
    service: ["VNC"]
  - name: "policy_180"
    policyid: 180
    srcaddr: ["addr_192_168_0_18"]
    dstaddr: ["addr_18_aws_com"]
    service: ["LDAP"]
  - name: "policy_190"
    policyid: 190
    srcaddr: ["addr_192_168_0_19"]
    dstaddr: ["addr_19_aws_com"]
    service: ["NTP"]
  - name: "policy_200"
    policyid: 200
    srcaddr: ["addr_192_168_0_20"]
    dstaddr: ["addr_20_aws_com"]
    service: ["ALL"]

I could be completely off base here but I tried to research and find examples of how to manage policy order with the Provider and couldn't find anything. How is policy order supposed to be managed? Individual resources with depends_on is not scalable when dealing with hundreds of policies and letting policies be created in a random order won't work either.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions