Reputation: 2860
I have a Terraform module like below
main.tf
resource "azurerm_resource_group" "test1"{
count = "${length(var.azurerm_resource_group_name)}"
name = "${element(var.azurerm_resource_group_name, count.index)}"
location = "${element(var.azurerm_resource_group_location, count.index)}"
}
I have defined output.tf as like below
output "resource_gp" {
value = "${azurerm_resource_group.test1[count.index]}"
}
For the same module, variables.tf is below:
variable "azurerm_resource_group_name" {
type = "list"
default = ["SimpleMoon-Terra"]
}
variable "azurerm_resource_group_location" {
type = "list"
default = ["Central US"]
}
Calling the module as like below:
variable "azurerm_resource_group" {
default={
name = "simpletestrg"
location = "Central US"
}
}
# Create Resource Group
module "Test_Resource_Groups" {
source ="../../Modules/Test_Resource_Groups"
#name ="${var.azurerm_resource_group.name}"
#location ="${var.azurerm_resource_group.location}"
}
However, I'm getting error like below:
Error: Missing resource instance key
on ../../Modules/Test_Resource_Groups/output.tf line 2, in output "resource_gp":
2: value = "${azurerm_resource_group.test1.name[count.index]}"
Because azurerm_resource_group.test1 has "count" set, its attributes must be
accessed on specific instances.
For example, to correlate with indices of a referring resource, use:
azurerm_resource_group.test1[count.index]
I'm unable to understand what the actual error is and how to have it rectified.
Upvotes: 0
Views: 4150
Reputation: 74614
The source code snippet shown in your error message disagrees with the source code you shared for outputs.tf
:
value = "${azurerm_resource_group.test1.name[count.index]}" # in the error message
value = "${azurerm_resource_group.test1[count.index]}" # in the given source code
There are a few problems here, one of which is only in the source code shown in the error message:
azurerm_resource_group.test1
has count
set, it appears in expressions as a list of objects. Therefore the first operation applied to it must be an index operation, giving an expression like azurerm_resource_group.test1[count.index].name
: take the name
attribute of the count.index
element of the azurerm_resource_group.test1
list.count.index
inside an output value expression, because there is no count
argument in scope there to know what count.index
value to use. If you goal was to return a list of all of the names of all of the resource groups, you can write that as azurerm_resource_group.test1[*].name
, which is a splat expression.In this particular case the value of output "resource_gp"
will always be the same as the value of var.azurerm_resource_group_name
, because the names are populated from there. However, such a reference via the resource is useful because it tells Terraform that anything that refers to that output value must in turn depend on azurerm_resource_group.test1
, and thus helps ensure that all of the operations will be applied in an appropriate order.
There is a different way to write what you wrote here that will achieve a similar result on initial creation but will also work better for subsequent changes to var.azurerm_resource_group_name
.
First, we will define the variable as being a map of objects so that we can use only a single variable to specify the resource groups.
variable "azurerm_resource_groups" {
type = map(object({
location = string
}))
default = {
SimpleMoon-Terra = {
location = "Central US"
}
}
}
We can then use for_each
instead of count
when defining the resource group:
resource "azurerm_resource_group" "test1"{
for_each = var.azurerm_resource_groups
name = each.key
location = each.value.location
}
As well as making the expressions simpler, this has another important effect: Terraform will identify the resource groups with addresses like azurerm_resource_group.test1["SimpleMoon-Terra"]
rather than azurerm_resource_group.test1[0]
, and so when you add and remove items from var.azurerm_resource_groups
Terraform can keep track of which remote resource group object belongs to which element of the map.
for_each
also causes azurerm_resource_group.test1
to appear in expressions as a map rather than as a list, so we need to change the output value
to work with that. Because the map keys are the resource group names, we can use the keys
function to obtain them. We'll also use toset
to convert this to a set, reflecting that these are not in any particular order and so users of this value should not rely on the ordering:
output "resource_group_names" {
value = toset(keys(azurerm_resource_group.test1))
}
Your calling module can then call this module as in the following example:
module "test_resource_groups" {
source = "../../Modules/Test_Resource_Groups"
azurerm_resource_groups = {
simpletestrg = {
location = "Central US"
}
}
}
In that calling module, references to module.test_resource_groups.resource_group_names
will produce the set of resource group names, which can then be used with for_each
elsewhere in the configuration to produce one object per resource group.
Upvotes: 2