昨天我們聊到迴圈與條件邏輯,學會了怎麼透過 count
、for_each
和條件表達式,讓 Terraform 能一次產生一組資源,或根據環境自動決定要不要建立。
這些技巧解決了「我要建立多個資源」的問題。
但在實際專案裡,我們常常遇到另一種情況:不是「很多個資源」,而是「一個資源裡面有一堆重複的子設定」。
舉個例子:
這些子設定如果全部手動寫,程式碼馬上就變得很長,幾十行、上百行都是同樣的結構,只是數字或名稱不同。別說改起來麻煩,光是看就頭暈。這時候,Terraform 就幫我們準備了一個好用的工具:Dynamic Blocks。
Dynamic Block 的概念很簡單:
就像昨天的 for_each
可以幫你產生多個資源一樣,Dynamic Block 可以在 資源內部,對子區塊再做一次迴圈。
想像一下,你要建立一個防火牆規則,要開放三個 port:80、443、8080。
如果用傳統寫法,你可能要寫三段 allow
區塊。但有了 Dynamic Block,就能這樣寫:
variable "allowed_ports" {
default = [80, 443, 8080]
}
resource "google_compute_firewall" "example" {
name = "example-firewall"
network = "default"
dynamic "allow" {
for_each = var.allowed_ports
content {
protocol = "tcp"
ports = [allow.value]
}
}
}
Terraform 在 apply 的時候,會自動展開成三個 allow
區塊。
這樣一來,我們的程式碼只有幾行,但效果是一樣的。更重要的是,未來只要改變 allowed_ports
變數,規則就會自動調整,完全不用手動複製貼上。
上面例子只是同一種協定(TCP)的多個 port,如果要進一步控制不同的協定怎麼辦?
例如:我要同時允許 TCP 80/443,還要允許 UDP 53。這時候用 Map 搭配 Dynamic Block,就能一次搞定:
variable "rules" {
default = {
tcp = [80, 443]
udp = [53]
}
}
resource "google_compute_firewall" "example" {
name = "example-firewall"
network = "default"
dynamic "allow" {
for_each = var.rules
content {
protocol = allow.key
ports = allow.value
}
}
}
這樣寫展開後 Terraform 就會自動生成:
這樣比原本要手動寫很多段規則來得更乾淨又直覺。
Dynamic Block 也能和昨天學到的條件判斷結合。
舉個例子:在 production 環境才允許外部存取,其他環境只允許內部。
dynamic "allow" {
for_each = var.env == "prod" ? ["0.0.0.0/0"] : ["10.0.0.0/8"]
content {
protocol = "tcp"
ports = ["443"]
}
}
這樣 dev/test 環境就只允許內部網段,production 才開放給外部。完全不用再維護多份不同的防火牆設定檔,一個程式碼就能搞定。
Dynamic Block 是一個很好用的工具,特別是在面對需要重複子設定的情境時,例如防火牆規則、IAM 綁定,甚至一些安全性設定。這些地方往往結構都很類似,如果單純用複製貼上會顯得冗長又難維護,用 Dynamic Block 就能把重複的部分抽象化,讓程式碼更乾淨。
但它也不是什麼情境都適合使用。如果只是一兩個設定,直接寫死其實會更直觀,也能讓後續閱讀程式碼的人更快理解。Dynamic Block 的價值主要還是在於「減少重複、提升可維護性」,而不是炫技或刻意追求動態化!
今天我們把焦點放在 Dynamic Blocks,學會了:
不用每個地方都刻意套用,但在遇到需要高度重複的設定時,它會讓整份配置既乾淨又有彈性。這幾天我們一路從 count
、for_each
到條件式,再到今天的 Dynamic Block,其實都是在逐步累積「程式化配置」的能力。
明天要分享的主題則會換個角度,來看看 Functions 與 Local Values,它們能幫助我們在字串、列表、映射的處理上更靈活~