Terraform 變數與輸出值進階運用

Advanced usage of Terraform variables and outputs including variable types, definitions, validation, sensitive handling and output configurations

在 Terraform 中,變數(Variables)與輸出值(Outputs)是建構可重用、可維護基礎設施程式碼的關鍵元素。本文將深入探討變數的各種類型、定義方式、驗證機制、敏感資料處理,以及輸出值的進階設定。

變數類型(Variable Types)

Terraform 支援多種變數類型,大致可分為基本型別(Primitive Types)複雜型別(Complex Types)

基本型別

型別說明範例
string一組 Unicode 字元組成的文字"Hello, World"
number數值,可以是整數或浮點數15, 6.283185
bool布林值true, false

複雜型別

型別說明範例
list有序的值序列["a", "b", "c"]
set無序且唯一的值集合toset(["a", "b", "c"])
map鍵值對的集合{name = "John", age = 30}
object具有命名屬性的結構化資料object({name = string, age = number})
tuple固定長度、可包含不同型別的序列tuple([string, number, bool])

變數定義方式

基本變數定義

在 Terraform 中,使用 variable 區塊來定義變數:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
variable "instance_type" {
  description = "EC2 執行個體類型"
  type        = string
  default     = "t3.micro"
}

variable "instance_count" {
  description = "要建立的執行個體數量"
  type        = number
  default     = 1
}

variable "enable_monitoring" {
  description = "是否啟用監控"
  type        = bool
  default     = true
}

複雜型別變數定義

 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
30
31
32
33
34
35
# List 型別
variable "availability_zones" {
  description = "可用區域清單"
  type        = list(string)
  default     = ["ap-northeast-1a", "ap-northeast-1c"]
}

# Map 型別
variable "instance_tags" {
  description = "執行個體標籤"
  type        = map(string)
  default = {
    Environment = "production"
    Project     = "web-app"
  }
}

# Object 型別
variable "database_config" {
  description = "資料庫設定"
  type = object({
    engine         = string
    engine_version = string
    instance_class = string
    storage_size   = number
    multi_az       = bool
  })
  default = {
    engine         = "mysql"
    engine_version = "8.0"
    instance_class = "db.t3.medium"
    storage_size   = 100
    multi_az       = true
  }
}

變數賦值方式

Terraform 提供多種方式來為變數賦值:

1. 命令列參數

1
terraform apply -var="instance_type=t3.large"

2. 變數檔案(terraform.tfvars)

1
2
3
4
# terraform.tfvars
instance_type     = "t3.large"
instance_count    = 3
enable_monitoring = true

3. 環境變數

1
2
export TF_VAR_instance_type="t3.large"
terraform apply

4. 自動載入的變數檔案

Terraform 會自動載入以下檔案:

  • terraform.tfvars
  • terraform.tfvars.json
  • *.auto.tfvars
  • *.auto.tfvars.json

變數驗證(Variable Validation)

Terraform 提供變數驗證機制,可在 planapply 階段檢查輸入值是否符合預期規則。

基本驗證

1
2
3
4
5
6
7
8
9
variable "instance_type" {
  description = "EC2 執行個體類型"
  type        = string

  validation {
    condition     = can(regex("^t[23]\\.", var.instance_type))
    error_message = "執行個體類型必須為 t2 或 t3 系列"
  }
}

多重驗證規則

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
variable "database_port" {
  description = "資料庫連接埠"
  type        = number

  validation {
    condition     = var.database_port >= 1024 && var.database_port <= 65535
    error_message = "連接埠必須介於 1024 到 65535 之間"
  }

  validation {
    condition     = var.database_port != 3389
    error_message = "不可使用 RDP 連接埠 3389"
  }
}

複雜型別驗證

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
variable "vpc_config" {
  description = "VPC 設定"
  type = object({
    cidr_block = string
    subnets    = list(string)
  })

  validation {
    condition     = can(cidrhost(var.vpc_config.cidr_block, 0))
    error_message = "CIDR 區塊格式不正確"
  }

  validation {
    condition     = length(var.vpc_config.subnets) >= 2
    error_message = "至少需要定義 2 個子網路"
  }
}

敏感變數處理(Sensitive Variables)

對於 API 金鑰、密碼等敏感資料,Terraform 提供 sensitive 參數來保護這些值不會在 CLI 輸出中顯示。

定義敏感變數

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
variable "db_password" {
  description = "資料庫密碼"
  type        = string
  sensitive   = true
}

variable "api_key" {
  description = "API 金鑰"
  type        = string
  sensitive   = true
}

使用敏感變數

1
2
3
4
5
6
7
8
9
resource "aws_db_instance" "main" {
  identifier     = "production-db"
  engine         = "mysql"
  engine_version = "8.0"
  instance_class = "db.t3.medium"

  username = "admin"
  password = var.db_password  # 敏感值不會顯示在 plan 輸出中
}

重要提醒:雖然 sensitive = true 可以防止值在 CLI 輸出中顯示,但這些值仍會以明文形式儲存在 Terraform 狀態檔案(state file)中。請務必妥善保護您的狀態檔案存取權限。

敏感資料最佳實踐

  1. 使用外部秘密管理工具:如 HashiCorp Vault、AWS Secrets Manager
  2. 加密狀態檔案:使用遠端後端(如 S3)並啟用加密
  3. 限制狀態檔案存取:透過 IAM 政策控制誰可以讀取狀態
1
2
3
4
5
6
7
8
# 使用 AWS Secrets Manager 取得敏感資料
data "aws_secretsmanager_secret_version" "db_password" {
  secret_id = "production/database/password"
}

resource "aws_db_instance" "main" {
  password = data.aws_secretsmanager_secret_version.db_password.secret_string
}

輸出值設定(Output Values)

輸出值用於將模組內部的資訊暴露給外部使用,是模組間共享資料的重要機制。

基本輸出值

1
2
3
4
5
6
7
8
9
output "instance_id" {
  description = "EC2 執行個體 ID"
  value       = aws_instance.main.id
}

output "instance_public_ip" {
  description = "EC2 執行個體公開 IP"
  value       = aws_instance.main.public_ip
}

敏感輸出值

1
2
3
4
5
output "db_connection_string" {
  description = "資料庫連線字串"
  value       = "mysql://${aws_db_instance.main.username}:${aws_db_instance.main.password}@${aws_db_instance.main.endpoint}"
  sensitive   = true
}

使用 depends_on

在某些情況下,您可能需要明確指定輸出值的依賴關係:

1
2
3
4
5
output "web_endpoint" {
  description = "網站端點 URL"
  value       = "https://${aws_instance.web.public_dns}"
  depends_on  = [aws_security_group.web_sg]
}

這在以下情況特別有用:

  • 確保安全群組規則已套用後才輸出連線資訊
  • 等待特定資源完全就緒後才暴露端點

輸出值條件判斷

1
2
3
4
output "load_balancer_dns" {
  description = "負載平衡器 DNS 名稱"
  value       = var.enable_load_balancer ? aws_lb.main[0].dns_name : null
}

複雜輸出值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
output "instance_details" {
  description = "所有執行個體的詳細資訊"
  value = {
    for instance in aws_instance.main :
    instance.tags["Name"] => {
      id         = instance.id
      private_ip = instance.private_ip
      public_ip  = instance.public_ip
    }
  }
}

實際應用範例

以下是一個完整的模組範例,展示變數與輸出值的綜合運用:

 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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# variables.tf
variable "environment" {
  description = "部署環境名稱"
  type        = string

  validation {
    condition     = contains(["dev", "staging", "production"], var.environment)
    error_message = "環境必須為 dev、staging 或 production"
  }
}

variable "app_config" {
  description = "應用程式設定"
  type = object({
    name           = string
    port           = number
    replicas       = number
    container_image = string
  })

  validation {
    condition     = var.app_config.port >= 1 && var.app_config.port <= 65535
    error_message = "連接埠必須在有效範圍內"
  }

  validation {
    condition     = var.app_config.replicas >= 1
    error_message = "至少需要 1 個副本"
  }
}

variable "secrets" {
  description = "應用程式秘密資訊"
  type = object({
    db_password = string
    api_key     = string
  })
  sensitive = true
}

# outputs.tf
output "application_url" {
  description = "應用程式存取 URL"
  value       = "https://${aws_lb.app.dns_name}:${var.app_config.port}"
}

output "deployment_info" {
  description = "部署資訊摘要"
  value = {
    environment = var.environment
    app_name    = var.app_config.name
    replicas    = var.app_config.replicas
    created_at  = timestamp()
  }
}

總結

善用 Terraform 的變數與輸出值功能,可以讓您的基礎設施程式碼更加:

  • 可重用:透過變數參數化設定
  • 安全:使用敏感變數保護機密資料
  • 可靠:透過變數驗證確保輸入值正確
  • 模組化:透過輸出值實現模組間的資料共享

掌握這些進階技巧,將有助於您建構更專業、更易維護的 Terraform 專案。

參考資源

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