v.0.12.10
main.tf
resource "azurerm_network_security_rule" "predefined_rules" {
count = length(var.predefined_rules)
name = lookup(var.predefined_rules[count.index], "name")
source_address_prefix = join(",", lookup(var.predefined_rules[count.index], "source_address_prefix", var.source_address_prefix))
destination_address_prefix = join(",", lookup(var.predefined_rules[count.index], "destination_address_prefix", var.destination_address_prefix))
resource_group_name = azurerm_resource_group.nsg.name
network_security_group_name = azurerm_network_security_group.nsg.name
}
variable.tf
# Predefined rules
variable "predefined_rules" {
type = list(any)
default = []
}
# source address prefix to be applied to all rules
variable "source_address_prefix" {
type = list(string)
default = ["*"]
# Example ["10.0.3.0/24"] or ["VirtualNetwork"]
}
# Destination address prefix to be applied to all rules
variable "destination_address_prefix" {
type = list(string)
default = ["*"]
# Example ["10.0.3.0/32","10.0.3.128/32"] or ["VirtualNetwork"]
}
Error: Invalid function argument
TestTerraformNSG 2019-11-13T06:42:00Z command.go:100:
TestTerraformNSG 2019-11-13T06:42:00Z command.go:100: on ../../main.tf line 27, in resource "azurerm_network_security_rule" "predefined_rules":
TestTerraformNSG 2019-11-13T06:42:00Z command.go:100: 27: source_address_prefix = join(",", lookup(var.predefined_rules[count.index], "source_address_prefix", var.source_address_prefix))
TestTerraformNSG 2019-11-13T06:42:00Z command.go:100: |----------------
TestTerraformNSG 2019-11-13T06:42:00Z command.go:100: | var.source_address_prefix is list of string with 1 element
TestTerraformNSG 2019-11-13T06:42:00Z command.go:100:
TestTerraformNSG 2019-11-13T06:42:00Z command.go:100: Invalid value for "default" parameter: the default value must have the same
TestTerraformNSG 2019-11-13T06:42:00Z command.go:100: type as the map elements.
TestTerraformNSG 2019-11-13T06:42:00Z command.go:100:
TestTerraformNSG 2019-11-13T06:42:00Z command.go:100:
Error: Invalid function argument
TestTerraformNSG 2019-11-13T06:42:00Z command.go:100:
TestTerraformNSG 2019-11-13T06:42:00Z command.go:100: on ../../main.tf line 28, in resource "azurerm_network_security_rule" "predefined_rules":
TestTerraformNSG 2019-11-13T06:42:00Z command.go:100: 28: destination_address_prefix = join(",", lookup(var.predefined_rules[count.index], "destination_address_prefix", var.destination_address_prefix))
TestTerraformNSG 2019-11-13T06:42:00Z command.go:100: |----------------
TestTerraformNSG 2019-11-13T06:42:00Z command.go:100: | var.destination_address_prefix is list of string with 1 element
TestTerraformNSG 2019-11-13T06:42:00Z command.go:100:
TestTerraformNSG 2019-11-13T06:42:00Z command.go:100: Invalid value for "default" parameter: the default value must have the same
TestTerraformNSG 2019-11-13T06:42:00Z command.go:100: type as the map elements.
I want to lookup the key source_address_prefix in predefined_rules[index] map,it's a list, if the key is not included in the map, I'd like to return a list(string) var.source_address_prefix, which isn't supported
error raised
Hi @yupwei68! Sorry for this strange behavior, and thanks for reporting it.
It looks like the type checking in the lookup function is getting confused by the list(any) type on that variable. It _should_ respond to that situation (that is, when the element type of the list isn't known yet) by marking the result as unknown, but it seems to be detecting an error instead.
We'll investigate this further in the future, but in the meantime we think you should be able to work around it by defining an explicit element type for var.predefined_rules. For example:
variable "predefined_rules" {
type = list(object({
name = string
source_address_prefix = list(string)
destination_address_prefix = list(string)
}))
}
That would then lead to a slightly different resource configuration:
resource "azurerm_network_security_rule" "predefined_rules" {
count = length(var.predefined_rules)
name = var.predefined_rules[count.index].name
source_address_prefix = join(",", var.predefined_rules[count.index].source_address_prefix != null ? var.predefined_rules[count.index].source_address_prefix : var.source_address_prefix)
destination_address_prefix = join(",", var.predefined_rules[count.index].destination_address_prefix != null ? var.predefined_rules[count.index].destination_address_prefix : var.destination_address_prefix)
resource_group_name = azurerm_resource_group.nsg.name
network_security_group_name = azurerm_network_security_group.nsg.name
}
or, alternatively, using for_each to identify each rule by its name and simplify the expressions further:
resource "azurerm_network_security_rule" "predefined_rules" {
for_each = { for r in var.predefined_rules : r.name => r }
name = each.key
source_address_prefix = join(",", each.value.source_address_prefix != null ? each.value.source_address_prefix : var.source_address_prefix)
destination_address_prefix = join(",", each.value.destination_address_prefix != null ? each.value.destination_address_prefix : var.destination_address_prefix)
resource_group_name = azurerm_resource_group.nsg.name
network_security_group_name = azurerm_network_security_group.nsg.name
}
Hi @teamterraform ,Thanks for your rapid reply. But there exist an extra problem, the source_address_prefix and destination_address_prefix is obligatory instead of optional. And user cases failed when:
Error: Invalid value for module argument
TestTerraformNSG 2019-11-14T03:15:46Z command.go:100:
TestTerraformNSG 2019-11-14T03:15:46Z command.go:100: on ../../modules/HTTP/main.tf line 7, in module "nsg":
TestTerraformNSG 2019-11-14T03:15:46Z command.go:100: 7: predefined_rules = [
TestTerraformNSG 2019-11-14T03:15:46Z command.go:100: 8: {
TestTerraformNSG 2019-11-14T03:15:46Z command.go:100: 9: name = "HTTP"
TestTerraformNSG 2019-11-14T03:15:46Z command.go:100: 10: },
TestTerraformNSG 2019-11-14T03:15:46Z command.go:100: 11: ]
My workaround
variable "mat" {
type = map(any)
default = {"a":1234}
}
output "aaa" {
value = lookup(var.mat, "b", null) == null ? "hot dog" : "not a hot dog"
}
I've just found another workaround for this that works well for my case (aws alb listener rule actions) - I switched my var type from list(any) to string and have the consumer pass the value through jsonencode, which I then jsondecode in the module (e.g. for_each = jsondecode(var.any_list)) and use try(setting.value.prop, null).
Most helpful comment
I've just found another workaround for this that works well for my case (aws alb listener rule actions) - I switched my var type from
list(any)tostringand have the consumer pass the value throughjsonencode, which I thenjsondecodein the module (e.g.for_each = jsondecode(var.any_list)) and usetry(setting.value.prop, null).