【问题标题】:Terraform 0.12 AWS resource containing JSON built from variableTerraform 0.12 AWS 资源,包含从变量构建的 JSON
【发布时间】:2021-01-03 02:51:46
【问题描述】:

要在 AWS 组织中预置标签策略,我需要从变量构建 JSON content。对标签策略、scp 等的管理应该是集中的,因此可以在任何地方应用更改:重命名、添加、删除标签等。

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
    }
  }
}
provider "aws" {
  profile = "default"
  region  = "us-west-1"
}

我面临的问题是:我将如何构建 JSON 对象?

示例变量/标签映射:

# tag_policies.tf
variable "resource_tags" {
  description = "Central resource tags"
  type = list( object( {
    name = string
    tags = map(string)
  } ) )
  default = [
    {
      name = "Environment"
      tags = {
        prod = "crn::env:prod"
        lab = "crn::env:lab"
        dev = "crn::env:dev"
      }
    }
  ]
}

到目前为止,我尝试使用 HCL 模板标签,但在遍历标签名称映射时,我最终得到了一个 , 逗号太多。这适用于带有标记名称子映射的join(),但如果我尝试包装模板标记,则不会锻炼。 我为什么要尝试这个?因为我的想法已经用完了。

# vars.tf
resource "aws_organizations_policy" "root-tag-policy" {
  name = "RootTagPolicy"
  type = "TAG_POLICY"

  content = <<CONTENT
{
  "tags": {
    %{ for tag in var.resource_tags_env ~}
      "${tag.name}": {
        "tag_key": {
          "@@assign": "${tag.name}",
          "@@operators_allowed_for_child_policies": [ "@@none" ]
        },
        "tag_value": { "@@assign": [ "${join( ", ", values( tag.tags ) )}" ] }
      },
    %{ endfor ~}
  }
}
CONTENT
}

【问题讨论】:

    标签: json amazon-web-services terraform terraform-provider-aws hcl


    【解决方案1】:

    解决方案实际上非常简单:使用for expression 迭代标签并用花括号{ … } 将其括起来以返回一个对象(=&gt; 返回元组)。

    最后 jsonencode() 关心将 HCL key = value 语法转换为正确的 JSON。

    resource "aws_organizations_policy" "root-tag-policy" {
      name = "RootTagPolicy"
      type = "TAG_POLICY"
    
      content = jsonencode( [ for key, tag in var.resource_tags: {
        "${tag.name}" = {
          "tag_key" = {
            "@@assign" = tag.name,
            "@@operators_allowed_for_child_policies" = [ "@@none" ]
          },
          "tag_value" = { "@@assign" = [ join( ", ", values( tag.tags ) ) ] }
        }
      } ] )
    }
    

    EDIT这仍然不起作用,因为我忘记了整个 JSON 对象需要被包裹在 tags: {} 中。

    【讨论】:

      【解决方案2】:

      kaiser 的回答展示了一个很好的通用方法:构建一个合适的数据结构,然后将其传递给 jsonencode 以从中获取有效的 JSON 字符串。

      这是一个我认为与原始问题中的字符串模板所产生的匹配的示例:

        content = jsonencode({
          tags = {
            for tag in var.resource_tags_env : tag.name => {
              tag_key = {
                "@@assign" = tag.name
                "@@operators_allowed_for_child_policies" = ["@@none"]
              }
              tag_value = {
                "@@assign" = values(tag.tags)
              }
            }
          }
        })
      

      我不熟悉 aws_organizations_policy 资源类型,所以如果我在这里弄错了一些细节,我很抱歉,但希望您可以修改上面的示例来生成您需要的 JSON 数据结构。

      【讨论】:

      • 感谢您的光临并回答,马丁!你把我推到了阅读文档无法理解的缺失部分:key =&gt; { …data… } 它是。我将添加另一个答案来展示我最终是如何解决它的(不使用name 键)。没有你的回答,不可能做到这一点!
      【解决方案3】:

      在阅读了@martin-atkins 的回答后,我终于明白了for 是如何为objectsmaps 工作的。 var before =&gt; 箭头实际上是结果对象的一部分。 (当我将它与其他语言的箭头函数和参数进行比较时,这让我非常困惑。)

      该过程的第一部分是构建地图的地图。主要原因是我不想在变量映射中使用name 键的约定。这可能会导致稍后处理约定,应该不惜一切代价避免这种情况,因为如果一个人不密切注意或意识到这一点,这可能是一个陷阱。所以key 现在实际上是name

      数据结构

      variable "resource_tags" {
        description = "Central resource tags"
        type = map(
          map(string)
        )
        default = {
          Environment = {
            common = "grn::env:common"
            prod = "grn::env:prod"
            stage = "grn::env:stage"
            dev = "grn::env:dev"
            demo = "grn::env:demo"
            lab = "grn::env:lab"
          },
          Foo = {
            bar = "baz"
          }
        }
      }
      

      content 作为 JSON 格式

      在了解{ "tags": { … } }中的key只是=&gt;之前的部分之后,我可以将最终资源减少到以下块。

      resource "aws_organizations_policy" "root-tag-policy" {
        name = "RootTagPolicy"
        description = "Tag policies, assigned to the root org."
        type = "TAG_POLICY"
      
        content = jsonencode({
          tags = {
            for key, tags in var.resource_tags : key => {
              tag_key = {
                "@@assign" = key
                "@@operators_allowed_for_child_policies" = ["@@none"]
              }
              tag_value = {
                "@@assign" = values( tags )
              }
            }
          }
        })
      }
      

      快速测试:

      resource 块之后添加以下output 语句:

      output "debug" {
        value = aws_organizations_policy.tp_root-tag-policy.content
      }
      

      现在apply(或planrefresh)只是这个资源。这种方式更快。然后从applyrefresh 运行输出构建的debug

      $ terraform apply -target=aws_organizations_policy.root-tag-policy
      …things happening…
      $ terraform output debug | json_pp
      

      专业提示:

      1. output 的输出直接导入json_ppjq,以便您阅读。
      2. 如果您想在顶部进行验证,请使用jq .。如果您看到输出,则表示它是有效的。否则,您应该收到 0 作为回复。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-12-03
        • 2019-05-19
        • 1970-01-01
        • 2022-10-13
        • 1970-01-01
        • 2020-04-16
        相关资源
        最近更新 更多