Cool, thanks!
So the error message seems to be talking about var.systems["host01"].interfaces["stable"]
; the fact that it’s saying element "stable"
rather than attribute "stable"
suggests that Terraform is understanding this as a map key rather than as an attribute.
I think again there’s a missing level in your data structure: interfaces
is defined as being a map of objects, but you’ve assigned only a single object to it, and so Terraform is trying its best to understand all of the attributes of that object as being elements of the interfaces map.
This seems similar to my last answer: either the type constraint needs to specify a single object instead of a map of objects, or the value you pass needs to include a map of objects. I feel like we’re not connecting here so I must be misunderstanding something about what’s going on; is your intent for each host to have only one interface, or should each host have zero or more interfaces where each one has a unique key?
One theory I have is that Terraform’s ability to automatically convert an object into a map is making it hard for you to see what is a map and what is an object in your value, since they’re all using the object syntax. Perhaps it would make things clearer to explicitly convert to maps in all of the places where the type constraint calls for maps, so it’s easier to see in the expression which of the objects are being passed directly as objects and which ones are intended to convert as maps:
module "the_hosts" {
source = "./hosts"
systems = tomap({
host01 = {
interfaces = tomap({
leafs = [
{
device = "en0"
ip = "10.X.X.X"
netmask = "/24"
gateway = "10.X.X.X"
},
{ device = "en1"
ip = "10.X.X.X"
netmask = "/24"
gateway = "10.X.X.X"
}
],
lom = { ip = "X.X.X.X" },
management = {
device = "en3"
ip = "X.X.X.X"
netmask = "/24"
gateway = "X.X.X.X"
mac = "aa:bb:cc:dd:ee:ff"
},
stable = {
device = "lo"
ip = "X.X.X.X"
}
}),
disks = tolist(["sda", "sdb"])
},
host02 = {
interfaces = tomap({
leafs = [
{
device = "en0"
ip = "10.X.X.X"
netmask = "/24"
gateway = "10.X.X.X"
},
{ device = "en1"
ip = "10.X.X.X"
netmask = "/24"
gateway = "10.X.X.X"
}
],
lom = { ip = "X.X.X.X" },
management = {
device = "en3"
ip = "X.X.X.X"
netmask = "/24"
gateway = "X.X.X.X"
mac = "aa:bb:cc:dd:ee:00"
},
stable = {
device = "lo"
ip = "X.X.X.X"
}
}),
disks = tolist(["sda", "sdb"])
}
})
}
In the above I’ve added explicit tomap(...)
and tolist(...)
conversions in all of the places where Terraform would’ve previously attempted automatic conversions in order to meet the type constraint. This gives Terraform a little extra information about what types I was intending to produce, which allows it to produce a different error message:
╷
│ Error: Invalid function argument
│
│ on unsuitable-value.tf line 5, in module "the_hosts":
│ 5: interfaces = tomap({
│ 6: leafs = [
│ 7: {
│ 8: device = "en0"
│ 9: ip = "10.X.X.X"
│ 10: netmask = "/24"
│ 11: gateway = "10.X.X.X"
│ 12: },
│ 13: { device = "en1"
│ 14: ip = "10.X.X.X"
│ 15: netmask = "/24"
│ 16: gateway = "10.X.X.X"
│ 17: }
│ 18: ],
│ 19: lom = { ip = "X.X.X.X" },
│ 20: management = {
│ 21: device = "en3"
│ 22: ip = "X.X.X.X"
│ 23: netmask = "/24"
│ 24: gateway = "X.X.X.X"
│ 25: mac = "aa:bb:cc:dd:ee:ff"
│ 26: },
│ 27: stable = {
│ 28: device = "lo"
│ 29: ip = "X.X.X.X"
│ 30: }
│ 31: }),
│ ├────────────────
│ │ while calling tomap(v)
│
│ Invalid value for "v" parameter: cannot convert object to map of any single
│ type.
╵
╷
│ Error: Invalid function argument
│
│ on unsuitable-value.tf line 35, in module "the_hosts":
│ 35: interfaces = tomap({
│ 36: leafs = [
│ 37: {
│ 38: device = "en0"
│ 39: ip = "10.X.X.X"
│ 40: netmask = "/24"
│ 41: gateway = "10.X.X.X"
│ 42: },
│ 43: { device = "en1"
│ 44: ip = "10.X.X.X"
│ 45: netmask = "/24"
│ 46: gateway = "10.X.X.X"
│ 47: }
│ 48: ],
│ 49: lom = { ip = "X.X.X.X" },
│ 50: management = {
│ 51: device = "en3"
│ 52: ip = "X.X.X.X"
│ 53: netmask = "/24"
│ 54: gateway = "X.X.X.X"
│ 55: mac = "aa:bb:cc:dd:ee:00"
│ 56: },
│ 57: stable = {
│ 58: device = "lo"
│ 59: ip = "X.X.X.X"
│ 60: }
│ 61: }),
│ ├────────────────
│ │ while calling tomap(v)
│
│ Invalid value for "v" parameter: cannot convert object to map of any single
│ type.
╵
Because I gave Terraform precise type information instead of relying on automatic type conversions, Terraform was able to be more specific about what’s wrong here: it identified this tomap
call in particular as being incorrect, because the argument I passed it is an interface object rather than a map of interface objects. The text of this error message is admittedly far less specific than the one we had before, but it has the benefit of drawing attention to the specific part of the data structure that was wrong, and Terraform announcing what it was trying to do when it failed.
Making that actually be a map of objects made it validate:
module "the_hosts" {
source = "./child"
systems = tomap({
host01 = {
interfaces = tomap({
a = {
leafs = [
{
device = "en0"
ip = "10.X.X.X"
netmask = "/24"
gateway = "10.X.X.X"
},
{ device = "en1"
ip = "10.X.X.X"
netmask = "/24"
gateway = "10.X.X.X"
}
],
lom = { ip = "X.X.X.X" },
management = {
device = "en3"
ip = "X.X.X.X"
netmask = "/24"
gateway = "X.X.X.X"
mac = "aa:bb:cc:dd:ee:ff"
},
stable = {
device = "lo"
ip = "X.X.X.X"
}
},
}),
disks = tolist(["sda", "sdb"])
},
host02 = {
interfaces = tomap({
a = {
leafs = [
{
device = "en0"
ip = "10.X.X.X"
netmask = "/24"
gateway = "10.X.X.X"
},
{ device = "en1"
ip = "10.X.X.X"
netmask = "/24"
gateway = "10.X.X.X"
}
],
lom = { ip = "X.X.X.X" },
management = {
device = "en3"
ip = "X.X.X.X"
netmask = "/24"
gateway = "X.X.X.X"
mac = "aa:bb:cc:dd:ee:00"
},
stable = {
device = "lo"
ip = "X.X.X.X"
}
},
}),
disks = tolist(["sda", "sdb"])
}
})
}
The interface for each host is in a map element whose key is a
, again because I don’t really know enough about what you’re modelling to choose a more realistic key.