问题描述
我希望通过terraform模块在IAM策略中创建以下语句:
{
"Effect": "Allow","Action": [
"ec2:CreateNetworkInterfacePermission"
],"Resource": "arn:aws:ec2:us-east-1:<acc-id>:network-interface/*","Condition": {
"StringEquals": {
"ec2:Subnet": [
"subnet-id1","subnet-id2"
],"ec2:AuthorizedService": "codebuild.amazonaws.com"
}
}
}
我面临的问题是我无法弄清楚哪个插值会得到我[“ subnet1”,“ subnet2”]。
这里有一点背景。我用TF模块(pubic_subnets)创建了子网,输出代码是这样的:
output "subnet_ids" { value = "${join(",",aws_subnet.public.*.id)}" }
,其结果是类似“ subnet-1,subnet-2,subnet-3”的字符串。这是我从中获取子网ID的地方,我想将其传递给另一个模块。
我还有另一个用于IAM策略的模块(下面的代码)。我试图从上方获取subnet_ids并将其传递给iam-policy模块,以便可以将它们放入我的语句中,但是,我不知道如何使用TF中的任何插值。我尝试了大约十二种不同的插值,但是没有运气。 我能够将子网ID传递到IAM策略的唯一方法是从字符串(0,1)中提取项目并将它们作为单独的变量传递。 这不是完美的,因为我想包括所有子网ID,有时我会有3个或更多子网。
有人成功做到了这种魔术吗?如何更新代码,以便所有子网ID均以[“ subnet1”,“ subnet2”,“ subnet3”]格式传递?
这是我在2个文件中的iam-policy模块的代码:
- main.tf
data "template_file" "policy" {
template = "${file("${var.policy_document}")}"
vars = {
account_id = "${var.account_id}"
role_name = "${var.role_name}"
log_group = "${var.log_group}"
repository_arn = "${var.repository_arn}"
website_s3_bucket = "${var.website_s3_bucket}"
pipeline_s3_bucket = "${var.pipeline_s3_bucket}"
subnet1 = "${var.subnet1}"
subnet2 = "${var.subnet1}"
}
}
resource "aws_iam_policy" "policy" {
name = "${var.policy_name}"
description = "${var.description}"
path = "${var.path}"
policy = "${data.template_file.policy.rendered}"
}
- variables.tf
variable "policy_name" { default = "" }
variable "description" { }
variable "path" { default = "" }
variable "policy_document" {
type = "string"
default = ""
}
# variables for policy
variable "log_group" { }
variable "account_id" { }
variable "role_name" { }
variable "repository_arn" { }
variable "website_s3_bucket" { }
variable "pipeline_s3_bucket" { }
variable "subnet1" { }
variable "subnet2" { }
在我的项目文件(project.tf)中,我有:
module "iam_policy_for_codebuild_service_role" {
source = "../aws-policy.local"
policy_name = "${var.name}CodeBuildServiceRolePolicy1${lookup(var.environment,var.region)}"
description = "Policy for codebuild service role."
path = "/"
policy_document = "./policies/policy1.json"
account_id = "${module.account_id.id}"
log_group = "${var.name}Build${lookup(var.environment,var.region)}logGroup"
role_name = "${var.name}CodeBuildServiceRole${lookup(var.environment,var.region)}"
repository_arn = "${aws_codecommit_repository.ce_repo.arn}"
website_s3_bucket = "${aws_s3_bucket.website_bucket.arn}"
pipeline_s3_bucket = "${aws_s3_bucket.codepipeline_bucket.arn}"
subnet1 = "${element(split(",module.public_subnet.subnet_ids),0)}"
subnet2 = "${element(split(",1)}"
}
这是我的./policies/policy1.json文件中的内容:
{
"Effect": "Allow","Action": [
"ec2:CreateNetworkInterfacePermission"
],"Resource": "arn:aws:ec2:us-east-1:${account_id}:network-interface/*","Condition": {
"StringEquals": {
"ec2:Subnet": ["${subnet1}","${subnet2}"],"ec2:AuthorizedService": "codebuild.amazonaws.com"
}
}
}
我开始认为这是不可能的,因为IAM策略在“ ec2:Subnet”之后必须有[]。
如果那里有一个可以执行此法术的法师,请分享使该法术在Terraform 11x版中起作用的要素。 :)
谢谢
解决方法
您可以将它们作为字符串或只是列表而不是各个子网subnet1
,subnet2
等传递给它们。
例如,在您的variables.tf
中,您可以使用变量代替单个子条目:
variable "subnet_ids" { }
然后在project.tf
subnet_ids = "${module.public_subnet.subnet_ids}"
将生成subnet_ids="subnet1,subnet2"
。然后,您通过jsonencode
将此变量传递到文件模板中,这将导致字符串["subnet1","subnet2"]
:
data "template_file" "policy" {
template = "${file("${var.policy_document}")}"
vars = {
account_id = "${var.account_id}"
role_name = "${var.role_name}"
log_group = "${var.log_group}"
repository_arn = "${var.repository_arn}"
website_s3_bucket = "${var.website_s3_bucket}"
pipeline_s3_bucket = "${var.pipeline_s3_bucket}"
subnet_ids = "${jsonencode(split(",",{var.subnet_ids)}"
}
}
最后,在./policies/policy1.json
"ec2:Subnet": ${subnet_ids},
应扩展为:
"ec2:Subnet": ["subnet1","subnet2"],
我尚未验证上述内容,因此可能需要进行一些更改才能使其完全正常运行。但是至少应该清楚如何解决问题的核心思想。
,在进行插值处理之后,我开始使用它,这是我的解决方案。希望它将对将来的人有所帮助。
其核心是使用replace函数进行字符串操作:
${replace(replace(replace(module.public_subnet.subnet_ids,"\","),"subnet","\"arn:aws:ec2:${var.region}:${module.account_id.id}:subnet"),"/$/","\"")}
这是我的模块现在的样子:
module "iam_policy" {
source = "../aws-policy.local"
policy_name = "${var.name}CodeBuildServiceRolePolicy1${lookup(var.environment,var.region)}"
description = "Policy for codebuild service role."
path = "/"
policy_document = "./policies/policy1.json"
account_id = "${module.account_id.id}"
log_group = "${var.name}Build${lookup(var.environment,var.region)}logGroup"
role_name = "${var.name}CodeBuildServiceRole${lookup(var.environment,var.region)}"
repository_arn = "${aws_codecommit_repository.ce_repo.arn}"
website_s3_bucket = "${aws_s3_bucket.website_bucket.arn}"
pipeline_s3_bucket = "${aws_s3_bucket.codepipeline_bucket.arn}"
subnets = "${replace(replace(replace(module.public_subnet.subnet_ids,"\"")}"
}
但是我将尝试从上面的注释中实现jsonencode,以缩短此代码,因为它看起来很丑陋。