系列文章
- Terraform 系列文章
- Grafana 系列文章
概述
前文 Grafana 系列 - Grafana Terraform Provider 基礎(chǔ) 介紹了使用 Grafana Terraform Provider 創(chuàng)建 Datasource.
現(xiàn)在有這么一個現(xiàn)實需求:
有大量的同類型 (type) 的 datasource 需要批量添加,而且這些 datasource 的基本信息是以 json 的格式已經(jīng)存在。
需要對 json 進行解析/精簡/重構(gòu)等操作并將 json 作為 Terraform 的 datasource.
Json 的格式可能類似于這樣:
[
{
"env_name": "dev",
"prom_url": "http://dev-prom.example.com",
"es_url": "http://dev-es.example.com:9200",
"jaeger_url": "http://dev-jaeger.example.com"
},
{
"env_name": "test",
"prom_url": "http://test-prom.example.com",
"es_url": "http://test-es.example.com:9200",
"jaeger_url": "http://test-jaeger.example.com"
}
]
??Notes:
舉一反三,后面的解決方案也適用于其他任意 Json 格式。
該如何實現(xiàn)???
解決方案
通過 Terraform 的 locals
jsondecode
for
循環(huán) 和 for_each
實現(xiàn)。
具體如下:
- 構(gòu)造一個 local 變量
- local 變量從 .json 文件中讀取并內(nèi)容并通過
jsondecode
+file
將 json 文件解碼為 object - 使用
for
循環(huán),將 object 根據(jù)當前需求調(diào)整,將例子中env_name
作為 key, 將其他作為 value - 批量創(chuàng)建資源時,通過
for_each
, 進行批量創(chuàng)建。
基本概念
locals
locals
為 表達式 指定一個名稱,所以你可以在一個模塊中多次使用這個名稱,而不用重復表達式。
如果你熟悉傳統(tǒng)的編程語言,把 Terraform 模塊比作函數(shù)定義可能會很有用:
- variables(輸入變量) 就像函數(shù)的參數(shù)。
- outputs(輸出值) 就像函數(shù)的返回值。
-
locals
就像一個函數(shù)的臨時本地變量(局部值)。
一旦聲明了一個本地值,你可以在 表達式 中以local.<NAME>
的形式引用它。
本地值有助于避免在配置中多次重復相同的值或表達式,只有在一個單一的值或結(jié)果被用于許多地方的情況下,才可以適度地使用本地值。能夠在一個中心位置輕松地改變數(shù)值是本地值的關(guān)鍵優(yōu)勢。
file 函數(shù)
file
讀取指定路徑下的文件內(nèi)容,并將其作為 string 返回。
> file("${path.module}/hello.txt")
Hello World
jsondecode 函數(shù)
jsondecode
將一個給定的 string 解釋為 JSON,返回該字符串的解碼結(jié)果。
該函數(shù)以如下方式將 JSON 值映射到 Terraform 語言 type:
JSON type | Terraform type |
---|---|
String | string |
Number | number |
Boolean | bool |
Object |
object(...) 的屬性類型根據(jù)此表確定 |
Array |
tuple(...) 的元素類型根據(jù)此表確定 |
Null | Terraform 語言的 null 值 |
Terraform 語言的自動類型轉(zhuǎn)換規(guī)則意味著你通常不需要擔心一個給定的值到底會產(chǎn)生什么類型,只需以直觀的方式使用結(jié)果即可。
> jsondecode("{\"hello\": \"world\"}")
{
"hello" = "world"
}
> jsondecode("true")
true
jsonencode
執(zhí)行相反的操作,將一個 string 編碼為 JSON。
for 表達式
一個for
表達式通過轉(zhuǎn)換另一個復雜類型的值來創(chuàng)建一個復雜類型的值。輸入值中的每個元素可以對應于結(jié)果中的一個或零個值,并且可以使用一個任意的表達式來將每個輸入元素轉(zhuǎn)化為輸出元素。
例如,如果var.list
是一個字符串的列表,那么下面的表達式將產(chǎn)生一個全大寫字母的字符串的元組:
[for s in var.list : upper(s)]
這個for
表達式遍歷了var.list
中的每個元素,然后評估表達式upper(s)
,將s
設(shè)置為每個相應的元素。然后它用所有執(zhí)行該表達式的結(jié)果按相同的順序建立一個新的元組值。
一個for
表達式的輸入(在in
關(guān)鍵字之后給出)可以是一個列表,一個集合,一個元組,一個 map,或者一個對象 (object)。
上面的例子顯示了一個只有一個臨時符號s
的for
表達式,但是一個for
表達式可以選擇聲明一對臨時符號,以便也使用每個項目的鍵或索引:
[for k, v in var.map : length(k) + length(v)]
對于 map 或?qū)ο箢愋?,像上面那樣?code>k符號是指當前元素的鍵或?qū)傩悦Q。你也可以對列表和 map 使用雙符號形式,在這種情況下,額外的符號是每個元素的索引,從 0 開始,常規(guī)的符號名稱是i
或idx
,除非選擇一個很有幫助的更具體的名稱:
[for i, v in var.list : "${i} is ${v}"]
索引或關(guān)鍵符號總是可選的。如果你在for
關(guān)鍵字后面只指定一個符號,那么這個符號將總是代表輸入集合的每個元素的值。
for
表達式周圍的括號的類型決定了它產(chǎn)生的結(jié)果的類型。
上面的例子使用[
和]
,產(chǎn)生一個元組。如果你用{
和}
代替,結(jié)果是一個對象,你必須提供兩個結(jié)果表達式,用=>
符號分開:
{for s in var.list : s => upper(s)}
這個表達式產(chǎn)生一個對象,其屬性是來自var.list
的原始元素,其相應的值是大寫版本。例如,產(chǎn)生的值可能如下:
{
foo = "FOO"
bar = "BAR"
baz = "BAZ"
}
單獨的for
表達式只能產(chǎn)生一個對象值或一個元組值,但 Terraform 的自動類型轉(zhuǎn)換規(guī)則意味著你通??梢栽谄谕褂昧斜?、map 和集合 (set) 的地方使用其結(jié)果。
一個 for
表達式也可以包括一個可選的 if
子句來過濾源集合中的元素,產(chǎn)生一個比源值更少元素的值:
[for s in var.list : upper(s) if s != ""]
在for
表達式中過濾集合的一個常見原因是根據(jù)一些標準將一個源集合分成兩個獨立的集合。例如,如果輸入的var.users
是一個對象的映射,其中每個對象都有一個屬性is_admin
,那么你可能希望產(chǎn)生包含管理員和非管理員對象的單獨映射:
variable "users" {
type = map(object({
is_admin = bool
}))
}
locals {
admin_users = {
for name, user in var.users : name => user
if user.is_admin
}
regular_users = {
for name, user in var.users : name => user
if !user.is_admin
}
}
因為for
表達式可以從無序類型(map、對象、集合 set)轉(zhuǎn)換為有序類型(列表、元祖),Terraform 必須為無序集合的元素選擇一個隱含的排序。
對于 map 和對象,Terraform 通過鍵或?qū)傩悦Q對元素進行排序,使用詞法排序。
對于字符串的集合,Terraform 按其值排序,使用詞法排序。
for
表達式機制是為了在表達式中從其他集合值中構(gòu)建集合值,然后你可以將其分配給期待復雜值的單個資源參數(shù)。
for_each 元參數(shù)
默認情況下,一個 資源塊 配置一個真實的基礎(chǔ)設(shè)施對象(同樣,一個 模塊塊 將一個子模塊的內(nèi)容納入一次配置)。然而,有時你想管理幾個類似的對象(比如一個固定的計算實例池),而不需要為每個對象單獨寫一個塊。Terraform 有兩種方法可以做到這一點: count
和 for_each
。
如果一個資源或模塊塊包括一個for_each
參數(shù),其值是一個 map 或字符串集合,Terraform 為該 map 或字符串集合的每個成員創(chuàng)建一個實例。
版本說明: for_each
是在 Terraform 0.12.6 中添加的。Terraform 0.13 中增加了對for_each
的模塊支持;以前的版本只能在資源中使用它。
注意:一個特定的資源或模塊塊不能同時使用count
和for_each
。
for_each
是 Terraform 語言定義的一個元參數(shù)。它可以與模塊和每一種資源類型一起使用。
for_each
元參數(shù)接受一個 map 或字符串集合,并為該 map 或字符串集合的每個項目創(chuàng)建一個實例。每個實例都有一個獨特的基礎(chǔ)設(shè)施對象與之相關(guān)聯(lián),每個實例都在應用配置時被單獨創(chuàng)建、更新或銷毀。
Map:
resource "azurerm_resource_group" "rg" {
for_each = {
a_group = "eastus"
another_group = "westus2"
}
name = each.key
location = each.value
}
字符串集合:
resource "aws_iam_user" "the-accounts" {
for_each = toset( ["Todd", "James", "Alice", "Dottie"] )
name = each.key
}
在設(shè)置了for_each
的區(qū)塊中,表達式中還有一個each
對象,所以你可以修改每個實例的配置。這個對象有兩個屬性:
-
each.key
- 這個實例對應的 map 鍵(或集合成員)。 -
each.value
- 該實例對應的 map 值。(如果提供了一個集合,這與each.key
相同。)
當 for_each
被設(shè)置時,Terraform 區(qū)分了區(qū)塊本身和與之相關(guān)的多個資源或模塊實例。實例由提供給for_each
的值中的一個 map 鍵(或集合成員)來識別。
-
<TYPE>.<NAME>
或module.<NAME>
(例如,azurerm_resource_group.rg
) 代表這個塊。 -
<TYPE>.<NAME>[<KEY>]
或module.<NAME>[<KEY>]
(例如,azurerm_resource_group.rg["a_group"]
,azurerm_resource_group.rg["another_group"]
, etc.) 代表獨立的實例
這與沒有count
或for_each
的資源和模塊不同,它們可以在沒有索引或鍵的情況下被引用。
String & Template
字符串是 Terraform 中最復雜的一種文字表達,也是最常用的一種。
Terraform 同時支持字符串的引號語法和 heredoc
語法。這兩種語法都支持用于插值和操作文本的模板序列。
帶引號的字符串是一系列由雙引號字符("
)劃定的字符。
有兩個不使用反斜線的特殊轉(zhuǎn)義序列:
Sequence | Replacement |
---|---|
$${ |
字面意思是${ ,不會開始一個插值序列。 |
%%{ |
字面意思是%{ ,不會開始一個模板指令序列。 |
${ ... }
序列是一個插值,它評估標記之間給出的表達式,如果有必要,將結(jié)果轉(zhuǎn)換為字符串,然后將其插入到最終的字符串中:
"Hello, ${var.name}!"
在上面的例子中,命名的對象var.name
被訪問,其值被插入到字符串中,產(chǎn)生的結(jié)果類似 "Hello, Juan!"。
%{ ... }
序列是一個指令,它允許有條件的結(jié)果和對集合的迭代,類似于條件和for
表達式。
以下指令被支持:
-
%{if <BOOL>}
/%{else}
/%{endif}
指令根據(jù)一個 bool 表達式的值在兩個模板之間進行選擇:"Hello, %{ if var.name != "" }${var.name}%{ else }unnamed%{ endif }!"
else
部分可以省略,在這種情況下,如果條件表達式返回false
,結(jié)果就是一個空字符串。 -
%{for <NAME> in <COLLECTION>}
/%{endfor}
指令在給定的集合或結(jié)構(gòu)值的元素上進行迭代,對每個元素評估一次給定的模板,將結(jié)果串聯(lián)起來:<<EOT %{ for ip in aws_instance.example.*.private_ip } server ${ip} %{ endfor } EOT
實戰(zhàn)
需求:
有大量的同類型 (type) 的 datasource 需要批量添加,而且這些 datasource 的基本信息是以 json 的格式已經(jīng)存在。
需要對 json 進行解析/精簡/重構(gòu)等操作并將 json 作為 Terraform 的 datasource.
Json 的格式可能類似于這樣:
[
{
"env_name": "dev",
"prom_url": "http://dev-prom.example.com",
"es_url": "http://dev-es.example.com:9200",
"jaeger_url": "http://dev-jaeger.example.com"
},
{
"env_name": "test",
"prom_url": "http://test-prom.example.com",
"es_url": "http://test-es.example.com:9200",
"jaeger_url": "http://test-jaeger.example.com"
}
]
解決方案:
- 構(gòu)造一個 local 變量
- local 變量從 .json 文件中讀取并內(nèi)容并通過
jsondecode
+file
將 json 文件解碼為 object - 使用
for
循環(huán),將 object 根據(jù)當前需求調(diào)整,將例子中env
作為 key, 將其他作為 value - 批量創(chuàng)建資源時,通過
for_each
, 進行批量創(chuàng)建。
串起來, 最終如下:
locals {
# 將 json 文件轉(zhuǎn)換為 對象
user_data = jsondecode(file("${path.module}/env-details.json"))
# 構(gòu)造一個 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
# 利用 ${} 構(gòu)造新的 url
jaeger = "${env.jaeger_url}/trace/"
es = env.es_url
}
}
}
resource "grafana_data_source" "prometheus" {
# 通過 for_each 迭代
for_each = local.envs
type = "prometheus"
name = "${each.key}_prom"
uid = "${each.key}_prom"
url = each.value.prometheus
json_data_encoded = jsonencode({
httpMethod = "POST"
})
}
resource "grafana_data_source" "jaeger" {
for_each = local.envs
type = "jaeger"
name = "${each.key}_jaeger"
uid = "${each.key}_jaeger"
url = each.value.jaeger
}
resource "grafana_data_source" "elasticsearch" {
for_each = local.envs
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"
})
}
完成??????文章來源:http://www.zghlxwxcb.cn/news/detail-499220.html
???參考文檔
- Overview - Configuration Language | Terraform | HashiCorp Developer
- Terraform: Using for-each in Terraform to iterate through local JSON (copyprogramming.com)
- automation - Iterate over Json using Terraform - Stack Overflow
- Using data returned by jsondecode and iterate over the results in a for_each loop - Terraform - HashiCorp Discuss
- How to Use Terraform's 'for_each', with Examples - The New Stack
三人行, 必有我?guī)? 知識共享, 天下為公. 本文由東風微鳴技術(shù)博客 EWhisper.cn 編寫.文章來源地址http://www.zghlxwxcb.cn/news/detail-499220.html
到了這里,關(guān)于Terraform 系列-使用 for-each 對本地 json 進行迭代的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!