Terraform 在数据中使用 each.value.policy_name 来动态检索特定策略

Terraform use each.value.policy_name in data to retrieve specific policy dynamically

我想自动创建 IAM 角色和策略并分别将策略附加到角色:

variables.tf

variable "roles" {
type = map(object({
role_name        = string
role_description = string
policies         = map(object({ policy_name = string, policy_description = string  }))
 })
)}

terraform.tfvars

roles = {
"aws-config-role-1" = {
role_name        = "aws-config-s3"
role_description = "Custom AWSConfig Service Role for the Recorder to record s3 only"
policies = {
  "s3" = {
    policy_name        = "s3",
    policy_description = "Custom policy for AWSConfigRecorder Service Role to allow record only S3 resources"
  },
  "policy" = {
    policy_name        = "policy",
    policy_description = "Custom policy for AWSConfigRecorder Service Role"
  }
}
policy_description = "S3 Policy to get list of all s3 buckets in the account"
}
 "aws-config-role-2" = {
role_name        = "aws-config-ebs"
role_description = "Custom AWSConfig Service Role for the Recorder to allow record only ec2 ebs resources"
policies = {
  "ebs" = {
    policy_name        = "ebs",
    policy_description = "Custom policy for AWSConfigRecorder Service Role to record ebs volumes"
  }
}
policy_description = "EBS Policy to get list of all ec2 ebs volumes in the account"
}
}

每个角色可以有不同数量的策略,在我的示例中 aws-config-role-1 有 2 个策略(s3 和策略)和 aws- config-role-2 只有 1 个策略 (ebs)

现在我需要使用 locals 和 flatten 函数,所以每个角色都有一个策略列表

locals.tf

locals {
policies = flatten([
for role_key, role in var.roles : [
  for policy_key, policy in role.policies : {
    role_key  = role_key
    role_name = role.role_name
    role_description = role.role_description
    policy_key       = policy_key
    policy_name      = policy.policy_name
    policy_description = policy.policy_description
  }
]
])
}

在 terraform 控制台中:

> local.policies
[
{
"policy_description" = "Custom policy for AWSConfigRecorder Service Role"
"policy_key" = "policy"
"policy_name" = "policy"
"role_description" = "Custom AWSConfig Service Role for the Recorder to record s3 only"
"role_key" = "aws-config-role-1"
"role_name" = "aws-config-s3"
},
{
"policy_description" = "Custom policy for AWSConfigRecorder"
"policy_key" = "s3"
"policy_name" = "s3"
"role_description" = "Custom AWSConfig Role for s3"
"role_key" = "aws-config-role-1"
"role_name" = "aws-config-s3"
},
{
"policy_description" = "Custom policy for AWSConfigRecorder"
"policy_key" = "ebs"
"policy_name" = "ebs"
"role_description" = "Custom AWSConfig Role for ebs"
"role_key" = "aws-config-role-2"
"role_name" = "aws-config-ebs"
},
]

创建角色和策略

roles.tf

resource "aws_iam_role" "this" {
for_each           = var.roles
name               = "${var.project}-${var.env}-${each.value["role_name"]}-role"
path               = "/${var.project}/${var.module_name}/"
description        = each.value["role_description"]
assume_role_policy = <<POLICY
{
 "Version": "2012-10-17",
 "Statement": [
{
  "Action": "sts:AssumeRole",
  "Principal": {
    "Service": "config.amazonaws.com"
  },
  "Effect": "Allow",
  "Sid": ""
 }
]
}
POLICY
}

然后我制定政策

resource "aws_iam_policy" "this" {
for_each = {
for policy in local.policies : "${policy.role_key}.${policy.policy_name}" => policy
}
name   = "${var.project}-${var.env}-${each.value.policy_name}-Policy"
policy = "data.aws_iam_policy_document.${each.value.policy_name}.json"
path   = "/${var.project}/${var.module_name}/"
description = each.value.policy_description
}

和 data.tf 定义了所有策略

data "aws_iam_policy_document" "s3" {
statement {
sid    = "GetListS3"
effect = "Allow"
actions = [
  "s3:GetAccelerateConfiguration",
  "s3:GetAccessPoint",
  "s3:GetAccessPointPolicy",
  "s3:GetAccessPointPolicyStatus",
  "s3:GetAccountPublicAccessBlock",
  "s3:GetBucketAcl",
  "s3:GetBucketCORS",
  "s3:GetBucketLocation",
  "s3:GetBucketLogging",
  "s3:GetBucketNotification",
  "s3:GetBucketObjectLockConfiguration",
  "s3:GetBucketPolicy",
  "s3:GetBucketPublicAccessBlock",
  "s3:GetBucketRequestPayment",
  "s3:GetBucketTagging",
  "s3:GetBucketVersioning",
  "s3:GetBucketWebsite",
  "s3:GetEncryptionConfiguration",
  "s3:GetLifecycleConfiguration",
  "s3:GetReplicationConfiguration",
  "s3:ListAccessPoints",
  "s3:ListAllMyBuckets",
  "s3:ListBucket"
]
resources = [
  "arn:aws:s3:::*"
]
}
}
data "aws_iam_policy_document" "ebs" {
statement {
sid    = "ListEBSVolumes"
effect = "Allow"
actions = [
  "ec2:Describe*",
  "ec2:GetEbsEncryptionByDefault"
]
resources = ["*"]
}
}

data "aws_iam_policy_document" "policy" {
statement {
sid       = "Pol"
effect    = "Allow"
actions   = ["ec2:Describe*"]
resources = ["*"]
}
}

但是当我运行 terraform plan

in aws_iam_policy.this policy 字段转换为字符串而不是数据值,我得到一个错误

│ Error: "policy" contains an invalid JSON policy
│ 
│   with aws_iam_policy.this["aws-config-role-1.policy"],
│   on roles.tf line 31, in resource "aws_iam_policy" "this":
│   31:   policy = "data.aws_iam_policy_document.${each.value.policy_name}.json"

基本上,如果我查看政策内部,它包含字符串 政策 =data.aws_iam_policy_document.s3.json 代替实际数据

有办法解决这个问题吗?请指教

不能通过以下方式动态创建对数据源的引用

policy = "data.aws_iam_policy_document.${each.value.policy_name}.json"

这将导致您的 policy 成为 文字字符串 ,例如"data.aws_iam_policy_document.s3.json",其结果并非您认为的那样。

您必须完全重构您的设计,可能使用 for_each 与您的 aws_iam_policy_document 和动态块。