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

Outputs and Locals

local คือ expression ที่มีชื่อ คำนวณครั้งเดียวแล้วนำกลับมาใช้ซ้ำภายใน configuration ส่วน output จะดึงค่าออกจาก configuration ไปให้มองเห็นได้หลัง apply — รวมกับ variable แล้วทั้งสามคือ named value สามชนิดที่แตกต่างกันใน Terraform

variable ถูกตั้งค่าโดยคนที่เรียกใช้ configuration และ output ถูกเปิดเผยให้คนที่นำไปใช้ต่อ ส่วน local ไม่ใช่ทั้งสองอย่างนั้น เป็นแค่ความสะดวกภายใน เป็นชื่อที่ตั้งให้กับ expression เพื่อไม่ต้องเขียน expression เดิมซ้ำทุกครั้งที่ต้องใช้ผลลัพธ์ ไม่มีใครนอก configuration ตั้งค่า local ได้ และไม่มีใครนอก configuration อ่านได้โดยตรงเช่นกัน

locals {
name_prefix = "${var.environment}-${var.project}"
common_tags = {
Project = var.project
Environment = var.environment
ManagedBy = "terraform"
}
vm_count = var.environment == "prod" ? 5 : 2
# Azure Storage Account names must be 3-24 characters, lowercase letters and numbers only
storage_account_name = lower(replace("${local.name_prefix}appdata", "-", ""))
}
resource "azurerm_linux_virtual_machine" "web" {
count = local.vm_count
name = "${local.name_prefix}-web-${count.index}"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
size = "Standard_D2s_v3"
admin_username = "azureuser"
network_interface_ids = [
azurerm_network_interface.web[count.index].id,
]
admin_ssh_key {
username = "azureuser"
public_key = file("~/.ssh/id_rsa.pub")
}
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts"
version = "latest"
}
tags = merge(local.common_tags, {
Name = "${local.name_prefix}-web-${count.index}"
})
}
resource "azurerm_storage_account" "app_data" {
name = local.storage_account_name
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
account_tier = "Standard"
account_replication_type = "LRS"
tags = local.common_tags
}

local.common_tags ถูกคำนวณครั้งเดียวจาก var.project กับ var.environment แล้วถูกอ้างอิงทั้งจาก azurerm_linux_virtual_machine.web และ azurerm_storage_account.app_data ถ้าไม่มี local ตัวนี้ key-value สามคู่เดิมต้องถูก copy ไปทุก resource block ที่ต้องใช้ และถ้าวันหลังต้องเปลี่ยน tagging scheme ก็ต้องไล่หาทุก copy เอง local.vm_count ทำหน้าที่คล้ายกันกับ conditional เล็ก ๆ การตัดสินใจ “prod ใช้ 5 ที่อื่นใช้ 2” ถูกเขียนครั้งเดียวแล้วอ้างอิงด้วยชื่อทุกที่ที่ต้องใช้ local.storage_account_name ยิ่งเห็นประโยชน์ชัดกว่านั้นอีก — ชื่อ Azure Storage Account ต้องมีความยาว 3 ถึง 24 ตัวอักษร เป็นตัวพิมพ์เล็กกับตัวเลขเท่านั้น ห้ามมีขีดกลาง ดังนั้น expression นี้ตัดขีดกลางออกจาก name_prefix ครั้งเดียวในที่เดียว แทนที่ทุก resource ที่ต้องใช้ชื่อ storage account ที่ถูกต้องจะต้องเรียก lower(replace(...)) ซ้ำเอง local ไม่เคยถูก assign โดย caller และไม่ปรากฏใน terraform plan เป็นอะไรที่ override ได้ — แต่คือแค่ชื่อของค่าที่คำนวณไว้ภายใน configuration นี้เท่านั้น

output block ดึงค่าที่มีอยู่ภายใน configuration — ส่วนใหญ่คือ attribute ของ resource ที่รู้ค่าจริงได้ก็ต่อเมื่อ apply เสร็จแล้ว — แล้วทำให้ค่านั้นใช้งานได้จากภายนอก configuration description ไว้บอกความหมายของค่า และ sensitive = true ซ่อนค่าจาก CLI output ปกติเหมือนที่ทำกับ variable

output "web_vm_ids" {
description = "IDs of the virtual machines created for the web tier"
value = azurerm_linux_virtual_machine.web[*].id
}
output "app_data_storage_account_id" {
description = "Resource ID of the storage account used for application data"
value = azurerm_storage_account.app_data.id
}
output "sql_admin_password" {
description = "Administrator login password generated for the Azure SQL server"
value = azurerm_mssql_server.main.administrator_login_password
sensitive = true
}
Terminal window
# List every output and its value
terraform output
# Print one output's value, unquoted for scripting
terraform output web_vm_ids
# Machine-readable output, for piping into jq or another tool
terraform output -json app_data_storage_account_id

terraform output เฉย ๆ จะ print output ทุกตัวที่ประกาศไว้หลัง apply ล่าสุด terraform output <name> print แค่ตัวเดียว และ terraform output -json แสดงค่า (หรือทุกค่าถ้าไม่ระบุชื่อ) เป็น JSON ซึ่งจำเป็นเวลา CI pipeline หรือ script อื่นต้องเอาผลลัพธ์ไปใช้ต่อแบบ programmatic ไม่ใช่ให้คนอ่านเฉย ๆ sensitive = true บน sql_admin_password ทำงานเหมือนกับตอนอยู่บน variable ทุกอย่าง Terraform จะ print (sensitive value) ใน output ของ plan/apply และใน terraform output เปล่า ๆ แต่ terraform output sql_admin_password ยังคง print ค่าจริงถ้าเรียกชื่อตรง ๆ และค่านั้นยังถูกเก็บเป็น plain text ใน state เหมือนเดิม นี่คือการป้องกันตอนแสดงผลบน terminal กับ log ไม่ใช่ access control

ภายใน root configuration เดียว output ส่วนใหญ่เป็นแค่ข้อมูล — เป็นวิธีที่สะดวกในการโชว์ค่าให้คนที่รัน terraform apply เห็นเฉย ๆ ความสำคัญจริง ๆ จะเห็นชัดก็ต่อเมื่อ configuration ไม่ได้เป็นสิ่งเดียวโดด ๆ อีกต่อไป เวลาแยก infrastructure ออกเป็น module, module ไม่มีทางอื่นเลยที่จะส่งค่าที่คำนวณแล้วกลับไปให้ผู้เรียกใช้นอกจากผ่าน output block — module เรื่อง Modules ในคอร์สนี้จะพูดถึงการอ้างอิง module.<name>.<output> แบบละเอียดต่อไป เวลาแยก infrastructure ออกเป็น Terragrunt unit ที่ deploy แยกกันแทน dependency block จะอ่าน output ของอีก unit หนึ่งเพื่อเชื่อม unit เข้าด้วยกันโดยไม่ต้อง hard-code ค่าใด ๆ ระหว่างกันเลย — pattern นี้จะถูกพูดถึงอีกครั้งใน module ของ Terragrunt ทีหลัง ทั้งสองกรณีใช้กลไกเดียวกับที่เห็นตรงนี้ ค่าหนึ่งรู้ค่าจริงภายใน configuration หนึ่ง output block เปิดเผยออกมา แล้วสิ่งอื่น — parent module หรือ dependent unit — นำไปใช้ต่อ

flowchart LR
  ve["var.environment"] --> lp["local.name_prefix"]
  vp["var.project"] --> lp
  lp --> ri["azurerm_linux_virtual_machine.web"]
  lp --> rb["azurerm_storage_account.app_data"]
  rb --> ob["output app_data_storage_account_id"]
  ob --> dep["dependency block or module output reference"]
Multiple value sources resolve to one value per variable, by precedence
อะไรคือความต่างหลักในจุดประสงค์ระหว่าง local กับ output
local ต่างจาก variable อย่างไร
ทำไม output ถึงสำคัญกับการ compose infrastructure ทั้งที่ module กับ Terragrunt dependency จะพูดถึงละเอียดกว่าทีหลัง
terraform output -json มีประโยชน์ตรงไหน