Data Source 概述
在 Terraform 中,Data Source(資料來源)是一個強大的功能,允許我們查詢和使用已存在的基礎設施資源資訊。與 Resource 不同的是,Data Source 只會讀取資料,而不會建立、修改或刪除任何資源。
Data Source 的主要用途包括:
- 查詢現有資源的屬性(如 VPC ID、AMI ID 等)
- 獲取動態資訊(如可用區域列表)
- 跨專案或跨帳號共享資源資訊
- 減少硬編碼,提高配置的靈活性
基本語法
Data Source 的基本語法結構如下:
1
2
3
4
5
6
7
8
9
10
11
12
| data "<PROVIDER>_<TYPE>" "<NAME>" {
# 查詢條件
filter {
name = "attribute-name"
values = ["attribute-value"]
}
}
# 引用 Data Source 的值
output "result" {
value = data.<PROVIDER>_<TYPE>.<NAME>.<ATTRIBUTE>
}
|
與 Resource 的主要差異在於使用 data 關鍵字而非 resource,並且通常需要提供 filter 或其他查詢條件來定位特定資源。
常用 AWS Data Sources
AWS Provider 提供了豐富的 Data Source,以下介紹幾個最常用的類型。
查詢 AMI
查詢最新的 Amazon Linux 2 AMI:
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
| data "aws_ami" "amazon_linux_2" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
filter {
name = "root-device-type"
values = ["ebs"]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux_2.id
instance_type = "t3.micro"
tags = {
Name = "Web Server"
}
}
|
查詢 VPC 和 Subnet
查詢特定標籤的 VPC 及其子網路:
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
| data "aws_vpc" "selected" {
filter {
name = "tag:Environment"
values = ["production"]
}
}
data "aws_subnets" "private" {
filter {
name = "vpc-id"
values = [data.aws_vpc.selected.id]
}
filter {
name = "tag:Type"
values = ["private"]
}
}
# 取得每個子網路的詳細資訊
data "aws_subnet" "private" {
for_each = toset(data.aws_subnets.private.ids)
id = each.value
}
output "private_subnet_cidrs" {
value = [for s in data.aws_subnet.private : s.cidr_block]
}
|
查詢 IAM 資訊
查詢現有的 IAM Role 和 Policy:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| data "aws_iam_role" "existing_role" {
name = "my-existing-role"
}
data "aws_iam_policy" "admin_policy" {
name = "AdministratorAccess"
}
output "role_arn" {
value = data.aws_iam_role.existing_role.arn
}
output "policy_arn" {
value = data.aws_iam_policy.admin_policy.arn
}
|
使用 External Data Source
當內建的 Data Source 無法滿足需求時,可以使用 external data source 執行外部腳本來獲取資料:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| data "external" "git_info" {
program = ["bash", "-c", <<-EOF
echo "{\"branch\": \"$(git rev-parse --abbrev-ref HEAD)\", \"commit\": \"$(git rev-parse --short HEAD)\"}"
EOF
]
}
resource "aws_instance" "app" {
ami = data.aws_ami.amazon_linux_2.id
instance_type = "t3.micro"
tags = {
Name = "App Server"
GitBranch = data.external.git_info.result["branch"]
GitCommit = data.external.git_info.result["commit"]
}
}
|
注意事項:
- 外部程式必須輸出有效的 JSON 格式
- 所有值都必須是字串類型
- 腳本執行失敗會導致 Terraform 執行失敗
Dynamic 區塊使用
dynamic 區塊允許我們動態生成重複的巢狀區塊,常與 Data Source 結合使用:
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
| variable "ingress_rules" {
type = list(object({
port = number
protocol = string
cidr_blocks = list(string)
description = string
}))
default = [
{
port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "HTTP"
},
{
port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "HTTPS"
},
{
port = 22
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"]
description = "SSH from internal"
}
]
}
resource "aws_security_group" "web" {
name = "web-sg"
description = "Security group for web servers"
vpc_id = data.aws_vpc.selected.id
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
description = ingress.value.description
}
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
|
for_each 與 Data Source 結合
將 for_each 與 Data Source 結合,可以實現更靈活的資源部署:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| data "aws_availability_zones" "available" {
state = "available"
}
resource "aws_subnet" "public" {
for_each = toset(slice(data.aws_availability_zones.available.names, 0, 3))
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(aws_vpc.main.cidr_block, 8, index(data.aws_availability_zones.available.names, each.value))
availability_zone = each.value
tags = {
Name = "public-subnet-${each.value}"
Type = "public"
}
}
|
更進階的範例 - 根據標籤動態建立資源:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| locals {
environments = ["dev", "staging", "prod"]
}
data "aws_secretsmanager_secret" "db_credentials" {
for_each = toset(local.environments)
name = "db-credentials-${each.value}"
}
data "aws_secretsmanager_secret_version" "db_credentials" {
for_each = data.aws_secretsmanager_secret.db_credentials
secret_id = each.value.id
}
output "db_secrets" {
value = { for env, secret in data.aws_secretsmanager_secret_version.db_credentials : env => jsondecode(secret.secret_string) }
sensitive = true
}
|
最佳實踐
使用 Data Source 時,請遵循以下最佳實踐:
- 優先使用 Data Source 而非硬編碼:減少環境間的配置差異
- 適當處理查詢失敗的情況:使用
try() 函數或 count 條件 - 注意查詢效能:避免在大型環境中進行過於寬泛的查詢
- 善用 filter:精確的過濾條件可以提高查詢效率
- 結合 locals 使用:將複雜的 Data Source 結果處理邏輯放在 locals 中
參考資料