Recursion in Terraform, with example
Tags: terraform, hashicorp, recursion, loop
Published: 2023-08-12 14:29:27

> สัปดาห์ที่แล้วได้พูดถึงการทำ Loop รวมถึงใส่เงื่อนไขในการสร้าง Resources หลายๆ แบบได้โดยใช้ count และ module มาช่วย
คำถามถัดไปคือ Terraform สามารถทำงานในลักษณะ Recursive ได้หรือไม่

## TL;DR;
ได้ทดสอบโดยให้เรียก Module ซ้อนกัน(เรียกตัวเอง) พบว่าไม่สามารถเรียกใช้งานได้ จึงสรุปได้ว่าด้วย Terraform ไม่สามารถทำงานในลักษณะแบบ Recursive จริงๆได้(แต่ถ้าเป็นการเรียกซ้อนกัน ที่ไม่ใช่เรียกตัวเอง ยังทำได้ปกตินะครับ แต่เราต้องสร้าง module รองรับไว้)

ถ้าจบแค่นี้อาจจะสั้นไปนิด งั้นเราลองมาแปลง Recursive ให้เป็น Iterative ก่อน

ขอทดสอบด้วยโจทย์ Tower of Hanoi ที่โจทย์ให้หา solution ในการเลื่อน Disk ทั้งหมดจากเสา Source(A) ไปยัง Destination(C) โดยมีเสาพัก 1 เสาคือ Temp(B), กติกาคือ เลื่อนได้ครั้งละ 1 Disk เท่านั้น และห้าม Disk ใหญ่วางทับ Disk เล็ก [ทดลองเล่นด้วยตัวเอง](https://www.mathsisfun.com/games/towerofhanoi.html)

โดยปกติ Tower of Hanoi ถูก Solved ได้ง่ายๆ โดยใช้ Recursive ในที่นี้เราจะให้ Terraform ช่วยแก้จึงต้อง

1. แปลง Algorithm จาก Recursive เป็น Iterative ก่อน 
2. จากนั้นนำ Count และ Module มาช่วยในการรัน loop แบบ Iterative

```
#main.tf สำหรับใช้ทำ loop ด้านนอก โดยใช้ count
#ค่าของ count ได้มาจากการแปลง Recursive -> Iterative แล้วดูว่าต้อง move ทั้งหมดเท่าใด

locals {
  n=3
  n_total=pow(2,local.n)-1
}

module "my_module" {
  count = local.n_total
  source = "./my_module"
  n = local.n_total
  i = count.index
  A = "A"
  B = local.n%2==0?"C":"B"
  C = local.n%2==0?"B":"C"
}

output "n_total" {
  value = local.n_total
}
output "my_module" {
  value = module.my_module
```

```
#my_module/my_module.tf ที่ใช้รันคำสั่งซึ่งเดิมจะรันใน recursive function
#ในที่นี้ก็คือให้ทำหาค่าเสาที่จะทำการ Move
#ค่าที่ส่งให้ output สามารถนำไปใช้ต่อใน main module ที่เป็นผู้เรียก my_module/my_module.tf ได้

variable "n" {}
variable "i" {}
variable "A" {}
variable "B" {}
variable "C" {}

output "move" {
  value = var.i%3==0 ? "move ${var.A} ${var.C}" : var.i%3==1?"move ${var.A} ${var.B}":"move ${var.B} ${var.C}"

}
```

ท่า Iterative นี้ จะบอกได้ว่าต้อง move ระหว่างเสาใด แต่ไม่ได้บอกว่าเสาใดคือต้นทางและปลายทาง ทั้งนี้การ Move จะทำได้เพียบแบบเดียวเท่านั้นเพื่อไม่ให้ผิดเงื่อนไข Disk ใหญ่ห้ามทับ Disk ที่เล็กกว่า

หากต้องการรายละเอียดที่เจาะจงว่าเสาใดเป็นต้นทาง จะต้องทำ stack(terraform: list) เพิ่มด้วย โดยเก็บข้อมูลของ Disk ที่อยู่ในแต่ละเสา แล้วใช้เงื่อนไขซ้อนเข้าไปเพื่อเปรียบเทียบความใหญ่ของ Disk เพื่อตัดสินใจว่าระหว่างสองเสาที่ต้อง Move เสาใดควรเป็นต้นทาง(เสาที่ Disk บนสุดเล็กกว่า, หรือเสาปลายทางว่างนั่นเอง)

อีก 1 Use case ที่น่าจะมีประโยชน์คือการทำ directory traversal แบบ recursive, ท่านี้สามารถใช้ command ช่วยรันล่วงหน้าเพื่อสร้างตัวแปรแบบเดียวกับที่ทำใน Tower of Hanoi หรือจะเรียกว่าเป็นการเลี่ยง recursive ก็ได้ครับ

ช่วงนี้เป็นนิมิตหมายที่ดีที่ทุกคนในทีมต่างมีงานเยอะ เลยขอ Terraform ท่าสวยๆ อีก 1 รอบ, สัปดาห์หน้าคาดว่าจะได้ขึ้นโครงร่าง API Security ส่วนประกอบ with example ขอขอบคุณที่ติดตามครับ

ผลลัพธ์ที่ได้จาก Terraform - Recursive -Tower of Hanoi

```
#n=1
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

my_module = [
  {
    "move" = "move A C"
  },
]
n_total = 1
```

```
#n=2
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

my_module = [
  {
    "move" = "move A B"
  },
  {
    "move" = "move A C"
  },
  {
    "move" = "move C B"
  },
]
n_total = 3
```


```
#n=3
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

my_module = [
  {
    "move" = "move A C"
  },
  {
    "move" = "move A B"
  },
  {
    "move" = "move B C"
  },
  {
    "move" = "move A C"
  },
  {
    "move" = "move A B"
  },
  {
    "move" = "move B C"
  },
  {
    "move" = "move A C"
  },
]
n_total = 7
```