Terraform:如何动态生成 JSON 策略块?

Terraform:如何动态生成 JSON 策略块?

我有以下资源:

resource "aws_iam_user_policy" "ses_send_policy" {
  count       = var.enabled ? 1 : 0
  name_prefix = var.user_policy_name_prefix
  user        = aws_iam_user.ses_smtp_user[0].name

  policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ses:SendEmail",
                "ses:SendRawEmail"
            ],
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "ses:FromAddress": [
                        "${var.user_email_address}"
                    ]
                }
            }
        }
    ]
}
EOF
}

我想问如何使策略中的“条件”块成为可选的并基于 bool 类型变量?我只想在 时才使用它var.condition = true

答案1

文档aws_iam_user_policy在回答这个问题时,主要用法示例显示policy如下设置:

  # Terraform's "jsonencode" function converts a
  # Terraform expression result to valid JSON syntax.
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          "ec2:Describe*",
        ]
        Effect   = "Allow"
        Resource = "*"
      },
    ]
  })

请注意,它建议使用功能jsonencode来生成整个值,而不是尝试通过模板连接从各个部分构造 JSON,因为这样可以确保结果始终是有效的 JSON 语法。

其优点在于你可以使用任何表达式您需要对数据结构做出动态决策。在您的案例中,您已经从变量中获得了对用户电子邮件地址的动态引用,因此让我们首先将您所拥有的内容转换为更像文档中的示例的形式:

  policy = jsonencode({
    Version = 2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "ses:SendEmail",
          "ses:SendRawEmail",
        ]
        Resource = "*"
        Condition = {
          StringEquals = {
            "ses:FromAddress" = [
              var.user_email_address,
            ]
          }
        }
      }
    ]
  })

请注意,该值现在以 Terraform 自己的表达式语法而不是 JSON 语法编写。Terraform 将在评估函数jsonencode调用时自行构建有效的 JSON 语法。

您的新要求是在某些情况下完全省略。要将该要求描述为 Terraform 表达式,需要将描述始终存在的部分( 、和)Condition的对象与描述可选部分的另一个表达式合并。EffectActionResource

例如:

  policy = jsonencode({
    Version = 2012-10-17"
    Statement = [
      merge(
        {
          Effect = "Allow"
          Action = [
            "ses:SendEmail",
            "ses:SendRawEmail",
          ]
          Resource = "*"
        },
        coalesce(var.condition ? {
          Condition = {
            StringEquals = {
              "ses:FromAddress" = [
                var.user_email_address,
              ]
            }
          }
        } : null, {}),
      )
    ]
  })

我在这里所做的更改有些微妙,并且由于其他内容均未更改,因此可能很难看出,因此这里有一个精简版本,其中一些先前的元素被注释替换,只是为了使新内容更加突出:

  policy = jsonencode({
    Version = 2012-10-17"
    Statement = [
      merge(
        {
          # (common attributes here)
        },
        coalesce(var.condition ? {
          # (conditional attributes here)
        } : null, {}),
      )
    ]
  })

功能merge接受多个对象并返回一个包含所有对象元素的对象。在这种情况下,第二个参数merge是一个更复杂的表达式,它根据条件值生成具有属性的对象Condition或空对象。将空对象合并到对象中不会改变结果,因此在这种情况下,条件控制第二个参数是否会贡献任何属性。

答案2

您可以使用aws_iam_policy_document资源,并使条件块动态的

相关内容