0

要在 AWS 组织中预置标签策略,我需要content从变量构建 JSON。标签策略、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
}
4

3 回答 3

1

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 数据结构。

于 2020-09-16T18:07:02.907 回答
1

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

最后jsonencode()关心将 HCLkey = 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 ) ) ] }
    }
  } ] )
}

编辑这仍然不起作用,因为我忘记了整个 JSON 对象需要包装在tags: {}.

于 2020-09-16T09:37:38.107 回答
0

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

该过程的第一部分是构建地图的地图。主要原因是我不想在变量映射中约定一个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"
    }
  }
}

作为contentJSON

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

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

快速测试:

在块后添加以下output语句resource

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

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

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

专业提示:

  1. output将输出直接通过管道传输到json_pporjq这样您就可以阅读它。
  2. jq .如果您想在顶部进行验证,请使用。如果您看到输出,则表示它是有效的。否则,您应该收到0回复。
于 2020-09-16T22:24:34.163 回答