Error messages after change to Azure Palo module - This object does not have an attribute named "network_interface_id"

EDIT: Solved I was missing the output value that is needed to call that type of module value. Thanks @apparentlymart

I’m using the following repo to make a template for some Palo firewalls, I had to change some things in the module to make it work with my environment, mostly naming. terraform/fw_common.tf at master · wwce/terraform · GitHub

Error Message:


on main.tf line 87, in module "common_intlb":
│   87:   network_interface_ids    = module.common_fw.network_interface_id[2]
│     ├────────────────
│     │ module.common_fw is a object, known only after apply
│
│ This object does not have an attribute named "network_interface_id".

Here is my fw_commom.tf:

provider "azurerm" {

    features {}

}

resource "azurerm_resource_group" "lower_palo_rg" {

  name     = var.lowerPaloRG

  location = var.location

  tags = {

      "Environment" = var.environment,

      "DeployedBy" = "Terraform",

      "OwnerEmail" = "Codymalloy@amrock.com",

      "OwningTeam" = "Infrared",

      "Application" = var.application

  }

}

resource "azurerm_storage_account" "main" {

  name                     = var.storageAccountName

  resource_group_name      = var.lowerPaloRG

  location                 = var.location

  account_tier             = "Standard"

  account_replication_type = "LRS"

}

module "common_fw" {

  source                    = "./modules/vm-series/"

  #name might need to be indexed in the actual modele and not here - prints as vm1 and vm2 in 'terraform plan

  #so it's very likley this logic is actuall already set here, just need to chagne from 'vm' to '0' so we get '-01' and not '-vm1'

  name                      = "${var.fw_prefix}-0"

  resource_group_name       = var.lowerPaloRG

  location                  = var.location

  vm_count                  = 2

  username                  = var.fw_username

  password                  = var.fw_password

  panos                     = var.fw_panos

  license                   = var.fw_license

  avset_name                = var.avSetName

  subnet_mgmt               = var.subnet_mgmt_id

  subnet_untrust            = var.subnet_untrust_id

  subnet_trust              = var.subnet_trust_id

  #Enable but have NSG

  nic0_public_ip            = false

  nic1_public_ip            = false

  nic2_public_ip            = false

 // nic1_backend_pool_id      = [module.common_extlb.backend_pool_id]

 // nic2_backend_pool_id      = [module.common_intlb.backend_pool_id]

  mgmtNSGname               = var.mgmtNSGname

  dataNSGname               = var.dataNSGname

  pipNicName                = var.pipNicName

  storageUri                = azurerm_storage_account.main.primary_blob_endpoint

  nsg_prefix                = var.mgmtIngressNSGAddress

  public_ip_address_allocation = "static"

  nicName                   = var.nicName

  untrustNicName            = var.untrustNicName

  trustNicName              = var.trustNicName

  mgmtNicName               = var.mgmtNicName

}

module "common_extlb" {

  source                   = "./modules/lb/"

  name                     = var.extLbName

  resource_group_name      = var.lowerPaloRG

  location                 = var.location

  type                     = "public"

  sku                      = "Standard"

  probe_ports              = [443]

  frontend_ports           = [443]

  backend_ports            = [443]

  protocol                 = "Tcp"

  network_interface_ids    = module.common_fw.nic1_id

}

module "common_intlb" {

  source                   = "./modules/lb/"

  name                     = var.intLbName

  resource_group_name      = var.lowerPaloRG

  location                 = var.location

  type                     = "private"

  sku                      = "Standard"

  probe_ports              = [443]

  frontend_ports           = ["All"]

  backend_ports            = ["All"]

  protocol                 = "All"

  subnet_id                = var.subnet_trust_id

  private_ip_address       = var.intLbStaticIP

  network_interface_ids    = module.common_fw.nic2_id

}

Here is the module:


#-----------------------------------------------------------------------------------------------------------------
# Create NSGs for firewall dataplane interfaces (required for Standard SKU LB)
resource "azurerm_network_security_group" "mgmt" {
  name                = var.mgmtNSGname
  location            = var.location
  resource_group_name = var.resource_group_name

  security_rule {
    name                       = "mgmt-inbound"
    priority                   = 1000
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_ranges    = ["443", "22"]
    source_address_prefix      = var.nsg_prefix
    destination_address_prefix = "*"
  }
}

resource "azurerm_network_security_group" "data" {
  name                = var.dataNSGname
  location            = var.location
  resource_group_name = var.resource_group_name

  security_rule {
    name                       = "data-inbound"
    priority                   = 1000
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "*"
    source_port_range          = "*"
    destination_port_range     = "*"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }

  security_rule {
    name                       = "data-outbound"
    priority                   = 1000
    direction                  = "Outbound"
    access                     = "Allow"
    protocol                   = "*"
    source_port_range          = "*"
    destination_port_range     = "*"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }
}

#-----------------------------------------------------------------------------------------------------------------
# Create public IPs for firewall's management & dataplane1 interface
resource "azurerm_public_ip" "nic0" {
  count               = var.nic0_public_ip ? var.vm_count : 0
  name                = "${var.pipNicName}-01"
  location            = var.location
  resource_group_name = var.resource_group_name
  allocation_method   = var.public_ip_address_allocation
  sku                 = var.sku
}

resource "azurerm_public_ip" "nic1" {
  count               = var.nic1_public_ip ? var.vm_count : 0
  name                = "${var.pipNicName}-01"
  location            = var.location
  resource_group_name = var.resource_group_name
  allocation_method   = var.public_ip_address_allocation
  sku                 = var.sku
}

resource "azurerm_public_ip" "nic2" {
  count               = var.nic2_public_ip ? var.vm_count : 0
  name                = "${var.pipNicName}-01"
  location            = var.location
  resource_group_name = var.resource_group_name
  allocation_method   = var.public_ip_address_allocation
  sku                 = var.sku
}

#-----------------------------------------------------------------------------------------------------------------
# Create firewall interfaces (mgmt, data1, data2).  Dynamic interface is created first, then IP is set statically.

resource "azurerm_network_interface" "nic0" {
  count                     = var.vm_count
  name                      = var.mgmtNicName
  location                  = var.location
  resource_group_name       = var.resource_group_name

  ip_configuration {
    name                          = "ipconfig1"
    subnet_id                     = var.subnet_mgmt
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = var.nic0_public_ip ? element(concat(azurerm_public_ip.nic0.*.id, [""]), count.index) : ""

  }
}

resource "azurerm_network_interface" "nic1" {
  count                     = var.vm_count
  name                      = var.untrustNicName
  location                  = var.location
  resource_group_name       = var.resource_group_name
  enable_ip_forwarding      = true

  ip_configuration {
    name                                    = "ipconfig1"
    subnet_id                               = var.subnet_untrust
    private_ip_address_allocation           = "Dynamic"
    public_ip_address_id                    = var.nic1_public_ip ? element(concat(azurerm_public_ip.nic1.*.id, [""]), count.index) : ""
  }
}

resource "azurerm_network_interface" "nic2" {
  count                     = var.vm_count
  name                      = var.trustNicName
  location                  = var.location
  resource_group_name       = var.resource_group_name
  enable_ip_forwarding      = true

  ip_configuration {
    name                                    = "ipconfig1"
    subnet_id                               = var.subnet_trust
    private_ip_address_allocation           = "Dynamic"
    public_ip_address_id                    = var.nic2_public_ip ? element(concat(azurerm_public_ip.nic2.*.id, [""]), count.index) : ""
  }
}

resource "azurerm_network_interface_security_group_association" "nic0" {
  count                     = var.vm_count
  network_interface_id      = element(azurerm_network_interface.nic0.*.id, count.index)
  network_security_group_id = azurerm_network_security_group.mgmt.id
}

resource "azurerm_network_interface_security_group_association" "nic1" {
  count                     = var.vm_count
  network_interface_id      = element(azurerm_network_interface.nic1.*.id, count.index)
  network_security_group_id = azurerm_network_security_group.data.id
}

resource "azurerm_network_interface_security_group_association" "nic2" {
  count                     = var.vm_count
  network_interface_id      = element(azurerm_network_interface.nic2.*.id, count.index)
  network_security_group_id = azurerm_network_security_group.data.id
}


#-----------------------------------------------------------------------------------------------------------------
# Create VM-Series NGFWs
resource "azurerm_availability_set" "default" {
  name                        = var.avset_name
  location                    = var.location
  resource_group_name         = var.resource_group_name
  platform_fault_domain_count = var.avset_fault_domain_count
  managed                     = true
}

resource "azurerm_virtual_machine" "vmseries" {
  count                        = var.vm_count
  name                         = "${var.name}${count.index + 1}"
  location                     = var.location
  resource_group_name          = var.resource_group_name
  vm_size                      = var.size
  primary_network_interface_id = element(azurerm_network_interface.nic0.*.id, count.index)

  network_interface_ids = [
    element(azurerm_network_interface.nic0.*.id, count.index),
    element(azurerm_network_interface.nic1.*.id, count.index),
    element(azurerm_network_interface.nic2.*.id, count.index),
  ]

  availability_set_id = azurerm_availability_set.default.id

  os_profile_linux_config {
    disable_password_authentication = false
  }

  plan {
    name      = var.license
    publisher = "paloaltonetworks"
    product   = "vmseries-flex"
  }

  storage_image_reference {
    publisher = "paloaltonetworks"
    offer     = "vmseries-flex"
    sku       = var.license
    version   = var.panos
  }

  storage_os_disk {
    name              = "${var.name}${count.index + 1}-osdisk"
    caching           = "ReadWrite"
    create_option     = "FromImage"
    managed_disk_type = "Standard_LRS"
  }

  os_profile {
    computer_name  = "${var.name}${count.index + 1}"
    admin_username = var.username
    admin_password = var.password
  }

  boot_diagnostics {
    enabled = true
    storage_uri = var.storageUri
  }
}

Hi @camalloy,

In order for there to be an attribute named network_interface_id on module.common_fw, that module must declare an output value of that name:

output "network_interface_id" {
  # ...
}

I don’t see a declaration like this in the configuration snippets you shared, though perhaps it’s in a different file that you didn’t include. If so, it’d be helpful if you could share the contents of that file to see how the output value is declared.

1 Like

Thanks for the info. This actually gave me the solution. The default template came with an output.tf file, I removed it since most of the things were not relevant, turns out it did have some critical stuff in there.