Terraform 條件式與迴圈進階語法

Terraform Conditionals and Loops Advanced Syntax

條件式與迴圈概述

在 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
}

最佳實踐

  1. 優先使用 for_each 而非 count:for_each 使用 key 識別資源,刪除中間元素時不會影響其他資源。

  2. 善用 locals 簡化複雜邏輯:將複雜的條件運算放在 locals 區塊,提高可讀性。

  3. 避免過度巢狀:深層巢狀的條件式會降低程式碼可讀性,考慮拆分為多個 locals 或模組。

  4. 使用有意義的變數名稱:在 for 表達式中使用描述性的迭代變數名稱。

  5. 考慮 null 值處理:使用 try() 或 coalesce() 函數處理可能的 null 值。

1
2
3
4
locals {
  instance_type = try(var.custom_type, "t3.micro")
  region        = coalesce(var.region, "us-east-1")
}

參考資料

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy