'Terraform: Reference locals values inside the creation block

Is it possible to reference another locals value inside the creation of a locals value?

The example below was the smallest and simplest example I could come up with.

variable "size" {
  default     = 3
}

variable "infrastructure_version" {
  default = 1
}

locals {
  values = {
    for n in range(var.size) : n => {
      name = "instance_${n + 1}"
      full_name = "test_${name}_v${var.infrastructure_version}"
    }
  }
}

When trying to access name within the for loop inside the locals block i get the following error:

│ Error: Invalid reference
│ 
│   on instances.tf line 13, in locals:
│   13:       full_name = "test_${name}_v${var.infrastructure_version}"
│ 
│ A reference to a resource type must be followed by at least one attribute access, specifying the resource name.

Other attempts: (These were desperate attempts with no real likelihood of succeeding)

  • local.values[n].name which gives Error: Self-referencing local value
  • n.name which gives Error: Unsupported attribute
  • self.name which gives Error: Invalid "self" reference

Anyone know if this is possible? Or am I stuck repeating the creation of name inside full_name as well?

full_name = "test_instance_${n + 1}_v${var.infrastructure_version}"



Solution 1:[1]

Instead of doing variable interpolation every time a value should be used, it's possible to create a local module that acts as a function. Where you can use local variables and reuse previously created variables.

Short example below, it's better suited when used for more complex and larger applications due to the amount of overhead.

main.tf: From the main module I import the local module that will serve as a function

module "instance_conf" {
  source = "./modules"
  count  = var.size

  index = count.index
  infra = var.infrastructure_version
}

locals {
  values = {for idx, val in module.instance_conf: idx => val}  
}

I send in index and infra to the module as input. The variable definitions in the other module must be matching those, here you could also provide descriptions if needed.

modules/func.tf:

variable "index" {
  type = number
}

variable "infra" {
  type = number
}

locals {
  name = "instance_${var.index + 1}"
}

output "name" {
  value = local.name
}
output "full_name" {
  value = "test_${local.name}_v${var.infra}"
}

To get the desired output to the main module, calculate values either in the locals block or directly in the output block. When importing this module the values will be available as a list of maps, for each count.index in the main module. count = var.size

The list could look like this:

[
  {
    name: "instance_1",
    full_name: "test_instance_1_v1"
  },
  {
    name: "instance_2",
    full_name: "test_instance_2_v1"
  },
  ...
]

So in order to use the module output as previous with for_each I converted the list of map objects, to a map with the index of each map object as the key for that object.

locals {
  values = {for idx, val in module.instance_conf: idx => val}  
}

And now when using local.values it will look like this:

{
  "1": {
    name: "instance_1",
    full_name: "test_instance_1_v1"
  },
  "2": {
    name: "instance_2",
    full_name: "test_instance_2_v1"
  },
  ...
}

The project structure now looks like this:

.
??? main.tf
??? modules
?   ??? values_function.tf

Hopefully this helps someone else out. When variable interpolation and recreation of a value every time it is used, is not an acceptable answer. Mainly because of the maintainability factor.

Solution 2:[2]

Your last attempt is correct. You can't make it different and it works:

 full_name = "test_instance_${n + 1}_v${var.infrastructure_version}"

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2