Kamlendra Sharma
Kamlendra Sharma

Reputation: 717

Terraform Creating map dynamically using local variable instead of input variable

Requirements: I have a bunch of EC2s. Which I am grouping according to the Tags. in this example total number of group =4 and each group has 7 EC2 : 1 parent-6 Child. here i am sharing code of child whose naming matters.

working code:Here I am sharing the child EC2 code which is working perfectly fine with the input variables of map type named :ws_to_Child_Node_name_map_count. Now i want it to be scalable(number of parent-child) for which I am looking to use 'dynamically created map in locals' instead of using input variable. main.tf

resource "aws_instance" "ec2_instance_child" {
  count                   = var.ec2_instance_child_count
  tags = {
    NodeName       = "${lookup(var.ws_to_Child_Node_name_map_count, count.index+1, 99)}"
  }
}

variable.tf

variable "ws_to_Child_Node_name_map_count"  {
  type = map
  default = {
      "1"="1"
      "2"="2"
      "3"="3"
      "4"="4"
      "5"="5"
      "6"="6"
      "7"="1"
      "8"="2"
      "9"="3"
      "10"="4"
      "11"="5"
      "12"="6"
      "13"="1"
      "14"="2"
      "15"="3"
      "16"="4"
      "17"="5"
      "18"="6"
      "19"="1"
      "20"="2"
      "21"="3"
      "22"="4"
      "23"="5"
      "24"="6"
    }
  }
variable "ec2_instance_child_count" {
  description = "Number of instances to run"
  default     = "24"   #number of group *6
}

the map shown above I want to create dynamically using two variables, which in future i will not be constant.

variable "child_count" {
  default     = 6
}

variable "group_count" {
  default     =  4
}

Upvotes: 1

Views: 24177

Answers (2)

Martin Atkins
Martin Atkins

Reputation: 74064

The mapping table you wrote here seems to be describing a variant of the modulo operation that starts counting at one rather than zero.

If that's your intent, you could potentially calculate this dynamically using an expression rather than producing a separate mapping table.

variable "child_count" {
  default     = 6
}

variable "group_count" {
  default     =  4
}

resource "aws_instance" "example" {
  count = var.child_count * var.group_count

  tags = {
    # The % symbol is Terraform's modulo operator
    NodeName = ((count.index - 1) % var.child_count) + 1
  }
}

The - 1 and + 1 in the expression above are allowing for the fact that you are using one-based counting rather than zero-based counting. For zero-based, this would reduce to count.index % var.child_count.


If you still want to make the mapping table in a local value for some reason, you can perform the above calculation inside a for expression instead:

locals {
  lookup_table = {
    for n in range(1, (var.child_count * var.group_count) + 1) :
    n => ((n - 1) % var.child_count) + 1
  }
}

This uses the range function to count from 1 up to your total count, and then produces a map with one element per element of that result where the value is the result of the same modulo calculation I showed in the resource block above.

From Terraform 0.12 onwards it is never necessary to use null_resource or null_data_source as a hack to transform lists, because the for expression syntax can now meet the same use-cases.

Upvotes: 5

Montassar Bouajina
Montassar Bouajina

Reputation: 1642

i can help you create a dynamic list of maps with terraform using a hack here's an example :

locals {
  childs = 24
  group  = [1,2,3,4,5,6]
}

# Here's the hack! The null_resource has a map called triggers that we can set to arbitrary values.
# We can also use count to create a list of null_resources. By accessing the triggers map inside of
# that list, we get our list of maps! See the output variable below.
resource "null_resource" "res" {
  count = local.childs+1

  triggers = {
    parent    = "${count.index}"
    child     = "${element(local.group, count.index)}"
  }
}

# And here's the result! We have a dynamic list of maps. I'm just outputting it here

output "map" {
  value = "${null_resource.res.*.triggers}"
}

you can try it create a main.tf and run terraform init terraform apply

the result should be like this :

map = [
  {
    "child" = "1"
    "parent" = "0"
  },
  {
    "child" = "2"
    "parent" = "1"
  },
  {
    "child" = "3"
    "parent" = "2"
  },
  {
    "child" = "4"
    "parent" = "3"
  },
  {
    "child" = "5"
    "parent" = "4"
  },
  {
    "child" = "6"
    "parent" = "5"
  },
  {
    "child" = "1"
    "parent" = "6"
  },
  {
    "child" = "2"
    "parent" = "7"
  },
  {
    "child" = "3"
    "parent" = "8"
  },
  {
    "child" = "4"
    "parent" = "9"
  },
  {
    "child" = "5"
    "parent" = "10"
  },
  {
    "child" = "6"
    "parent" = "11"
  },
  {
    "child" = "1"
    "parent" = "12"
  },
  {
    "child" = "2"
    "parent" = "13"
  },
  {
    "child" = "3"
    "parent" = "14"
  },
  {
    "child" = "4"
    "parent" = "15"
  },
  {
    "child" = "5"
    "parent" = "16"
  },
  {
    "child" = "6"
    "parent" = "17"
  },
  {
    "child" = "1"
    "parent" = "18"
  },
  {
    "child" = "2"
    "parent" = "19"
  },
  {
    "child" = "3"
    "parent" = "20"
  },
  {
    "child" = "4"
    "parent" = "21"
  },
  {
    "child" = "5"
    "parent" = "22"
  },
  {
    "child" = "6"
    "parent" = "23"
  },
  {
    "child" = "1"
    "parent" = "24"
  },
]

If you want to check every parent and every child (you can use locals) you can create a 2 loops like this :

   locals {
      childs = 24
      group = 6
    
      result = {
      for j in range(1, local.childs + 1) : j => [
      for i in range(1, local.group + 1) : {
        child = i,
        parent = j
      }
      ]
    
      }
    }

your output will be grouped by parents like this :

  "1" = [
    {
      "child" = 1
      "parent" = 1
    },
    {
      "child" = 2
      "parent" = 1
    },
    {
      "child" = 3
      "parent" = 1
    },
    {
      "child" = 4
      "parent" = 1
    },
    {
      "child" = 5
      "parent" = 1
    },
    {
      "child" = 6
      "parent" = 1
    },
  ]
  "2" = [
    {
      "child" = 1
      "parent" = 2
    },
    {
      "child" = 2
      "parent" = 2
    },
    {
      "child" = 3
      "parent" = 2
    },
    {
      "child" = 4
      "parent" = 2
    },
    {
      "child" = 5
      "parent" = 2
    },
    {
      "child" = 6
      "parent" = 2
    },
  ]

Upvotes: 2

Related Questions