Hi @jammyshaw,
You should be able to get a result like you’re asking about using resource for_each
, which allows us to systematically declare multiple instances of a resource using a single resource
block.
When using for_each
like this, we need to give Terraform a unique string key with which to track each of the instances between runs. Since your three subnet IDs are hard-coded in your example, you could in principle use those IDs directly as those keys, but it’s more common for IDs like those to be dynamically chosen and thus not suitable for for_each
, and so I’m not going to show an example of that just because I don’t want to show you a seeminly-straightforward example that would work in this specific situation but would not generalize to more common situations.
Instead, I’d suggest to change the local value to be a map where the keys are fixed strings that won’t change if you were to recreate the subnets in future, and the values are the subnet ids. For the sake of this example, I’m going to assume AWS availability zone names are a suitable unchanging unique key, though that would not be true if you were to create more than one subnet in the same AZ; in that case, you can choose other keys that make sense for whatever you’re trying to model.
locals {
subnet_ids = tomap({
"us-east-1a" = "subnet-0ad390442883258a9"
"us-east-1b" = "subnet-0b4ef4bbad2c5d7e8"
"us-east-1c" = "subnet-0519c71d2872bf471"
})
}
resource "aws_instance" "example" {
for_each = local.subnet_ids
instance_type = "m5.large"
subnet_id = each.value
# ...
}
The above configuration declares that there should be one instance of aws_instance.example
for each element of local.subnet_ids
, with each one assigned an address based on the keys in that map:
aws_instance.example["us-east-1a"]
aws_instance.example["us-east-1b"]
aws_instance.example["us-east-1c"]
Because I assigned each.value
to subnet_id
in the configuration, each of these instances will have its own different value of that argument, which will be the value from the map declared above.
Because Terraform will be tracking these by the map keys, you can later add new entries to the map in order to (indirectly) declare new instances, or reassign the subnet ID for an existing key to (indirectly) tell Terraform to replace that particular instance.
Incidentally, while it is valid and reasonable to declare multiple AWS instances directly like this, for this particular situation where it seems like your goal is to spread workload across multiple AZs it might be worth considering using aws_autoscaling_group
instead, which makes EC2 itself responsible for managing the multiple instances and thus allows the remote system to respond dynamically to problems such as instance failures and AZ-level outages.
If you were to use an autoscaling group instead of multiple directly-managed EC2 instances here, you’d specify the full set of subnets all together on a single autoscaling group object, similar to what you originally attempted with aws_instance
, and then from Terraform’s perspective the “group” behaves as a single object whereas the individual instances are created dynamically by EC2 autoscaling.
This also allows you to separate the number of instances you need from the number of availability zones to spread them over: autoscaling will either choose a subset of the subnet IDs you specified (if the desired count is smaller than the subnet count) or create more than one instance in the same subnet (if the desired count is greater than the subnet count), and thus you can potentially scale up or down your number of instances without also creating or destroying subnets.
To do that with the local value I used in the above example, you could use values
to take just the subnet ids again, or you could revert to your original idea of just making a single set/list of ids and passing that directly. In both cases, the subnet IDs need to be assigned to the (rather oddly-named) vpc_zone_identifier
argument:
vpc_zone_identifier = values(local.subnet_ids)