條件式與迴圈概述
在 Terraform 中,條件式與迴圈是實現基礎設施即程式碼(IaC)靈活性的核心功能。透過這些進階語法,我們可以根據不同環境、需求動態建立資源,避免重複的程式碼,並提高配置的可維護性。
本文將深入探討 Terraform 提供的條件式與迴圈機制,包括三元運算子、count、for_each、for 表達式以及 dynamic 區塊等進階用法。
條件運算子(三元運算子)
Terraform 使用與大多數程式語言相似的三元運算子語法:
1
| condition ? true_value : false_value
|
基本範例
1
2
3
4
5
6
7
8
9
10
11
12
13
| variable "environment" {
type = string
default = "dev"
}
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = var.environment == "prod" ? "t3.large" : "t3.micro"
tags = {
Name = var.environment == "prod" ? "Production Server" : "Development Server"
}
}
|
條件式建立資源
透過 count 搭配條件式,可以決定是否建立資源:
1
2
3
4
5
6
7
8
9
| variable "create_bucket" {
type = bool
default = true
}
resource "aws_s3_bucket" "optional" {
count = var.create_bucket ? 1 : 0
bucket = "my-optional-bucket"
}
|
count 參數
count 是 Terraform 中最基本的迴圈機制,用於建立多個相同類型的資源。
基本用法
1
2
3
4
5
6
7
8
9
| resource "aws_instance" "server" {
count = 3
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
tags = {
Name = "Server-${count.index}"
}
}
|
搭配變數列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| variable "server_names" {
type = list(string)
default = ["web", "api", "db"]
}
resource "aws_instance" "server" {
count = length(var.server_names)
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
tags = {
Name = var.server_names[count.index]
}
}
|
for_each 迴圈
for_each 提供比 count 更強大的迴圈功能,適用於 map 或 set 類型的資料。
使用 Map
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| variable "instances" {
type = map(object({
instance_type = string
ami = string
}))
default = {
web = {
instance_type = "t3.micro"
ami = "ami-0c55b159cbfafe1f0"
}
api = {
instance_type = "t3.small"
ami = "ami-0c55b159cbfafe1f0"
}
}
}
resource "aws_instance" "server" {
for_each = var.instances
ami = each.value.ami
instance_type = each.value.instance_type
tags = {
Name = each.key
}
}
|
使用 Set
1
2
3
4
5
6
7
8
9
10
| variable "subnet_ids" {
type = set(string)
default = ["subnet-abc123", "subnet-def456"]
}
resource "aws_route_table_association" "example" {
for_each = var.subnet_ids
subnet_id = each.value
route_table_id = aws_route_table.example.id
}
|
for 表達式
for 表達式用於轉換集合資料,可以產生 list 或 map。
轉換為 List
1
2
3
4
5
6
7
8
9
| variable "names" {
type = list(string)
default = ["alice", "bob", "charlie"]
}
locals {
upper_names = [for name in var.names : upper(name)]
# 結果: ["ALICE", "BOB", "CHARLIE"]
}
|
轉換為 Map
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| variable "users" {
type = list(object({
name = string
role = string
}))
default = [
{ name = "alice", role = "admin" },
{ name = "bob", role = "user" }
]
}
locals {
user_roles = { for user in var.users : user.name => user.role }
# 結果: { "alice" = "admin", "bob" = "user" }
}
|
條件過濾
1
2
3
4
| locals {
admins = [for user in var.users : user.name if user.role == "admin"]
# 結果: ["alice"]
}
|
dynamic 區塊
dynamic 區塊允許在資源內動態產生巢狀區塊,非常適合處理可變數量的設定。
安全群組規則範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| variable "ingress_rules" {
type = list(object({
port = number
protocol = string
cidr_blocks = list(string)
}))
default = [
{ port = 80, protocol = "tcp", cidr_blocks = ["0.0.0.0/0"] },
{ port = 443, protocol = "tcp", cidr_blocks = ["0.0.0.0/0"] },
{ port = 22, protocol = "tcp", cidr_blocks = ["10.0.0.0/8"] }
]
}
resource "aws_security_group" "example" {
name = "dynamic-sg"
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.port
to_port = ingress.value.port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
}
|
條件式與迴圈組合使用
結合條件式與迴圈可以實現更複雜的邏輯:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| variable "environment" {
type = string
default = "prod"
}
variable "enable_monitoring" {
type = bool
default = true
}
locals {
base_instances = {
web = { type = "t3.micro", count = 2 }
api = { type = "t3.small", count = 1 }
}
prod_instances = var.environment == "prod" ? {
web = { type = "t3.large", count = 4 }
api = { type = "t3.medium", count = 2 }
} : local.base_instances
monitoring_config = var.enable_monitoring ? {
detailed_monitoring = true
alarm_actions = ["arn:aws:sns:us-east-1:123456789:alerts"]
} : {
detailed_monitoring = false
alarm_actions = []
}
}
|
常見模式與範例
多環境部署
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| variable "environments" {
type = map(object({
instance_count = number
instance_type = string
}))
default = {
dev = { instance_count = 1, instance_type = "t3.micro" }
stag = { instance_count = 2, instance_type = "t3.small" }
prod = { instance_count = 4, instance_type = "t3.large" }
}
}
resource "aws_instance" "app" {
for_each = var.environments
count = each.value.instance_count
instance_type = each.value.instance_type
ami = "ami-0c55b159cbfafe1f0"
tags = {
Environment = each.key
}
}
|
條件式模組呼叫
1
2
3
4
5
6
| module "monitoring" {
source = "./modules/monitoring"
count = var.enable_monitoring ? 1 : 0
instance_ids = aws_instance.app[*].id
}
|
最佳實踐
優先使用 for_each 而非 count:for_each 使用 key 識別資源,刪除中間元素時不會影響其他資源。
善用 locals 簡化複雜邏輯:將複雜的條件運算放在 locals 區塊,提高可讀性。
避免過度巢狀:深層巢狀的條件式會降低程式碼可讀性,考慮拆分為多個 locals 或模組。
使用有意義的變數名稱:在 for 表達式中使用描述性的迭代變數名稱。
考慮 null 值處理:使用 try() 或 coalesce() 函數處理可能的 null 值。
1
2
3
4
| locals {
instance_type = try(var.custom_type, "t3.micro")
region = coalesce(var.region, "us-east-1")
}
|
參考資料