learner
learner

Reputation: 2860

Azure terraform reports Missing resource instance key

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

Answers (1)

Martin Atkins
Martin Atkins

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:

  • Because 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.
  • You can't use 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

Related Questions