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
和动态块。
我想自动创建 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
和动态块。