Enumerate the Variables in Terraform.
Tags: terraform, variable, foreach, loop
Published: 2023-07-22 15:10:45

ความเดิมตอนที่แล้ว เราสามารถสร้างชุด VM ที่ซับซ้อนพร้อมกันหลายๆ ชุด โดยระบุแค่ค่าตั้งต้นเพียงเล็กน้อย
[ทำไมใช้ for_each แทน count ใน terraform](https://dev.to/terngr/thamaimaich-foreach-aethn-count-ain-terraform-1k0n)

ในตอนนี้เราจะ Enumerate ค่าใน list สั้นๆตั้งต้น ออกมาเป็นชุดที่แสดงข้อมูล VM แต่ละเครื่องแบบละเอียด เพื่อนำไปใช้ต่อกันครับ

ตอนนี้อาจจะซับซ้อนหน่อย แต่แสดงให้เห็นว่าเราสามารถเล่นแร่แปรธาตุและทำอะไรๆ กับ Variable บน Terraform ได้มากระดับหนึ่งทีเดียว ซึ่งหากต้องการ Customize code แบบ Programming มากขึ้นก็สามารถไปท่า CDK, SDK integration ได้ด้วย


เราทราบแล้วว่าการสร้าง Resources ด้วย for_each จะรับค่าจาก map หรือ set of strings เท่านั้น

สารตั้งต้น ทำให้สั้นเพื่อง่ายต่อการใช้งาน
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xvrtve6kaa9ix1tudt29.png)


ปลายทางที่ต้องการ จัดให้อยู่ในรูปแบบ Map โดยแจกแจง Properties ของแต่ละ VM เพื่อใช้ร่วมกับ for_each
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lm7c4a236bhgvjv767gp.png)

## Step 0:

สารตั้งต้น ต้องการสร้างเครื่องจำนวน 2 Group ชื่อว่า GroupA และ GroupB
โดย GroupA ให้สร้าง 2 เครื่อง ให้แต่ละเครื่องมี CPU=2 และ memory=2048
ส่วน GroupB ให้สร้าง 1 เครื่อง ให้แต่ละเครื่องมี CPU=1 และ memory=1024

```
vm = tolist([                                                                                                                                                                                                      
  {                                                                                                                                                                                                                  
    "group" = "GroupA"                                                                                                                                                                                               
    "count" = 2                                                                                                                                                                                                      
    "cpu" = 2                                                                                                                                                                                                        
    "memory" = 2048                                                                                                                                                                                                
  },                                                                                                                                                                                                               
  {                                                                                                                                                                                                                  
    "group" = "GroupB"                                                                                                                                                                                               
    "count" = 1                                                                                                                                                                                                      
    "cpu" = 1                                                                                                                                                                                                        
    "memory" = 1024                                                                                                                                                                                                
  },                                                                                                                                                                                                               
  {                                                                                                                                                                                                                  
    "group" = "GroupC"                                                                                                                                                                                               
    "count" = 0                                                                                                                                                                                                      
    "cpu" = 2                                                                                                                                                                                                        
    "memory" = 2048                                                                                                                                                                                                
  },                                                                                                                                                                                                             ])                    
```

## Step 1:

ใช้ for loop 2 ชั้น
โดย loop ชั้นแรก จะกวาดตามจำนวน Element ใน var.vm ซึ่งในที่นี้ก็คือจะกวาดตามจำนวน Group นั่นเอง ซึ่งในที่มี 3 Group คือ "GroupA", "GroupB", "GroupC"


loop ชั้นที่สอง จะกวาดตามจำนวนตัวแปร count ที่อยู่ใต้ element แต่ละตัวใน loop ชั้นแรก ซึ่งอ้างถึงตัวแปร count ได้จากตัวแปร i_name.count
ฉะนั้นในชั้นนี้ จะวิ่งตามจำนวนเครื่องที่ต้องการในแต่ละ Group นั่นเอง โดย
GroupA มีค่า Count=2 ก็จะมีการทำซ้ำ(loop) 2 รอบ,
GroupB มีค่า Count=1 ก็จะมีการทำซ้ำ(loop) 1 รอบ,
GroupA มีค่า Count=0 ก็จะมีการทำซ้ำ(loop) 0 รอบ

เมื่อรัน loop ชั้นแรกและชั้นที่สองเสร็จ จะได้การทำซ้ำ(loop) ทั้งหมด 3 รอบ เท่ากับจำนวนเครื่องที่ต้องการพอดี

หากปรับตัวเลข เป็นจำนวนที่ซับซ้อนขึ้น ก็จะได้การทำซ้ำที่มากขึ้นตาม เช่น GroupA Count=7, GroupB count=20, GroupC count=5 ก็จะได้จำนวนเครื่องทั้งหมด 32 เครื่องนั่นเอง

การทำ loop 2 ชั้น ตามจำนวน Group และตามค่าของ count ในแต่ละ Group

```
    for i_index, i_name in var.vm : [
      for j_index in range(0, i_name.count) : {
```



## Step 2:
สิ่งที่ทำในแต่ละรอบ การสร้างตัวแปรค่าใหม่ขึ้นมาจากค่าตัวแปรเดิม
จาก Step 1 เราได้สร้าง Loop 2 ชั้น เพื่อให้ทำซ้ำจำนวนรอบเท่ากับจำนวนเครื่องที่ต้องการได้แล้ว

ขั้นนี้ เราจะต้องทำสิ่งที่ต้องทำในแต่ละรอบ นั่นก็คือการกำหนดค่าให้กับ VM ในแต่ละรอบนั่นเอง

ตัวอย่างเช่น Loopชั้นแรกของ GroupA, Loopชั้นที่สอง ของ count=0 จะสร้าง VM สำหรับ GroupA โดยมี Index ของ GroupA เป็น count+1=1, และกำหนดค่า CPU, Memory ตาม GroupA

จากนั้น จะเป็น Loopชั้นแรกของ GroupA, แต่ Loop ชั้นที่สอง ของ count=1

ส่วนใน Loop ของ Group B ก็ทำเช่นเดียวกัน โดย Loop ชั้นแรกคือ GroupB, และ Loop ชั้นที่สองจะทำตามจำนวนของ count ซึ่งครั้งแรกคือ count=0 ก็จะสร้าง VM สำหรับ GroupB โดยมี Index ของ GroupB เป็น count+1=1, และกำหนดค่า CPU, Memory ตาม GroupB

การกำหนดค่าให้กับ loop แต่ละรอบ ตามค่า Group และค่า count

```
        name  = "${i_name.group}-${j_index+1}"
        group = i_name.group
        cpu   = i_name.cpu
        memory= i_name.memory
        index = j_index+1
```

## Step 3:
ประกอบ Step 1 และ 2 เข้าด้วยกัน
การประกอบ มีสิ่งที่ต้องทราบก่อนคือ
การใช้ (), [], {}
() ใช้ต่อท้าย Function เช่น flatten()
[] ใช้กับ list
{} ใช้กับ map

จากตัวแปรตั้งต้น เป็น list ของแต่ละ Group เพื่อให้เพิ่ม list ได้จำนวนมาก หรือเพิ่ม Group ได้จำนวนมากนั่นเอง ขั้นนี้ใช้ [], และเมื่อเข้าไปในแต่ละ Group จะเป็น map เพราะมี key: value ที่แน่นอน ก็คือ group, count, CPU, Memory ขั้นนี้ใช้ {}

ทั้งหมดที่ได้มา เนื่องจากเป็น map ซ้อนอยู่ใต้ชั้นของ list อีกที จึงนำมา Flatten ด้วย Function flatten ซึ่งใช้ () นั่นเอง

ชั้นนอกสุดจะเป็น function distinct() ทำการเช็คว่ามีค่าซ้ำไหม หากมีค่าใน list 2 ตัวที่ซ้ำกันก็จะนำออก
นำข้างต้นมารวมกัน จะได้เป็นการใช้งาน loop 2 ชั้น กับ list of map

```
  vm_list = distinct(flatten([
    for i_index, i_name in var.vm : [
      for j_index in range(0, i_name.count) : {
        name  = "${i_name.group}-${j_index+1}"
        group = i_name.group
        cpu   = i_name.cpu
        memory= i_name.memory
        index = j_index+1
      }
    ]
  ]))
```

สำหรับในบรรทัด    for i_index, i_name in var.vm : [
โดยปกติจะใช้ i_name เพื่อแทน element แต่ละตัวใน var.vm ซึ่งในที่นี้ก็คือแต่ละ Group นั่นเอง
แต่ครั้งนี้มีการใส่ i_index เพิ่มเข้ามาด้วย โดยจะแทน increment หรือการนับลำดับนั่นเอง ซึ่งสามารถนำ i_index ไปอ้างอิงใช้งานต่อได้หากต้องการใส่ลำดับแบบ Global เข้าไปเพิ่มเติม นอกเหนือจากการใช้ j_index ซึ่งเป็นลำดับเฉพาะแต่ละใน Group เท่านั้น

ในครั้งนี้เราจะไม่ได้ใส่ค่า i_index เข้าไป เพราะจะมีการใส่ลำดับทั้งหมด(global index) ในขั้นตอนการทำ map ขั้นสุดท้ายเพียงครั้งเดียวนั่นเอง

ทำการรัน จะได้

```
vm_list = [
{
    "cpu" = 2
    "group" = "GroupA"
    "index" = 1
    "memory" = 2048
    "name" = "GroupA-1"
  },
  {
    "cpu" = 2
    "group" = "GroupA"
    "index" = 2
    "memory" = 2048
    "name" = "GropuA-2"
  },
  {
    "cpu" = 1
    "group" = "GroupB"
    "index" = 1
    "memory" = 1024
    "name" = "GroupB-1"
  },
]
```

สรุปในตอนนี้ เราได้ทำการ Enumerate Variable จากชื่อ Group และ จำนวน มาใส่ในตัวแปร list พร้อมทั้งระบุชื่อ Group, ลำดับ, Property ที่ต้องการซึ่งในที่นี้คือ CPU และ Memory

ตอนถัดไปจะทำการ construct map variable ขึ้นมาใช้งานครับ

[1: ทำไมใช้ for_each แทน count ใน terraform](https://bit.ly/tf_for_each)
[2: Enumerate the Variables in Terraform.](https://bit.ly/tf_var1)
[3: Transform list to map in Terraform](https://bit.ly/tf_transform)