Terraform 系列 - 批量创建资源时如何根据某个字段判断是否创建

本文最后更新于:2023年8月11日 下午

系列文章

概述

前文 Grafana 系列 - Grafana Terraform Provider 基础 介绍了使用 Grafana Terraform Provider 创建 Datasource.

这几天碰到这么一个现实需求:

使用 Terraform 批量创建日志数据源时, 有的数据源类型是 ElasticSearch, 有些是 Opensearch. 那么, 如何根据某个字段 (如:es_type) 判断是否创建?

另外, 建议您先阅读前一篇文章: Terraform 系列 - 使用 for-each 对本地 json 进行迭代 方便快速了解上下文背景.

创建数据源的数据来源是个 json, json 通过前一篇文章的转换, 格式类似于这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"dev":
{
"env_name": "dev",
"prom_url": "http://dev-prom.example.com",
"jaeger_url": "http://dev-jaeger.example.com",
"es_url": "http://dev-es.example.com:9200",
"es_type": "elasticsearch"
},
"test":
{
"env_name": "test",
"prom_url": "http://test-prom.example.com",
"jaeger_url": "http://test-jaeger.example.com",
"es_url": "http://test-es.example.com:9200",
"es_type": "opensearch"
}
}

该如何实现?🤔

解决方案

使用: for 循环 + if 重构 map.

具体如下:

  • 批量创建资源时,通过 for_each, 进行批量创建。
  • 但是在 for_each 时, 通过 for 循环 + if 重构 map, 通过 local.env.es_type 判断是否创建.

具体如下:

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
locals {
# 将 json 文件转换为 对象
user_data = jsondecode(file("${path.module}/env-details.json"))
# 构造一个 map
# key 是 env_name
# value 又是一个 map, 其 key 是 grafana datasource type, value 是 url
envs = { for env in local.user_data : env.env_name =>
{
prometheus = env.prom_url
# 利用 ${} 构造新的 url
jaeger = "${env.jaeger_url}/trace/"
es = env.es_url
es_type = env.es_type
}
}
}


resource "grafana_data_source" "elasticsearch" {
for_each = {
for env_name, env_info in local.envs : env_name => env_info
if env_info.es_type == "elasticsearch"
}

type = "elasticsearch"
name = "${each.key}_es"
uid = "${each.key}_es"
url = each.value.es
database_name = "[example.*-]YYYY.MM.DD"

json_data_encoded = jsonencode({
esVersion = "6.0.0"

interval = "Daily"
includeFrozen = false
maxConcurrentShardRequests = 256
timeField = "@timestamp"

logLevelField = "level"
logMessageField = "message"
})
}

resource "grafana_data_source" "opensearch" {
for_each = {
for env_name, env_info in local.envs : env_name => env_info
if env_info.es_type == "opensearch"
}

type = "grafana-opensearch-datasource"
name = "${each.key}_opensearch"
uid = "${each.key}_opensearch"
url = each.value.es
basic_auth_enabled = true
basic_auth_username = "readonly"

json_data_encoded = jsonencode({
database = "[example.*-]YYYY.MM.DD"
version = "6.8.0"
flavor = "elasticsearch"

interval = "Daily"
pplEnabled = true
maxConcurrentShardRequests = 256
timeField = "@timestamp"

logLevelField = "level"
logMessageField = "message"
})

secure_json_data_encoded = jsonencode({
basicAuthPassword = "Changeme!"
})
}

不要看到这么长的代码就头晕, 很多跟本次没啥关系. 实现的关键就在于如下代码段:

1
2
3
4
for_each = {
for env_name, env_info in local.envs : env_name => env_info
if env_info.es_type == "elasticsearch"
}

还是很直白易懂的, 就不详细说明了. 如果 es_typeelasticsearch, 才把这个对象构造到 map 中.

之后, 对于不同的 DataSource type, 会有不同的参数, 如上文:

  • Opensearch 具有和 ES 不同的 type, Opensearch 加了认证
  • Opensearch 里是 database 字段而不是 database_name
  • Opensearch 里额外还有 flavor 字段和 pplEnabled 字段.

解决方案二

如果您的原始数据, 或者构造后的 localslist 而不是 map.

那么也可以使用: count + condition ? true_val : false_val 条件表达式完成同样的功能.

示例如下:

通过 var.cloudflare 的值是 true 还是 false 来判断.

1
2
3
4
5
6
7
8
9
resource "cloudflare_record" "record" {
count = var.cloudflare ? 1 : 0
zone_id = "${data.cloudflare_zones.domain.zones[0].id}"
name = "${var.subdomain}"
value = "${var.origin_server}"
type = "CNAME"
ttl = 1
proxied = true
}

关键点是: count = var.cloudflare ? 1 : 0 条件表达式.

也很清晰明了.

完成🎉🎉🎉

📚️参考文档


Terraform 系列 - 批量创建资源时如何根据某个字段判断是否创建
https://ewhisper.cn/posts/2789/
作者
东风微鸣
发布于
2023年8月11日
许可协议