Module in Terraform for conditional use cases(Advanced)
Tags: terraform, module, if, condition
Published: 2023-08-04 16:13:51

มาต่อเรื่อง Terraform อีกซักนิดครับ การใช้เงื่อนไขใน Terraform สามารถทำได้หลายแบบ จริงๆ เน้นท่าประหลาดๆ สำหรับ Use cases ยากๆ ที่หาจากไหนไม่ได้ ถ้าหลักง่ายๆคิดว่าหาจาก Docs ได้[google.com](https://google.com) ก่อนอื่นขอปูพื้นนิดนึงครับ


## condition
สามารถกำหนดเงื่อนไข precondition และ/หรือ postcondition ได้
เมื่อไม่ได้เงื่อนไขตามที่กำหนด ก็จะไม่สามารถสร้าง resources เสร็จสมบูรณ์ได้ และผลคือ Terraform apply ไม่สำเร็จ

```
resource "aws_instance" "example" {
  instance_type = "t3.micro"
  ami           = data.aws_ami.example.id

  lifecycle {
    # The AMI ID must refer to an AMI that contains an operating system
    # for the `x86_64` architecture.
    precondition {
      condition     = data.aws_ami.example.architecture == "x86_64"
      error_message = "The selected AMI must be for the x86_64 architecture."
    }

    # The EC2 instance must be allocated a public DNS hostname.
    postcondition {
      condition     = self.public_dns != ""
      error_message = "EC2 instance must be in a VPC that has public DNS hostnames enabled."
    }
  }
}
```

จะเห็นได้ว่า การใช้ condition, precondition, postcondition ตัว condition เองจะต้องสำเร็จอย่างเดียวเท่านั้นจึงจะทำงานต่อจนจบได้ ถ้าไม่สำเร็จ/ไม่ผ่าน ก็จะ Error แล้วจบการทำงาน แต่ถ้าเป็นเงื่อนไขที่มีทางเลือกมากกว่าหนึ่งล่ะ

## ถ้าเป็นการกำหนดค่า จะใช้

```
<statement> ? <procedure when true> : < procedure when false>
```

ตัวอย่างเช่น จะหาค่าตำแหน่งว่าเป็น East หรือ West จากตัวเลข

location = myinput < 50 ? "East" : "West"

จะได้ว่า หาก myinput มีค่าน้อยกว่า 50 จริง จะได้ location = "East"
แต่ถ้าเป็นเท็จ ก็คือ myinput มีค่าตั้งแต่ 50 ขึ้นไป จะได้ location = "West" นั่นเอง

จะเห็นได้ว่า เราสามารถกำหนดค่าที่มีทางเลือกมากกว่า 1 ได้ ไม่จำเป็นต้อง True เสมอไป

ถ้าต้องการใช้แบบ switch case หรือหลายทางเลือก ก็สามารถซ้อนกันได้

```
location = n < 26 ? "North" : n<50 ? "East" : West
```

## Resource
ถ้าเราต้องการใส่เงื่อนใน ในการสร้าง Resource ล่ะ ก็คือถ้าตรงเงื่อนไขให้สร้าง ถ้าไม่ตรงเงื่อนไขไม่สร้าง ซึ่งจะไม่เหมือนกับเคส condition ที่ต้องตรงเงื่อนไขเท่านั้น ไม่ตรงไม่ได้

เคสนี้เราสามารถใช้การ count หรือ for_each ใน resource blog มาช่วยได้
โดยถ้าเงื่อนไขที่กำหนดถูกต้อง ให้ทำการสร้าง resource นี้จำนวน count=1 ก็คือสร้างตามปกตินั่นเอง
แต่ถ้าเงื่อนไขไม่ถูกต้อง ก็ให้ทำการสร้าง resource นี้จำนวน count=0 ก็คือไม่สร้างนั่นเอง

## Use case 1
ถ้าต้องการสร้าง resource แบบ count และใส่เงื่อนไขด้วย จะทำอย่างไร
เพราะการสร้าง resource แบบมีเงื่อนไขจะต้องใช้ count, และ terraform ไม่ยอมให้ทำ count ซ้อนกัน

เราจะใช้ความสามารถของ Module มาช่วยโดย
Count=n เพื่อทำการสร้าง Module จำนวน n module
แล้วในแต่ละ Module ค่อยไปสร้าง Resource Blog ตามปกติ ก็จะสามาถใช้ count กับ Resource Blog ในรูปแบบปกติได้ ถ้าตรงเงื่อนไขให้สร้าง ไม่ตรงเงื่อนไขไม่ต้องสร้างได้นั่นเอง


## Use case 2
Resource ที่มีการสร้าง objects ข้างในได้จำนวนเท่ากับ 1 หรือมากกว่า 1
เช่น VM ที่มี Disk 1 หรือ มากกว่า 1 ลูก
VM ที่ Attach Network Interface Card 1 หรือมากกว่า 1 ใบ
เราสามารถสั่งให้ Terraform Resource สร้าง Disk ลูกที่ 2 ตามเงื่อนไขได้ไหม(เช่น ถ้ามีการกำหนดขนาด Disk >0 จึงค่อยสร้าง)

จะเห็นได้ว่า ในเคสนี้ การใช้ Count จะเป็นการตัดสินใจสร้าง หรือไม่สร้างในระดับ Resource VM แต่ไม่สามารถตัดสินใจระดับ Object หรือจำนวน Object ใต้ Resource ได้

เราใช้การออกแบบมาแก้ปัญหา โดยสร้าง Resource มา 2 แบบ
แบบแรก มี Disk ลูกเดียว, กำหนดเงื่อนไขการสร้าง Resource นี้ต่อเมื่อค่าความจุของ Disk 2 <1 โดยใช้ Count เป็นตัวกำหนดเงื่อนไข
แบบที่สอง มี Disk สองลูก, กำหนดเงื่อนไขการสร้าง Resource นี้ต่อเมื่อค่าความจุของ Disk 2 >0 โดยใช้ Count เป็นตัวกำหนดเงื่อนไข

### สรุป
จะเห็นได้ว่า HCL ถูกออกแบบมาให้ง่ายต่อ Human ในการเข้าใจและใช้งาน แต่ไม่ได้ออกแบบมาในเชิงการ Programming, หากต้องการใส่เงื่อนไขที่ซับซ้อนหน่อยจะต้องออกแรงเพิ่มเป็นครั้งๆ ไป โดยใช้การทำเงื่อนไขที่ HCL มีให้ใช้คือ "? :" และ module
เราอาจใช้งาน module เกือบเป็น function ที่ถูกเรียกมาใช้งานก็ได้ เพราะสามารถไปทำชุดงานอีกชุด(อีก Folder) ได้ และ return ค่าได้ แต่ระวังเรื่องเงื่อนไขการเรียก Module ไม่สามารถกำหนดได้ เหตุผลคือการเรียกโมดูลต้องตายตัว จะเรียกตามเงื่อนไขไม่ได้ ต้องเรียกหรือไม่เรียกเลยเท่านั้น ในทีนี้ก็คือต้องเรียกตลอด แล้วค่อยใส่เงื่อนไขไว้ใต้ module อีกทีนั่นเองครับ