Coding Gun

รู้จักกับตัวแปรบน Terraform

ตัวแปรเป็นสิ่งที่ทำให้ Infrastructure as Code นั้นแตกต่างจากการใช้ shell script เพราะเราจะแยก code กับ data ออกจากกัน และเมื่อเราแยก data ออกมาแล้วเราจะสามารถปรับเปลี่ยนได้โดยที่ code ยังเหมือนเดิม

ประเภทของตัวแปร

terraform มีตัวแปรอยู่ 3 ประเภทด้วยกัน

  1. Input Variables
  2. Local Variables
  3. Output Variables

Input Variables

input variables จะเป็นตัวแปรที่ใช้ในการกำหนดค่าการทำงานของ terraform สิ่งที่ต้องพิจารณาคือ เราจะใช้ input variables เมื่อต้องการ การเปลี่ยนแปลงในอนาคต เช่น เปลี่ยน region วันนี้ใช้ ap-southeast-1 อนาคตอาจย้ายไป deploy ใน ap-southeast-2 เราจะใช้การประกาศตัวแปรแบบนี้

การประกาศตัวแปร

variable "region" {}

ถ้าเราประกาศตัวแปรแบบนี้ เวลา run terraform apply จะมี prompt ขึ้นมาให้เรากรอกค่าของตัวแปร region ก่อนจะสร้าง resource ต่อไป

อีกสิ่งหนึ่งที่ขาดหายไปคือ data type terraform ซึ่งถ้าเราต้องการกำหนด data type และค่า default เราจะเขียนแบบนี้

variable "region" {
  type        = string
  description = "AWS default region"
  default     = "ap-southeast-1"
}

ซึ่งถ้าตัวแปรของเราเป็นความลับ ไม่ต้องการให้แสดงค่าของตัวแปรนี้ออกไปที่ console และ log เช่น Token หรือ Access Key เราจะกำหนดให้ตัวแปรนี้เป็น sensitive แบบนี้

variable "aws_secret_key" {
  type        = string
  description = "AWS secret key"
  sensitive   = true
}

ค่า default ของ sensitive จะเป็น false คือไม่เก็บความลับ

นอกจากนี้ใน variable block จะยังสามารถกำหนด property ได้อีก 2 อย่างคือ

การกำหนดค่าให้กับ input

ในตัวอย่างก่อนหน้าเป็นการกำหนดค่า default ให้กับตัวแปร ซึ่งการกำหนดค่าให้กับตัวแปรยังมีอีกหลายวิธี โดยที่ terraform จะอ่านค่าตัวแปรตามลำดับความสำคัญ ตามนี้

  1. TF_VAR_ วิธีแรกเราจะสร้าง Environment Variables ที่นำหน้าด้วยคำว่า TF_VAR_ ซึ่งใน linux และ macos เราจะใช้การ export command แบบนี้

    export TF_VAR_region=ap-southeast-1
    

    บน windows เราสามารถกำหนด Environment Variables ด้วย Powershell ได้แบบนี้

    $env:TF_VAR_region=ap-southeast-1
    
  2. terraform.tfvars วิธีทีนี้เราจะกำหนดค่าของตัวแปรไว้ในไฟล์ terraform.tfvars หรือ terraform.tfvars.json เมื่อ terraform เห็นไฟล์ terraform.tfvars ก็จะ load ตัวแปรต่างๆที่อยู่ในไฟล์นี้จะถูกอ่านออกไปใช้งานโดยอัตโนมัติ

    # ไฟล์ terraform.tfvars
    region=ap-southeast-1
    

    terraform.tfvars.json จะถูกอ่านทีหลัง (มีความสำคัญมากกว่า terraform.tfvars)

  3. .auto.tfvars วิธีนี้เราจะตั้งชื่อไฟล์แล้วลงท้ายด้วย .auto.tfvars หรือ .auto.tfvars. json เหมือนกับ terraform.tfvars เลยแต่จะมี piority มากกว่า เพราะ terraform จะเอาค่าของตัวแปรในไฟล์นี้ไปทับ terraform.tfvars ซึ่งเราอาจแยกออกเป็น environment ต่าง เช่น dev.auto.tfvars หรือ prod.auto.tfvars

    .auto.tfvars และ .auto.tfvars.json จะเรียงลำดับการอ่านตามชื่อไฟล์

  4. -var-file วิธีนี้เราจะ กำหนดชื่อไฟล์เข้าไปตอนที่สั่ง terraform apply แบบนี้

    $ terraform apply -var-file=dev.tfvars
    

    ส่วนใหญ่เราจะใช้นามสกุลเป็น tfvars เหมือนกัน แต่ไม่ได้ load เข้าไปใช้งานโดยอัตโนมัติ

  5. -var วิธีนี้ไม่ได้ใช้ไฟล์เหมือนในวิธีที่ผ่านมา เราะกำหนดค่าให้กับตัวแปรทีละตัว

    $ terraform apply -var="region=ap-southeast-1"
    
  6. Command prompt ถ้าเราไม่ได้กำหดค่าของตัวแปรด้วยวิธีไหนเลย และใน variable block ก็ไม่ได้กำหนดค่า default ไว้ด้วย terraform จะ prompt ขึ้นมาให้ใส่ค่าของตัวแปรตอน apply

การนำตัวแปรไปใช้งาน

เมื่อต้องการนำตัวแปรนี้ไปใช้งาน เราจะใช้ var.ชื่อตัวแปร แบบนี้

var.region

หรือถ้าต้องการเอาไปแทรกไว้เป็นส่วนหนึ่งของข้อความ เราจะใช้ interpolation แบบนี้

instance_name = "${var.project}-Web-Server"

Local Variables

local variables เป็นตัวแปรที่เราใช้คำนวนภายใน เหมือนกับที่เรากำหนด constant ขึ้นมาใช้งานใน programming language อื่นๆ ในตัวอย่างนี้จะเป็นการกำหนด path ของไฟล์ที่จะนำไปใส่ไว้ใน s3 bucket

locals {
  website_content = {
    website = "/website/index.html"
    logo    = "/website/Globo_logo_Vert.png"
  }
}

แต่โดยส่วนใหญ่แล้วเราจะรับค่าจาก input variables มาคำนวนต่ออีกทีแบบนี้ สังเกตุว่าเรา input เข้ามาแค่ var.company,var.project และ var.environment แต่นำมาสร้าง local variables ได้อีกหลายตัวเลยทีเดียว

locals {
  common_tags = {
    company      = var.company
    project      = "${var.company}-${var.project}"
    environment  = var.environment
  }

  naming_prefix = "${var.project}-${var.environment}"

  s3_bucket_name = "${lower(local.naming_prefix)}-${random_integer.s3.result}"
}

# ตัวอย่างใช้ random module สำหรับการ random ตัวเลขขึ้นมา 5 หลักต่อท้าย s3_bucket_name
resource "random_integer" "s3" {
  min = 10000
  max = 99999
}

Output Variables

output เป็นตัวแปรที่เราจะประกาศไว้ใช้งานใน 2 กรณีคือ

  1. ใช้แสดง output ออกหน้าจอ ตอนที่ run คำสั่ง terraform apply
  2. เมื่อเราสร้าง module ขึ้นมาใน terraform เราจะต้อง output ตัวแปรต่างๆออกไปใช้งานใน module อื่นๆต่อไป เหมือนกับการ return ผลลัพธ์ออกจาก function

รูปแบบของ output จะเป็นแบบนี้

output "aws_alb_public_dns" {
  value       = "http://${aws_lb.nginx.dns_name}"
  description = "Public DNS for the application load balancer"
}

Data type

การใช้งานตัวแปรใน terraform เราจำเป็นต้องเลือกใช้ data type ให้เหมาะกับการใช้งาน โดยที่ terraform จะมี data type ในกลุ่มต่างๆ ดังนี้

  1. primitive เป็น data type พื้นฐานที่จำเป็นเป็นต้องใช้ในการเขียนโปรแกรม เช่น string, number และ boolean

    variable "enable_dns_hostnames" {
        type        = bool
        description = "Enable DNS hostnames in VPC"
        default     = true
    }
    
  2. Collections เป็น data type ที่มีสมาชิกมากกว่า 1 ค่า โดยที่สมาชิกแต่ละตัวต้องมี data type แบบเดียวกัน ซึ่ง terraform จะมี collections อยู่ 3 ประเภทคือ list, set และ map

    variable "vpc_public_subnets_cidr_block" {
        type        = list(string)
        description = "CIDR Block for Public Subnets in VPC"
        default     = ["10.0.0.0/24", "10.0.1.0/24"]
    }
    

    เราอาจใช้ list และ map พร้อมๆกันได้ แบบนี้

    variable "ingress_with_cidr_blocks" {
        description = "List of ingress rules to create where 'cidr_blocks' is used"
        type        = list(map(string))
        default     = [{
            cidr_block = ["0.0.0.0/0"],
            description = "",
            from_port = 22
        }, {
            cidr_block = ["0.0.0.0/0"],
            description = "",
            from_port = 443
        }]
    }
    
  3. Structure เป็นตัวแปรที่มีสมาชิกหลายประเภทมารวมกัน มี 2 ประเภท คือ tuple และ object

    ตัวอย่าง การประกาศตัวแปร object

    variable "object_example" {
        type = object({ name=string, age=number })
    }
    
    # การกำหนดค่าให้กับตัวแปรประเภท object
    object_example = {
        name = "John"
        age  = 52
    }
    

    ตัวอย่าง การประกาศตัวแปร tuple

    variable "tuple_example" {
        type = tuple([string, number, bool])
    }
    
    # การกำหนดค่าให้กับตัวแปรประเภท tuple
    ["a", 15, true]
    
  4. Any จะใช้กับตัวแปรที่สามารถเป็น data type อะไรก็ได้ เช่น list(any)

  5. null จะเหมือนกับการใช้งาน programming language ภาษาอื่นๆ null จะเป็นค่าเริ่มต้น จะหมายความว่า ยังไม่แน่ใจว่า มี หรือ ไม่มีค่า

    variable "security_group_id" {
        description = "ID of existing security group whose rules we will manage"
        type        = string
        default     = null
    }
    

อ่านต่อเพิ่มเติมได้ที่นี่

Phanupong Permpimol
Follow me

Software Engineer ที่เชื่อในเรื่องของ Process เพราะเมื่อ Process ดี Product ก็จะดีตาม ปัจจุบันเป็นอาจารย์และที่ปรึกษาด้านการออกแบบและพัฒนา Software และ Web Security