Module Composition and Sources
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”source argument บน module block บอก Terraform ว่าจะไปเอา code ของ module นั้นมาจากไหน และ infrastructure จริงที่ไม่ trivial ถูกสร้างขึ้นด้วยการประกอบ module เล็ก ๆ หลายตัวเข้าด้วยกัน — output ของ module หนึ่งถูกส่งตรงเข้าไปเป็น input variable ของอีก module
code ของ module มาจากไหน: source argument
หัวข้อที่มีชื่อว่า “code ของ module มาจากไหน: source argument”ทุก module block ต้องมี source และมีสามแบบหลักที่คุณจะเจอบ่อยมาก local relative path ชี้ไปที่ directory ภายใน repository ของคุณเอง public Terraform Registry resolve address สั้น ๆ แบบ <namespace>/<name>/<provider> ไปยัง module ที่ publish และมี version แล้ว Git URL ชี้ตรงไปที่ repository โดย pin ไปที่ tag, branch หรือ commit ที่ต้องการได้ด้วย ?ref=
# local relative pathmodule "storage" { source = "./modules/storage-account"}
# public Terraform Registrymodule "vnet" { source = "Azure/vnet/azurerm" version = "~> 5.0"}
# Git URL pinned to a specific tagmodule "vnet_from_git" { source = "git::https://github.com/acme/terraform-modules.git//vnet?ref=v1.4.0"}ทั้งสามแบบเป็นแค่คำตอบต่างกันของคำถามเดียวกัน: Terraform ไปเอาไฟล์ .tf ของ child module นี้จากที่ไหน ไม่มีอะไรในวิธีที่คุณเขียน variables.tf, main.tf หรือ outputs.tf ภายใน module ที่ต้องเปลี่ยน ไม่ว่า caller จะใช้ source แบบไหน
ประกอบ module เข้าด้วยกัน: output หนึ่งกลายเป็น input ของอีกตัว
หัวข้อที่มีชื่อว่า “ประกอบ module เข้าด้วยกัน: output หนึ่งกลายเป็น input ของอีกตัว”configuration แบบ flat ตัวเดียวที่มี resource เป็นร้อยอ่านยากและเอาไปใช้ซ้ำไม่ได้เลย infrastructure จริงถูกสร้างจาก module เล็ก ๆ ที่ focus เฉพาะเรื่อง แล้วต่อกันผ่าน output และ input ของแต่ละตัว module vnet expose identifier ที่ module อื่นต้องใช้:
output "vnet_id" { description = "ID of the created virtual network" value = azurerm_virtual_network.this.id}
output "subnet_ids" { description = "IDs of the subnets" value = azurerm_subnet.this[*].id}module virtual-machine ประกาศ input ที่ต้องใช้เพื่อ launch เข้าไปใน network นั้น:
variable "vnet_id" { description = "ID of the virtual network to launch the VM into" type = string}
variable "subnet_id" { description = "ID of the subnet to launch the VM into" type = string}root configuration คือส่วนที่ต่อทั้งสอง module เข้าด้วยกันจริง ๆ โดยส่ง output ของ module หนึ่งตรงเข้าไปเป็น input ของอีก module:
# root main.tfmodule "vnet" { source = "./modules/vnet" address_space = "10.0.0.0/16"}
module "web_server" { source = "./modules/virtual-machine"
vnet_id = module.vnet.vnet_id subnet_id = module.vnet.subnet_ids[0]}ไม่มี module ไหนต้องรู้ว่าอีกตัวมีอยู่ module vnet ไม่รู้เลยว่าจะมี module virtual-machine มา consume output ของตัวเอง และ module virtual-machine ก็ไม่รู้ว่า vnet_id ที่ได้รับมาจาก module หรือเป็นแค่ string ที่ hardcode ไว้ root configuration คือจุดเดียวที่รู้จักทั้งสอง module และเชื่อมทั้งสองเข้าด้วยกัน
public Terraform Registry ในฐานะตัวช่วยเพิ่ม productivity
หัวข้อที่มีชื่อว่า “public Terraform Registry ในฐานะตัวช่วยเพิ่ม productivity”pattern ที่พบบ่อยอย่าง “virtual network ที่มี public/private subnet” ถูกแก้ปัญหาไปแล้วหลายครั้ง public Terraform Registry มี community module ที่ maintain ดีและใช้กันแพร่หลาย — module ขององค์กร Azure เช่น Azure/vnet/azurerm และ Azure/naming/azurerm เป็นจุดเริ่มต้นที่พบบ่อยที่สุดสำหรับ Azure infrastructure — การหยิบ module พวกนี้มาใช้มักคุ้มเวลากว่าการเขียนของเทียบเท่าขึ้นมาเองตั้งแต่ต้น
module "vnet" { source = "Azure/vnet/azurerm" version = "~> 5.0"
resource_group_name = azurerm_resource_group.main.name vnet_name = "acme-app-vnet" address_space = ["10.0.0.0/16"] subnet_prefixes = ["10.0.1.0/24", "10.0.2.0/24"] subnet_names = ["private", "public"]}ถึงอย่างนั้น “ใช้กันแพร่หลาย” ก็ไม่เหมือนกับ “เชื่อได้แบบไม่ต้องดู” ก่อนจะเอา Registry module มาสร้าง production infrastructure ควรอ่านก่อนว่า module สร้างอะไรจริง ๆ — resource, default value และ role assignment ใด ๆ ที่ provision ให้โดยอัตโนมัติ Registry module ก็ยังเป็น code ของคนอื่นที่รันด้วย credential ของ Azure ที่เป็นของคุณ
flowchart LR
subgraph root["Root configuration"]
vnetmod["module vnet"]
vmmod["module web_server"]
end
subgraph vnet["Child module: vnet"]
vnetout1["output vnet_id"]
vnetout2["output subnet_ids"]
end
subgraph vm["Child module: virtual-machine"]
vmin1["var.vnet_id"]
vmin2["var.subnet_id"]
end
vnetmod --> vnetout1
vnetmod --> vnetout2
vnetout1 -->|module.vnet.vnet_id| vmin1
vnetout2 -->|module.vnet.subnet_ids[0]| vmin2
vmin1 --> vmmod
vmin2 --> vmmod