系列文章
- Terraform 系列文章
- Grafana 系列文章
概述
前文 Grafana 系列 - Grafana Terraform Provider 基礎 介紹了使用 Grafana Terraform Provider 創建 Datasource.
這幾天碰到這么一個現實需求:
使用 Terraform 批量創建日志數據源時, 有的數據源類型是 ElasticSearch, 有些是 Opensearch. 那么, 如何根據某個字段(如:es_type
)判斷是否創建?
另外, 建議您先閱讀前一篇文章: Terraform 系列 - 使用 for-each 對本地 json 進行迭代 方便快速了解上下文背景.
創建數據源的數據來源是個 json, json 通過前一篇文章的轉換, 格式類似于這樣:
{"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
判斷是否創建.
具體如下:
locals {# 將 json 文件轉換為 對象 user_data = jsondecode(file("${path.module}/env-details.json"))# 構造一個 map# key 是 env_name# value 又是一個 map, 其 key 是 grafana datasource type, value 是 urlenvs = { for env in local.user_data : env.env_name =>{prometheus = env.prom_url# 利用 ${} 構造新的 urljaeger = "${env.jaeger_url}/trace/"es = env.es_urles_type = env.es_type}}
}resource "grafana_data_source" "elasticsearch" {for_each = {for env_name, env_info in local.envs : env_name => env_infoif env_info.es_type == "elasticsearch"}type = "elasticsearch"name = "${each.key}_es"uid = "${each.key}_es"url = each.value.esdatabase_name = "[example.*-]YYYY.MM.DD"json_data_encoded = jsonencode({esVersion = "6.0.0"interval = "Daily"includeFrozen = falsemaxConcurrentShardRequests = 256timeField = "@timestamp"logLevelField = "level"logMessageField = "message"})
}resource "grafana_data_source" "opensearch" {for_each = {for env_name, env_info in local.envs : env_name => env_infoif env_info.es_type == "opensearch"}type = "grafana-opensearch-datasource"name = "${each.key}_opensearch"uid = "${each.key}_opensearch"url = each.value.esbasic_auth_enabled = truebasic_auth_username = "readonly"json_data_encoded = jsonencode({database = "[example.*-]YYYY.MM.DD"version = "6.8.0"flavor = "elasticsearch"interval = "Daily"pplEnabled = truemaxConcurrentShardRequests = 256timeField = "@timestamp"logLevelField = "level"logMessageField = "message"})secure_json_data_encoded = jsonencode({basicAuthPassword = "Changeme!"})
}
不要看到這么長的代碼就頭暈, 很多跟本次沒啥關系. 實現的關鍵就在于如下代碼段:
for_each = {for env_name, env_info in local.envs : env_name => env_infoif env_info.es_type == "elasticsearch"}
還是很直白易懂的, 就不詳細說明了. 如果 es_type
是 elasticsearch
, 才把這個對象構造到 map 中.
之后, 對于不同的 DataSource type, 會有不同的參數, 如上文:
- Opensearch 具有和 ES 不同的
type
, Opensearch 加了認證 - Opensearch 里是
database
字段而不是database_name
- Opensearch 里額外還有
flavor
字段和pplEnabled
字段.
解決方案二
如果您的原始數據, 或者構造后的 locals
是 list
而不是 map
.
那么也可以使用: count
+ condition ? true_val : false_val
條件表達式完成同樣的功能.
示例如下:
通過 var.cloudflare
的值是 true
還是 false
來判斷.
resource "cloudflare_record" "record" {count = var.cloudflare ? 1 : 0zone_id = "${data.cloudflare_zones.domain.zones[0].id}"name = "${var.subdomain}"value = "${var.origin_server}"type = "CNAME"ttl = 1proxied = true
}
關鍵點是: count = var.cloudflare ? 1 : 0
條件表達式.
也很清晰明了.
完成🎉🎉🎉
📚?參考文檔
- Terraform: Conditional creation of a resource based on a variable in .tfvars - Stack Overflow
- Conditionally create resources when a for_each loop is involved - Terraform - HashiCorp Discuss
三人行, 必有我師; 知識共享, 天下為公. 本文由東風微鳴技術博客 EWhisper.cn 編寫.