ข้ามไปยังเนื้อหา

State, variables & outputs

เราจะ refactor AWS scratch resource จากบทที่แล้วให้เป็นอะไรที่ reusable — input เป็น variables, ผลลัพธ์เป็น outputs, และ data source ไว้อ่านสิ่งที่ Terraform ไม่ได้สร้าง ระหว่างทางเราจะเพ่งดูไฟล์ที่ผูกทุกอย่างเข้าด้วยกัน: state

state คือ concept ที่เงียบที่สุดและมีผลกระทบมากที่สุดใน Terraform เข้าใจ state คือสิ่งที่ทำให้การกระโดดไป remote state และ locking (Module 3) เข้าท่า แทนที่จะรู้สึกเหมือนพิธีกรรม

state file ของ Terraform (terraform.tfstate) คือ map จาก configuration ของคุณไปยัง resource จริงที่สร้างขึ้น เมื่อคุณเขียน aws_s3_bucket.first state จำไว้ว่าชื่อนั้นชี้ไปที่ bucket clouddeploy-first-a1b2c3d4 ใน AWS ถ้าไม่มี state Terraform บอกไม่ได้ว่า “สร้าง bucket ใหม่” ต่างจาก “bucket นี้มีอยู่แล้ว” อย่างไร — ทุก plan เริ่มด้วยการเทียบ config กับ state กับ ความจริง

นั่นมีผลตามมาทันทีสองอย่างที่คอร์สนี้สร้างอยู่รอบ ๆ อย่างแรก state คือความจริงที่แชร์กัน: ถ้าสองคน (หรือคนกับ CI job) apply ทับ local file ตัวเดียวกัน พวกเขาทับกันเอง — นั่นคือเหตุผลที่ Module 3 ย้าย state ไปที่ locked remote backend อย่างที่สอง state เก็บ secret เป็น plaintext: database password โผล่ใน terraform.tfstate เป็น clear text นั่นคืออีกเหตุผลที่ state ควรอยู่ใน encrypted backend ไม่ใช่บน laptop

Variables parameterize configuration ให้โค้ดชุดเดียวรับ input ต่างกันได้; outputs คือ contract สาธารณะของ configuration นั้น — ค่าที่ configuration อื่น (และ Terragrunt dependency) ดึงไปใช้ Data sources อ่าน infrastructure ที่มีอยู่โดยไม่เข้าไปจัดการ

output เป็น contract ของ module เทียบกับการเอื้อมเข้าไปหยิบไส้ในของ resource:

  • Pros: output คือ surface ที่นิ่งและตั้งใจ เมื่อ live/aws/cluster ต้องการ subnet ID ของ network ก็อ่าน dependency.network.outputs.private_subnet_ids — ชื่อที่คุณเลือกเอง — ไม่ใช่ attribute ลึก ๆ ของ resource ที่อาจโดน refactor หายไป
  • Cons: คุณต้องออกแบบเอง output ที่ลืม expose จะ block consumer ปลายน้ำจนกว่าคุณจะเพิ่มแล้ว apply ใหม่ นั่นคือ feature เพราะบังคับให้ interface ชัดเจน

local state (ตอนนี้) เทียบกับ remote state (Module 3):

  • Pros of local: ตั้งค่าเป็นศูนย์ ไฟล์เดียวบน disk, ตรวจได้ทันที, เหมาะสำหรับการทดลองคนเดียว
  • Cons of local: ไม่มีการแชร์, ไม่มี locking, secret นั่งอยู่แบบไม่ encrypt ใน working directory ของคุณ, และ rm เดียวก็เสียบันทึกเดียวของสิ่งที่มีอยู่ ใช้กับ scratch/ ได้; ยอมรับไม่ได้สำหรับ platform จริง — นั่นคือเหตุผลทั้งหมดของ Module 3

ทำต่อใน scratch/aws/ โดยแยก config เป็นไฟล์ตาม convention

ให้แต่ละ variable มี type, default, และ — ตรงที่สำคัญ — validation:

variable "name" {
description = "Name prefix for the bucket"
type = string
default = "clouddeploy-first"
}
variable "region" {
description = "AWS region"
type = string
default = "us-east-1"
validation {
condition = can(regex("^[a-z]{2}-[a-z]+-[0-9]$", var.region))
error_message = "Region must look like us-east-1."
}
}
provider "aws" {
region = var.region
}
resource "random_id" "suffix" {
byte_length = 4
}
resource "aws_s3_bucket" "first" {
bucket = "${var.name}-${random_id.suffix.hex}"
}

data source อ่าน มากกว่าสร้าง aws_caller_identity return ว่าคุณ authenticate เป็นใคร — สะดวกสำหรับการ tag หรือสร้างชื่อ unique:

data "aws_caller_identity" "current" {}
output "bucket_name" {
description = "The created bucket's name"
value = aws_s3_bucket.first.bucket
}
output "bucket_arn" {
description = "The created bucket's ARN"
value = aws_s3_bucket.first.arn
}
output "account_id" {
description = "The AWS account this was deployed to"
value = data.aws_caller_identity.current.account_id
}

ทำเครื่องหมาย output ที่เป็นความลับจริง ๆ ด้วย sensitive = true (คุณจะทำแบบนี้กับ database password ใน Module 7) — Terraform จะ redact ค่าออกจาก log ของ plan/apply แต่ ไม่ encrypt ใน state นั่นคือประเด็นที่ผลักไปสู่ remote state ต่อไป

apply แล้วตรวจ state และ output:

Terminal window
terraform init
terraform apply # Apply complete! Resources: 2 added.
terraform output # all outputs
terraform output bucket_name # one output
terraform output -json # machine-readable, for scripting
account_id = "123456789012"
bucket_arn = "arn:aws:s3:::clouddeploy-first-a1b2c3d4"
bucket_name = "clouddeploy-first-a1b2c3d4"

ตอนนี้ดูว่า state ติดตามอะไรอยู่ และพิสูจน์ว่า state ตรงกับความจริง:

Terminal window
terraform state list # aws_s3_bucket.first, random_id.suffix, ...
terraform plan # No changes. Your infrastructure matches the configuration.

No changes นั่นคือแก่นทั้งหมด: plan เทียบ config → state → cloud จริงแล้วพบว่าตรงกันหมด เปลี่ยน var.name, รัน plan อีกครั้ง, แล้วดู Terraform เสนอ replace bucket จากนั้นเก็บกวาด:

Terminal window
terraform destroy

คุณเสร็จเมื่อ terraform output พิมพ์ค่าทั้งสามของคุณและ plan ใหม่รายงาน No changes — พิสูจน์ว่า state คือ map ที่แม่นยำของความจริง

ตรวจสอบความเข้าใจ:

  1. state file map ระหว่างอะไรกับอะไร และทำไม Terraform บอก “create” ต่างจาก “already exists” ไม่ได้ถ้าไม่มี state?
  2. ให้เหตุผลรูปธรรมสองข้อว่าทำไม local state ยอมรับไม่ได้สำหรับ platform จริงที่แชร์กัน
  3. ทำไม output ของ module ถึงเป็น dependency surface ที่ดีกว่าการเอื้อมเข้าไปหา attribute ของ resource ตรง ๆ?
  4. sensitive = true encrypt ค่าใน state ไหม? ถ้าไม่ แล้วแก้ปัญหาอะไรจริง ๆ และอะไรแก้ปัญหาการ encrypt?

ตอนนี้คุณรู้แล้วว่า state คืออะไร, ทำไมจึงเป็นแหล่งความจริง, และทำไมการเก็บไว้ local จึงโอเคสำหรับ scratchpad แต่ผิดสำหรับ platform คุณ parameterize resource ด้วย variable, ให้มี output contract ที่สะอาด, และอ่านโลกด้วย data source แล้ว output พวกนั้นคือสิ่งที่ Terragrunt จะต่อสายเข้าด้วยกันเป็น dependency ข้ามสาม cloud — และนั่นคือที่ที่เราจะไปต่อ

Next: Terragrunt & Remote State →