Reputation: 19434
I have the following variable
variable "instance_types" {
default = {
instances : [
{
count = 1
name = "control-plane"
ami = "ami-xxxxx"
instance_type = "t2.large"
iam_instance_profile = "xxx-user"
subnet_id = "subnet-xxxxx"
},
{
count = 3
name = "worker"
ami = "ami-xxxxx"
instance_type = "t2.large"
iam_instance_profile = "xxx-user"
subnet_id = "subnet-xxxxx"
}
]
}
}
With the following instance declaration (that I'm attempting to iterate)
resource "aws_instance" "k8s-node" {
# Problem here : How to turn an array of 2 objects into 4 (1 control_plane, 3 workers)
for_each = {for x in var.instance_types.instances: x.count => x}
ami = lookup(each.value, "ami")
instance_type = lookup(each.value, "instance_type")
iam_instance_profile = lookup(each.value, "iam_instance_profile")
subnet_id = lookup(each.value, "subnet_id")
tags = {
Name = lookup(each.value, "name")
Type = each.key
}
}
Goal: Get the aws_instance
to iterate 4 times (1 control_plane + 3 workers) and populate the values the index of instance_types
.
Problem : Cannot iterate the over the object array correctly with desired result. In a typical programming language this would be achieved in a double for loop.
Upvotes: 1
Views: 1679
Reputation: 28739
This can be solved easier with a data type of map(object))
for your input variable. The transformed data structure appears like:
variable "instance_types" {
...
default = {
"control-plane" = {
count = 1
ami = "ami-xxxxx"
instance_type = "t2.large"
iam_instance_profile = "xxx-user"
subnet_id = "subnet-xxxxx"
},
"worker" = {
count = 3
ami = "ami-xxxxx"
instance_type = "t2.large"
iam_instance_profile = "xxx-user"
subnet_id = "subnet-xxxxx"
}
}
}
Note the name
key in the object
is subsumed into the map
key for efficiency and cleanliness.
If the resources are split between the control plane and worker nodes, then we are finished and can immediately leverage this variable's value in a for_each
meta-argument. However, combining the resources now requires a data transformation:
locals {
instance_types = flatten([ # need this for final structure type
for instance_key, instance in var.instance_types : [ # iterate over variable input objects
for type_count in range(1, instance.count + 1) : { # sub-iterate over objects by "count" value specified; use range function and begin at 1 for human readability
new_key = "${instance_key} ${type_count}" # for resource uniqueness
type = instance_key # for easier tag value later
ami = instance.ami # this and below retained from variable inputs
instance_type = instance.instance_type
iam_instance_profile = instance.iam_instance_profile
subnet_id = instance.subnet_id
}
]
])
}
Now we can iterate within the resource with the for_each
meta-argument, and utilize the for
expression to reconstruct the input for suitable usage within the resource.
resource "aws_instance" "k8s-node" {
# local.instance_types is a list of objects, and we need a map of objects with unique resource keys
for_each = { for instance_type in local.instance_types : instance_type.new_key => instance_type }
ami = each.value.ami
instance_type = each.value.instance_type
iam_instance_profile = each.value.iam_instance_profile
subnet_id = each.value.subnet_id
tags = {
Name = each.key
Type = each.value.type
}
}
This will give you the behavior you desire, and you can modify it for style preferences or different uses as the need arises.
Note the lookup
functions are removed since they are only useful when default values are specified as a third argument, and that is not possible in object types within variable declarations except as an experimental feature in 0.14.
The absolute namespace for these resources' exported resource attributes would be:
(module?.<declared_module_name>?.)<resource_type>.<resource_name>[<resource_key>].<attribute>
For example, given an intra-module resource, first worker node, and private ip address exported attribute:
aws_instance.k8s-node["worker 1"].private_ip
Note you can also access all resources' exported attributes by terminating the namespace at <resource_name>
(retaining the map of all resources instead of accessing a singular resource value). Then you could also use a for
expression in an output
declaration to create a custom aggregate output for all of the similar resources and their identical exported attribute(s).
{ for node_key, node in aws_instance.k8s-node : node_key => node.private_ip }
Upvotes: 2