我正在使用VPC模块创建VPC和子网。
创建子网后,我想与其他帐户共享它们。该模块运行良好,并创建所有子网。我需要子网ID,这样我就可以使用RAM来共享子网。
我的代码大致看起来像
# Create VPC and subnets
module "vpc" {
...
...
}
# Next get subnet IDs
data "aws_subnets" "dev_subnet" {
filter {
name = "vpc-id"
values = [module.vpc.vpc_id]
}
tags = {
Environment = "pe-dev*"
}
}
# Create resource share and principal association
resource "aws_ram_resource_share" "share_subnets_with_dev_account" {}
resource "aws_ram_principal_association" "share_subnets_with_dev_account" {}
现在,我需要从子网ID中提取ARN,然后创建资源关联
resource "aws_ram_resource_association" "example" {
for_each = toset(data.aws_subnets.dev_subnet.ids)
resource_arn = "arn:aws:ec2:${var.region}:${var.aws_account_id}:subnet/${each.value}"
resource_share_arn = aws_ram_resource_share.share_subnets_with_dev_account.arn
}
但当我做一个新的terrafrom apply
时,我会得到错误
│ Error: Invalid for_each argument
│
│ on main.tf line 110, in resource "aws_ram_resource_association" "example":
│ 110: for_each = toset(data.aws_subnets.dev_subnet.ids)
│ ├────────────────
│ │ data.aws_subnets.dev_subnet.ids is a list of string, known only after apply
│
│ The "for_each" set includes values derived from resource attributes that cannot be determined until apply, and so Terraform cannot determine the full set of keys that will identify the instances of this resource.
│
│ When working with unknown values in for_each, it's better to use a map value where the keys are defined statically in your configuration and where only the values contain apply-time results.
│
│ Alternatively, you could use the -target planning option to first apply only the resources that the for_each value depends on, and then apply a second time to fully converge.
我想到的是添加一个depends_on
。像这样的
resource "aws_ram_resource_association" "example" {
for_each = toset(data.aws_subnets.dev_subnet.ids)
resource_arn = "arn:aws:ec2:${var.region}:${var.aws_account_id}:subnet/${each.value}"
resource_share_arn = aws_ram_resource_share.share_subnets_with_dev_account.arn
depends_on = [
module.vpc.aws_subnet.private
]
但现在我得到
│ Error: Invalid depends_on reference
│
│ on main.tf line 116, in resource "aws_ram_resource_association" "example":
│ 116: module.vpc.aws_subnet.private
│
│ References in depends_on must be to a whole object (resource, etc), not to an attribute of an object.
知道如何在创建aws_ram_resource_association
之前等待创建子网并获取子网ID吗?
编辑:
运行的是什么
data "aws_subnets" "dev_subnet" {
filter {
name = "vpc-id"
values = [module.vpc.vpc_id]
}
tags = {
Environment = "dev-*"
}
}
data "aws_subnet" "dev_subnet" {
for_each = toset(data.aws_subnets.dev_subnet.ids)
id = each.value
}
output "dev_subnet_arns" {
value = [for s in data.aws_subnet.dev_subnet : s.arn]
}
结果
+ dev_subnet_arns = [
+ "arn:aws:ec2:ca-central-1:0097747:subnet/subnet-013987fd9651c3545",
+ "arn:aws:ec2:ca-central-1:0477747:subnet/subnet-015d76b264280321a",
+ "arn:aws:ec2:ca-central-1:0091747:subnet/subnet-026cd0402fe283c33",
]
但仅当我在先前运行CCD_ 5之后执行CCD_。
如果我做了一个tf destroy
并重新创建所有内容,那么我会再次得到错误
tf plan
╷
│ Error: Invalid for_each argument
│
│ on main.tf line 116, in data "aws_subnet" "dev_subnet":
│ 116: for_each = toset(data.aws_subnets.dev_subnet.ids)
│ ├────────────────
│ │ data.aws_subnets.dev_subnet.ids is a list of string, known only after apply
│
│ The "for_each" set includes values derived from resource attributes that cannot be determined until apply, and so Terraform cannot determine the full set of keys that will identify the instances of this resource.
│
│ When working with unknown values in for_each, it's better to use a map value where the keys are defined statically in your configuration and where only the values contain apply-time results.
│
│ Alternatively, you could use the -target planning option to first apply only the resources that the for_each value depends on, and then apply a second time to fully converge.
这里的关键问题是,必须在计划资源之前评估for_each
,而不是在创建资源之前评估。
data.aws_subnets.dev_subnet.ids
的结果取决于VPC ID,只有创建了VPC才能知道VPC ID。但在创建VPC之前,aws_ram_resource_association.example
也必须同时进行规划,因此Terraform解决此问题的唯一方法是在规划步骤中创建VPC,这将违反Terraform在应用步骤之前不执行任何操作的期望。
在这里的架构中,调用模块试图检索一组尚未创建的子网(因为它们包含的VPC也尚未创建),解决这个问题的唯一方法是首先运行带有额外选项-target
的Terraform,强制它在计划其他事情之前先创建VPC和子网:
terraform apply -target=module.vpc
,这将导致Terraform创建并应用部分计划,仅包括该模块中声明的资源及其所依赖的资源terraform apply
,之后没有任何争论,计划并应用部分计划中没有包含的所有其他内容
然后,您可以正常使用terraform apply
进行持续维护,只要您永远不要更换VPC,从而导致其ID再次未知。
为了避免这个额外的特殊引导步骤,更好的设计是VPC模块将其声明的子网导出为额外的输出值,这意味着Terraform可以使用计划创建的子网集合,而不是已经存在的子网集。
不幸的是,您正在使用的VPC模块并没有以适合与for_each
一起使用的方式导出子网ID:它只导出子网标识,而没有将它们与一个唯一的密钥关联起来,该密钥可以在规划过程中识别它们。因此,不幸的是,对于当前设计的此模块,您需要使用count
而不是for_each
:
resource "aws_ram_resource_association" "example" {
count = length(module.vpc.private_subnets)
resource_arn = "arn:aws:ec2:${var.region}:${var.aws_account_id}:subnet/${module.vpc.private_subnets[count.index]}"
resource_share_arn = aws_ram_resource_share.share_subnets_with_dev_account.arn
}
这将导致此资源的实例按其在子网列表中的位置进行跟踪,因此,如果您将来添加或删除子网,则它们与列表项的关联将更改。
要在此处使用for_each
,需要此模块将子网导出为映射,其中键是可以从配置静态确定的值,例如CIDR块,值是关于每个子网的信息。
这里有一个假设的输出值,模块可以包括它来支持这一点,但要添加它,需要创建自己的共享模块分支并对其进行修改:
output "private_subnets" {
value = {
for sn in aws_subnet.private : sn.cidr_block => {
id = sn.id
}
}
}
通过这种方式修改模块,您的调用模块可以使用具有以下值的for_each
:
resource "aws_ram_resource_association" "example" {
for_each = module.vpc.private_subnets
resource_arn = "arn:aws:ec2:${var.region}:${var.aws_account_id}:subnet/${each.value.id}"
resource_share_arn = aws_ram_resource_share.share_subnets_with_dev_account.arn
}
有了这个新结构,Terraform将通过使用它们的CIDR块作为唯一标识符来跟踪aws_ram_resource_association.example
的实例,因此您可以随着时间的推移添加和删除CIDR块,Terraform将会正确地理解哪个";RAM资源协会;属于哪个子网,并添加/删除相关的各个子网。