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

Module Composition and Sources

source argument บน module block บอก Terraform ว่าจะไปเอา code ของ module นั้นมาจากไหน และ infrastructure จริงที่ไม่ trivial ถูกสร้างขึ้นด้วยการประกอบ module เล็ก ๆ หลายตัวเข้าด้วยกัน — output ของ module หนึ่งถูกส่งตรงเข้าไปเป็น input variable ของอีก module

ทุก 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 path
module "storage" {
source = "./modules/storage-account"
}
# public Terraform Registry
module "vnet" {
source = "Azure/vnet/azurerm"
version = "~> 5.0"
}
# Git URL pinned to a specific tag
module "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 อื่นต้องใช้:

modules/vnet/outputs.tf
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 นั้น:

modules/virtual-machine/variables.tf
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.tf
module "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 และเชื่อมทั้งสองเข้าด้วยกัน

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
A vnet module's outputs feeding a virtual-machine module's inputs from the root configuration
บทเรียนนี้อธิบาย module source สามแบบไหนบ้าง
ในตัวอย่างการประกอบ vnet กับ virtual-machine module virtual-machine module ได้รับ subnet ที่ต้อง launch เข้าไปอย่างไร
module vnet ในตัวอย่างนี้ต้องรู้หรือไม่ว่าจะมี virtual-machine module มา consume output ของตัวเอง
เมื่อไหร่ที่การหยิบ public Terraform Registry module มาใช้สมเหตุสมผลกว่าการเขียนเองตั้งแต่ต้น