PaloAltoNetworks / terraform-provider-panos

Terraform Panos provider
https://www.terraform.io/docs/providers/panos/
Mozilla Public License 2.0
87 stars 71 forks source link

Resource Creation - Resource Dependencies not resolved. #265

Closed stobias123 closed 3 years ago

stobias123 commented 3 years ago

Describe the bug

When creating a panos_nat_rule and configuring a panos_ethernet_interface in the same module, I am unable to force the nat rule to depend on output of the ethernet interface configuration.

Expected behavior

Create the eth interface first, then use attributes from that resource to configure the nat resource.

Current behavior

Error.

Error: Invalid index

  on ../modules/config/main.tf line 146, in resource "panos_nat_rule" "default_NAT_out":
 146:     sat_ip_address = panos_ethernet_interface.eth1.static_ips[0]
    |----------------
    | panos_ethernet_interface.eth1.static_ips is empty list of string

The given key does not identify an element in this collection value.

Possible solution

I would think that proper implementation of the dependency graph would resolve this issue...

Steps to reproduce

Use the below config

resource "panos_ethernet_interface" "eth1" {
    name = "ethernet1/1"
    vsys = "vsys1"
    mode = "layer3"
    enable_dhcp = false
    create_dhcp_default_route = false
    static_ips = ["${var.eth1_private_ip}"]
    comment = "Public facing, used for VPN termination"
    management_profile = panos_management_profile.ping_profile.name
}

resource "panos_nat_rule" "default_NAT_out" {
    name = "default_NAT_out"
    description = "This is the default nat for internets traffics"

    source_zones = [panos_zone.trust.name]
    destination_zone = panos_zone.untrust.name

    source_addresses = ["any"]
    destination_addresses = ["any"]

    sat_type = "dynamic-ip-and-port"
    sat_address_type = "interface-address"
    sat_interface = panos_ethernet_interface.eth1.name
    sat_ip_address = panos_ethernet_interface.eth1.static_ips[0]
    depends_on = [
        panos_ethernet_interface.eth1
    ]
}

Context

I am unable to set static IPs and use those settings downstream within the same module. Bootstrapping PANOS config

Your Environment

 ✘ stobias $ terraform version
Terraform v0.12.24
+ provider.aws v3.6.0
+ provider.panos v1.8.0
+ provider.vault v2.18.0
shinmog commented 3 years ago

Using Terraform 0.12.29 and putting your plan file just in a plan file (not in a module), I cannot reproduce your issue. So it's possible there's something about Terraform 0.12.24 and modules that has a bug...? But it does not seem to be the provider.

As an aside, I would recommend using panos_nat_rule_group instead of panos_nat_rule as panos_nat_rule will be removed in the future. It's fine to have a rule group that has a single rule in it, so don't worry about that. The configuration of the NAT rule is more intuitive in panos_nat_rule_group.

Here's the plan file that I used (having that depends_on in the "panos_nat_rule" block is not needed so I removed it):

provider "panos" {
    hostname = "127.0.0.1"
    username = "admin"
    password = "admin"
}

resource "panos_ethernet_interface" "eth1" {
    name = "ethernet1/1"
    vsys = "vsys1"
    mode = "layer3"
    static_ips = [
        "10.5.51.151",
    ]
    comment = "Public facing, used for VPN termination"
}

resource "panos_zone" "trust" {
    name = "L3-trust"
    mode = "layer3"
}

resource "panos_zone" "untrust" {
    name = "L3-untrust"
    mode = "layer3"
}

resource "panos_nat_rule" "default_NAT_out" {
    name = "default_NAT_out"
    description = "This id the default nat for internets traffics"

    source_zones = [
        panos_zone.trust.name,
    ]
    source_addresses = ["any"]

    destination_zone = panos_zone.untrust.name
    destination_addresses = ["any"]

    sat_type = "dynamic-ip-and-port"
    sat_address_type = "interface-address"
    sat_interface = panos_ethernet_interface.eth1.name
    sat_ip_address = panos_ethernet_interface.eth1.static_ips[0]
}

Here's the results of terraform apply -auto-approve:

$ terraform apply -auto-approve
panos_zone.untrust: Creating...
panos_zone.trust: Creating...
panos_ethernet_interface.eth1: Creating...
panos_zone.untrust: Creation complete after 0s [id=vsys1:L3-untrust]
panos_zone.trust: Creation complete after 0s [id=vsys1:L3-trust]
panos_ethernet_interface.eth1: Creation complete after 1s [id=vsys1:ethernet1/1]
panos_nat_rule.default_NAT_out: Creating...
panos_nat_rule.default_NAT_out: Creation complete after 0s [id=vsys1:default_NAT_out]

Warning: Please use panos_nat_rule_group instead

  on plan.tf line 36, in resource "panos_nat_rule" "default_NAT_out":
  36: resource "panos_nat_rule" "default_NAT_out" {

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
$
erichrockman commented 3 years ago

Similarly, when removing an address object, it tries to remove the address before removing from a security policy.

I removed an item from the map "var.onPremCIDR". The tf plan shows the removal of the address object and the removal of that element from the policy. However, it fails.

module.fwPaloAltoTransitCommonConfig02.panos_address_object.fw_onPrem_address["AWS"] will be destroyed

Plan: 0 to add, 2 to change, 2 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

module.fwPaloAltoTransitCommonConfig02.panos_address_object.fw_onPrem_address["AWS"]: Destroying... [id=vsys1:AWS] module.fwPaloAltoTransitCommonConfig01.panos_address_object.fw_onPrem_address["AWS"]: Destroying... [id=vsys1:AWS]

Error: rulebase -> security -> rules -> trust-to-onPrem -> destination

resource "panos_address_object" "fw_onPrem_address" {

for_each = var.onPremCIDR

name = substr(each.key, 0, 31)
type = "ip-netmask"
value = each.value

}

resource "panos_security_rule_group" "fw_azure_transit_rulebase" { position_keyword = "top" rule { name = "trust-to-onPrem" type = "intrazone" source_zones = [panos_zone.fw_trust_zone.name] source_addresses = [for o in panos_address_object.fw_spoke_network_address : o.name] source_users = ["any"] hip_profiles = ["any"] destination_zones = [panos_zone.fw_trust_zone.name] destination_addresses = [for p in panos_address_object.fw_onPrem_address : p.name] applications = ["any"] services = ["any"] categories = ["any"] action = "allow" log_start = true log_end = true } rule { name = "onPrem-to-trust" type = "intrazone" source_zones = [panos_zone.fw_trust_zone.name] source_addresses = [for o in panos_address_object.fw_onPrem_address : o.name] source_users = ["any"] hip_profiles = ["any"] destination_zones = [panos_zone.fw_trust_zone.name] destination_addresses = [for p in panos_address_object.fw_spoke_network_address : p.name] applications = ["any"] services = ["any"] categories = ["any"] action = "allow" log_start = true log_end = true } depends_on = [ panos_address_object.fw_spoke_network_address, panos_address_object.fw_onPrem_address ] }

tried with and without the depends_on block. fails either way.

shinmog commented 3 years ago

@erichrockman

This is a separate thing, and is the way that Terraform is supposed to work. You need to specify lifecycle.create_before_destroy on your resources to tell Terraform what the correct order of operations should be. I don't have this documented in the main provider documentation because it isn't specific to the panos provider, and it isn't something that the provider has control over.

I've opened up this issue (hashicorp/terraform-plugin-sdk#585) to put the burden of this on the provider instead of users.