Terraform 系列-使用 for-each 对本地 json 进行迭代

这篇具有很好参考价值的文章主要介绍了Terraform 系列-使用 for-each 对本地 json 进行迭代。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

系列文章

  • Terraform 系列文章
  • Grafana 系列文章

概述

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

现在有这么一个现实需求:

有大量的同类型 (type) 的 datasource 需要批量添加,而且这些 datasource 的基本信息是以 json 的格式已经存在。

需要对 json 进行解析/精简/重构等操作并将 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 格式。

该如何实现?🤔

解决方案

通过 Terraform 的 locals jsondecode for 循环 和 for_each 实现。

具体如下:

  • 构造一个 local 变量
  • local 变量从 .json 文件中读取并内容并通过 jsondecode + file 将 json 文件解码为 object
  • 使用 for 循环,将 object 根据当前需求调整,将例子中 env_name 作为 key, 将其他作为 value
  • 批量创建资源时,通过 for_each, 进行批量创建。

基本概念

locals

locals 为 表达式 指定一个名称,所以你可以在一个模块中多次使用这个名称,而不用重复表达式。

如果你熟悉传统的编程语言,把 Terraform 模块比作函数定义可能会很有用:

  • variables(输入变量) 就像函数的参数。
  • outputs(输出值) 就像函数的返回值。
  • locals 就像一个函数的临时本地变量(局部值)。

一旦声明了一个本地值,你可以在 表达式 中以local.<NAME>的形式引用它。

本地值有助于避免在配置中多次重复相同的值或表达式,只有在一个单一的值或结果被用于许多地方的情况下,才可以适度地使用本地值。能够在一个中心位置轻松地改变数值是本地值的关键优势

file 函数

file读取指定路径下的文件内容,并将其作为 string 返回。

> file("${path.module}/hello.txt")
Hello World

jsondecode 函数

jsondecode将一个给定的 string 解释为 JSON,返回该字符串的解码结果。

该函数以如下方式将 JSON 值映射到 Terraform 语言 type:

JSON type Terraform type
String string
Number number
Boolean bool
Object object(...)的属性类型根据此表确定
Array tuple(...)的元素类型根据此表确定
Null Terraform 语言的 null

Terraform 语言的自动类型转换规则意味着你通常不需要担心一个给定的值到底会产生什么类型,只需以直观的方式使用结果即可。

> jsondecode("{\"hello\": \"world\"}")
{
  "hello" = "world"
}
> jsondecode("true")
true

jsonencode 执行相反的操作,将一个 string 编码为 JSON。

for 表达式

一个for表达式通过转换另一个复杂类型的值来创建一个复杂类型的值。输入值中的每个元素可以对应于结果中的一个或零个值,并且可以使用一个任意的表达式来将每个输入元素转化为输出元素。

例如,如果var.list是一个字符串的列表,那么下面的表达式将产生一个全大写字母的字符串的元组:

[for s in var.list : upper(s)]

这个for表达式遍历了var.list中的每个元素,然后评估表达式upper(s),将s设置为每个相应的元素。然后它用所有执行该表达式的结果按相同的顺序建立一个新的元组值。

一个for表达式的输入(在in关键字之后给出)可以是一个列表,一个集合,一个元组,一个 map,或者一个对象 (object)。

上面的例子显示了一个只有一个临时符号sfor表达式,但是一个for表达式可以选择声明一对临时符号,以便也使用每个项目的键或索引:

[for k, v in var.map : length(k) + length(v)]

对于 map 或对象类型,像上面那样,k符号是指当前元素的键或属性名称。你也可以对列表和 map 使用双符号形式,在这种情况下,额外的符号是每个元素的索引,从 0 开始,常规的符号名称是iidx,除非选择一个很有帮助的更具体的名称:

[for i, v in var.list : "${i} is ${v}"]

索引或关键符号总是可选的。如果你在for关键字后面只指定一个符号,那么这个符号将总是代表输入集合的每个元素的值。

for表达式周围的括号的类型决定了它产生的结果的类型。

上面的例子使用[],产生一个元组。如果你用{}代替,结果是一个对象,你必须提供两个结果表达式,用=>符号分开:

{for s in var.list : s => upper(s)}

这个表达式产生一个对象,其属性是来自var.list的原始元素,其相应的值是大写版本。例如,产生的值可能如下:

{
  foo = "FOO"
  bar = "BAR"
  baz = "BAZ"
}

单独的for表达式只能产生一个对象值或一个元组值,但 Terraform 的自动类型转换规则意味着你通常可以在期望使用列表、map 和集合 (set) 的地方使用其结果。

一个 for 表达式也可以包括一个可选的 if 子句来过滤源集合中的元素,产生一个比源值更少元素的值:

[for s in var.list : upper(s) if s != ""]

for表达式中过滤集合的一个常见原因是根据一些标准将一个源集合分成两个独立的集合。例如,如果输入的var.users是一个对象的映射,其中每个对象都有一个属性is_admin,那么你可能希望产生包含管理员和非管理员对象的单独映射:

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)转换为有序类型(列表、元祖),Terraform 必须为无序集合的元素选择一个隐含的排序。

对于 map 和对象,Terraform 通过键或属性名称对元素进行排序,使用词法排序。

对于字符串的集合,Terraform 按其值排序,使用词法排序。

for表达式机制是为了在表达式中从其他集合值中构建集合值,然后你可以将其分配给期待复杂值的单个资源参数。

for_each 元参数

默认情况下,一个 资源块 配置一个真实的基础设施对象(同样,一个 模块块 将一个子模块的内容纳入一次配置)。然而,有时你想管理几个类似的对象(比如一个固定的计算实例池),而不需要为每个对象单独写一个块。Terraform 有两种方法可以做到这一点: countfor_each

如果一个资源或模块块包括一个for_each参数,其值是一个 map 或字符串集合,Terraform 为该 map 或字符串集合的每个成员创建一个实例。

版本说明: for_each是在 Terraform 0.12.6 中添加的。Terraform 0.13 中增加了对for_each 的模块支持;以前的版本只能在资源中使用它。

注意:一个特定的资源或模块块不能同时使用countfor_each

for_each是 Terraform 语言定义的一个元参数。它可以与模块和每一种资源类型一起使用。

for_each 元参数接受一个 map 或字符串集合,并为该 map 或字符串集合的每个项目创建一个实例。每个实例都有一个独特的基础设施对象与之相关联,每个实例都在应用配置时被单独创建、更新或销毁。

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
}

在设置了for_each 的区块中,表达式中还有一个each对象,所以你可以修改每个实例的配置。这个对象有两个属性:

  • each.key - 这个实例对应的 map 键(或集合成员)。
  • each.value - 该实例对应的 map 值。(如果提供了一个集合,这与each.key相同。)

for_each 被设置时,Terraform 区分了区块本身和与之相关的多个资源或模块实例。实例由提供给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.) 代表独立的实例

这与没有countfor_each的资源和模块不同,它们可以在没有索引或键的情况下被引用。

String & Template

字符串是 Terraform 中最复杂的一种文字表达,也是最常用的一种。

Terraform 同时支持字符串的引号语法和 heredoc 语法。这两种语法都支持用于插值和操作文本的模板序列。

带引号的字符串是一系列由双引号字符(")划定的字符。

有两个不使用反斜线的特殊转义序列:

Sequence Replacement
$${ 字面意思是${,不会开始一个插值序列。
%%{ 字面意思是%{,不会开始一个模板指令序列。

${ ... }序列是一个插值,它评估标记之间给出的表达式,如果有必要,将结果转换为字符串,然后将其插入到最终的字符串中:

"Hello, ${var.name}!"

在上面的例子中,命名的对象var.name被访问,其值被插入到字符串中,产生的结果类似 "Hello, Juan!"。

%{ ... } 序列是一个指令,它允许有条件的结果和对集合的迭代,类似于条件和for表达式。

以下指令被支持:

  • %{if <BOOL>}/%{else}/%{endif}指令根据一个 bool 表达式的值在两个模板之间进行选择:

    "Hello, %{ if var.name != "" }${var.name}%{ else }unnamed%{ endif }!"
    

    else部分可以省略,在这种情况下,如果条件表达式返回false,结果就是一个空字符串。

  • %{for <NAME> in <COLLECTION>}/%{endfor}指令在给定的集合或结构值的元素上进行迭代,对每个元素评估一次给定的模板,将结果串联起来:

    <<EOT
    %{ for ip in aws_instance.example.*.private_ip }
    server ${ip}
    %{ endfor }
    EOT
    

实战

需求:

有大量的同类型 (type) 的 datasource 需要批量添加,而且这些 datasource 的基本信息是以 json 的格式已经存在。

需要对 json 进行解析/精简/重构等操作并将 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"
    }
]

解决方案

  • 构造一个 local 变量
  • local 变量从 .json 文件中读取并内容并通过 jsondecode + file 将 json 文件解码为 object
  • 使用 for 循环,将 object 根据当前需求调整,将例子中 env 作为 key, 将其他作为 value
  • 批量创建资源时,通过 for_each, 进行批量创建。

串起来, 最终如下:

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
    }
  }
}

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"
  })
}

完成🎉🎉🎉

📚️参考文档

  • 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

三人行, 必有我师; 知识共享, 天下为公. 本文由东风微鸣技术博客 EWhisper.cn 编写.文章来源地址https://www.toymoban.com/news/detail-499220.html

到了这里,关于Terraform 系列-使用 for-each 对本地 json 进行迭代的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • springboot对本地文件进行操作

    方案一:使用ResourceUtils 这种方案是一个坑,详细内容参考:springboot读写磁盘json格式文件 使用这种方案一般都会提示: java.io.FileNotFoundException 方案二:使用commons-io commons-io中提供了文件的常规读、写工具类 读写文件时需要传入文件所在磁盘路径 参考如下代码 上面方法存在

    2023年04月25日
    浏览(28)
  • list_for_each_entry详解

    参考链接: 终于理解list_entry和list_for_each_entry linux 内核代码中list_for_each_entry宏之我见 linux之list_for_each和list_for_each_entry函数 container_of的用法 用户态下的list.h Linux内核中的许多链表操作,都是使用list_for_each_entry进行遍历,其定义在/usr/src/linux-2.6.32.9/include/linux/list.h路径,具体

    2024年02月15日
    浏览(39)
  • Terraform 系列-Terraform 简介

    👉 Terraform 系列文章 最近在使用 Terraform 来置备 OCI 的 Always Free Tier, 发现它非常好用。总结学习下:Terraform 的基础知识。 Terraform 是一种 基础架构即代码(IaC) 工具,可让您安全高效地构建、更改云和本地资源并对其进行 版本控制 。 HashiCorp Terraform 是一种基础架构即代码工具

    2023年04月12日
    浏览(39)
  • Yolov5对本地视频进行推理时,实现跳帧检测,提高推理效率

    今天在使用Yolov5的detect.py对本地视频进行推理时,发现推理速度受硬件性能影响比较大,为提高检测效率,在部分没必要逐帧检测的情况下,可以考虑设置跳帧检测。 对detect.py观察了一番之后,发现视频读取靠的是dataloaders.py库,于是继续观察。 最终得出了以下解决方案:

    2024年02月12日
    浏览(60)
  • Terraform 系列-Terraform Cloud 比 Terraform OSS 有哪些增强?

    👉 Terraform 系列文章 最近在使用 Terraform Cloud 来置备 OCI 的 Always Free Tier, 发现它非常好用,相比 Terraform OSS, 用起来省心多了。 也借此总结学习下:Terraform Cloud 比 Terraform OSS 有哪些增强,这些增强功能面向哪些客户,解决了哪些痛点? 可以作为我们基于 Terraform 开发自己的

    2023年04月13日
    浏览(38)
  • 「SQL面试题库」 No_123 The Most Recent Orders for Each Product

    「SQL面试题库」是由 不是西红柿 发起,全员免费参与的SQL学习活动。我每天发布1道SQL面试真题,从简单到困难,涵盖所有SQL知识点,我敢保证只要做完这100道题,不仅能轻松搞定面试,代码能力和工作效率也会有明显提升。 1.1 活动流程 整理题目 :西红柿每天无论刮风下雨

    2024年02月15日
    浏览(37)
  • Terraform 系列-什么是 IaC?

    👉 Terraform 系列文章 聊到 Terraform, 必然绕不开 IaC 这个概念?那么,什么是 IaC? 🤔 基础架构即代码 (Infrastructure as Code, IaC) 是指通过 代码 而不是手动流程/控制台点击来管理和配置基础架构。 这里有 2 个: Infrastructure Code Infrastructure 是被管理对象,在这里,主要是指公

    2023年04月11日
    浏览(29)
  • Terraform 系列-批量创建资源时如何根据某个字段判断是否创建

    Terraform 系列文章 Grafana 系列文章 前文 Grafana 系列 - Grafana Terraform Provider 基础 介绍了使用 Grafana Terraform Provider 创建 Datasource. 这几天碰到这么一个现实需求: 使用 Terraform 批量创建日志数据源时, 有的数据源类型是 ElasticSearch, 有些是 Opensearch. 那么, 如何根据某个字段(如: es_t

    2024年02月13日
    浏览(81)
  • 【C++】STL 算法 ③ ( 函数对象中存储状态 | 函数对象作为参数传递时值传递问题 | for_each 算法的 函数对象 参数是值传递 )

    在 C++ 语言中 , 函数对象 / 仿函数 可以像函数一样被调用 , 并且 其 还具有类的特征 , 可以 通过 继承 和 重载 来 修改 重载函数调用操作符函数 的行为 ; 函数对象 / 仿函数 通常是通过 定义一个类 , 然后为这个类 重载 函数调用操作符 () 来实现的 ; 函数对象的一个重要特性是

    2024年02月02日
    浏览(45)
  • 【使用内网穿透从公网对本地内网Web服务器访问】

    随着科技进步和时代发展,计算机及互联网已经深深融入我们的生活和工作,与之对应的,对计算机及网络的探索,让其为我们的生活增添色彩和乐趣,也成为很多人的业余爱好,而自行发布一个网站,就是这一爱好的直接体现。在现代众多软件的帮助下,我们已经可以在本

    2024年02月14日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包