如何根据 terraform 中的 cidr 块提取子网 ID?
How to pull a subnet id based on the cidr block in terraform?
我已经在 region.I 中创建了 vpc 和 3 个子网,其中有 3 个名为 jsc、valid、test 的服务,我必须为每个服务创建 3 个实例,并且我必须从每个区域 cidr 块传递子网 ID
variable "region" {
type = string
default = "ap-south-1"
}
variable "ecom-cidr" {
type = string
default = "10.0.0.0/16"
}
variable "azs" {
type = list(any)
default = ["ap-south-1a", "ap-south-1b", "ap-south-1c"]
}
variable "private-subnets" {
type = list(any)
default = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
}
variable "public-subnets" {
type = list(any)
default = ["10.0.4.0/28", "10.0.5.0/28", "10.0.6.0/28"]
}
variable "instance_count" {
type = string
default = 3
}
variable "service-names" {
type = list(any)
default = ["valid", "jsc", "test"]
}
resource "aws_subnet" "ecom-private" {
count = length(var.private-subnets)
vpc_id = aws_vpc.ecom-vpc.id
cidr_block = element(var.private-subnets, count.index)
availability_zone = element(var.azs, count.index)
map_public_ip_on_launch = false
tags = {
Name = "ecom-INT-${element(split("-", element(var.azs, count.index)), 2)}"
}
}
data "aws_subnet" "priv-subnet-details" {
vpc_id = aws_vpc.ecom-vpc.id
count = length(var.private-subnets)
filter {
name = "tag:Name"
values = ["ecom-INT-${element(split("-", element(var.azs, count.index)), 2)}"] # insert values here
}
}
现在我必须通过查找名称标签或 cidr 块将子网 ID 传递到 aws 实例资源下面
resource "aws_instance" "ecom-instances" {
count = 3*var.instance_count
ami = data.aws_ami.ecom.id
instance_type = "t3.micro"
subnet_id = <how to fetch>
tags = {
Name = "ecom-${element(var.service-names, count.index)}-service"
Service = "${element(var.service-names, count.index)}"
}
vpc_security_group_ids = [aws_security_group.ecom-sg["${element(var.service-names, count.index)}"].id]
来自数据源的子网详细信息如下
subnet-details = [
+ {
+ arn = "arn:aws:ec2:ap-south-1:114712064551:subnet/subnet-07fede319193c390d"
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-south-1a"
+ availability_zone_id = "aps1-az1"
+ available_ip_address_count = 251
+ cidr_block = "10.0.1.0/24"
+ customer_owned_ipv4_pool = ""
+ default_for_az = false
+ filter = [
+ {
+ name = "tag:Name"
+ values = [
+ "ecom-INT-1a",
]
},
]
+ id = "subnet-07fede319193c390d"
+ ipv6_cidr_block = null
+ ipv6_cidr_block_association_id = null
+ map_customer_owned_ip_on_launch = false
+ map_public_ip_on_launch = false
+ outpost_arn = ""
+ owner_id = "114712064551"
+ state = "available"
+ tags = {
+ "Name" = "ecom-INT-1a"
}
+ vpc_id = "vpc-0fd6cb4be13de15e8"
},
+ {
+ arn = "arn:aws:ec2:ap-south-1:114712064551:subnet/subnet-0680314588bcd64e6"
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-south-1b"
+ availability_zone_id = "aps1-az3"
+ available_ip_address_count = 251
+ cidr_block = "10.0.2.0/24"
+ customer_owned_ipv4_pool = ""
+ default_for_az = false
+ filter = [
+ {
+ name = "tag:Name"
+ values = [
+ "ecom-INT-1b",
]
},
]
+ id = "subnet-0680314588bcd64e6"
+ ipv6_cidr_block = null
+ ipv6_cidr_block_association_id = null
+ map_customer_owned_ip_on_launch = false
+ map_public_ip_on_launch = false
+ outpost_arn = ""
+ owner_id = "114712064551"
+ state = "available"
+ tags = {
+ "Name" = "ecom-INT-1b"
}
+ vpc_id = "vpc-0fd6cb4be13de15e8"
},
+ {
+ arn = "arn:aws:ec2:ap-south-1:114712064551:subnet/subnet-04f6ab1d3612c4f48"
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-south-1c"
+ availability_zone_id = "aps1-az2"
+ available_ip_address_count = 251
+ cidr_block = "10.0.3.0/24"
+ customer_owned_ipv4_pool = ""
+ default_for_az = false
+ filter = [
+ {
+ name = "tag:Name"
+ values = [
+ "ecom-INT-1c",
]
},
]
+ id = "subnet-04f6ab1d3612c4f48"
+ ipv6_cidr_block = null
+ ipv6_cidr_block_association_id = null
+ map_customer_owned_ip_on_launch = false
+ map_public_ip_on_launch = false
+ outpost_arn = ""
+ owner_id = "114712064551"
+ state = "available"
+ tags = {
+ "Name" = "ecom-INT-1c"
}
+ vpc_id = "vpc-0fd6cb4be13de15e8"
},
]
你能指导我吗
尝试了以下方法,但由于 for_each 包含在应用后计算的子网 ID,terraform 正在抛出 error.So 我想直接传递子网 ID 而不是使用每个
locals {
instance_configs = tomap({
for idx,pair in setproduct(var.service-names, data.aws_subnet.priv-subnet-details.*.id) :
"${pair[0]}-${pair[1]}" => {
service_name = pair[0]
subnet = pair[1]
}
})
}
resource "aws_instance" "ecom-instances" {
for_each = local.instance_configs
ami = data.aws_ami.ecom.id
instance_type = "t3.micro"
tags = {
Name = "ecom-${each.value.service_name}-service"
Service = each.value.service_name
}
vpc_security_group_ids = [aws_security_group.ecom-sg[each.value.service_name].id]
subnet_id = each.value.subnet
}
根据您的描述,听起来您的问题可以更直接地建模为对象映射,其中每个元素代表特定可用性区域的设置:
variable "availability_zones" {
type = map(object({
private_subnet = string
public_subnet = string
}))
}
然后您可以为其分配一个如下所示的值,以便清楚地指示哪些 CIDR 范围属于哪些可用性区域:
availability_zones = {
ap-south-1a = {
private_subnet = "10.0.1.0/24"
public_subnet = "10.0.4.0/28"
}
ap-south-1b = {
private_subnet = "10.0.2.0/24"
public_subnet = "10.0.5.0/28"
}
ap-south-1c = {
private_subnet = "10.0.3.0/24"
public_subnet = "10.0.6.0/28"
}
}
现在您拥有了一个直接符合 for_each
创建这些子网的期望的数据结构。例如,对于您的私有子网:
resource "aws_subnet" "ecom_private" {
for_each = var.availability_zones
vpc_id = aws_vpc.ecom-vpc.id
cidr_block = each.value.private_subnet
availability_zone = each.key
map_public_ip_on_launch = false
tags = {
Name = "ecom-INT-${split("-", each.key)[2]}"
}
}
这种方法的一个优点是 aws_subnet.ecom_private
现在是从可用性区域到子网对象的映射。正如我在对 的回答中提到的,特定的 Terraform 配置通常不应使用 data
块回读它也使用 resource
块管理的相同对象;相反,我们直接引用 resource
块的结果,以便 Terraform 可以看到对象是如何连接的,从而产生正确的操作顺序。
对于问题的第二部分,听起来您想为每个服务 和每个子网 声明一个 aws_instance
实例。为了通过 for_each
实现这一点,我们需要派生一个新的数据结构,每个服务和子网的组合都有一个元素。要找到两个集合的所有组合,我通常会使用 the setproduct
function,在这种情况下我们可能会这样使用:
locals {
service_subnets = {
for pair in setproduct(var.service_names, values(aws_subnet.ecom_private)) :
"${pair[0]}:${pair[1].availability_zone}" => {
service_name = pair[0]
subnet = pair[1]
}
}
}
此表达式创建从包含服务名称和可用区名称的复合键到包含服务名称和子网对象之一的对象的映射。此数据结构现在具有适合声明此集合的每个元素 aws_instance
的形状。请注意,这与您尝试的不同,因为密钥仅根据直接在配置中定义的数据构建:服务名称和可用性区域名称。您的示例不起作用,因为您尝试使用子网 ID,而 EC2 直到子网创建后才确定。
resource "aws_instance" "ecom" {
for_each = local.service_subnets
ami = data.aws_ami.ecom.id
instance_type = "t3.micro"
subnet_id = each.value.subnet.id
vpc_security_group_ids = [aws_security_group.ecom_sg[each.value.service_name].id]
tags = {
Name = "ecom-${each.value.service_name}-service"
Service = each.value.service_name
}
}
因此,这些实例将具有这样的地址,其中的键仅包含您在配置中直接定义的数据:
aws_instance.ecom["valid:ap-south-1a"]
aws_instance.ecom["valid:ap-south-1b"]
aws_instance.ecom["valid:ap-south-1c"]
aws_instance.ecom["jsc:ap-south-1a"]
aws_instance.ecom["jsc:ap-south-1b"]
aws_instance.ecom["jsc:ap-south-1c"]
aws_instance.ecom["test:ap-south-1a"]
aws_instance.ecom["test:ap-south-1b"]
aws_instance.ecom["test:ap-south-1c"]
我已经在 region.I 中创建了 vpc 和 3 个子网,其中有 3 个名为 jsc、valid、test 的服务,我必须为每个服务创建 3 个实例,并且我必须从每个区域 cidr 块传递子网 ID
variable "region" {
type = string
default = "ap-south-1"
}
variable "ecom-cidr" {
type = string
default = "10.0.0.0/16"
}
variable "azs" {
type = list(any)
default = ["ap-south-1a", "ap-south-1b", "ap-south-1c"]
}
variable "private-subnets" {
type = list(any)
default = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
}
variable "public-subnets" {
type = list(any)
default = ["10.0.4.0/28", "10.0.5.0/28", "10.0.6.0/28"]
}
variable "instance_count" {
type = string
default = 3
}
variable "service-names" {
type = list(any)
default = ["valid", "jsc", "test"]
}
resource "aws_subnet" "ecom-private" {
count = length(var.private-subnets)
vpc_id = aws_vpc.ecom-vpc.id
cidr_block = element(var.private-subnets, count.index)
availability_zone = element(var.azs, count.index)
map_public_ip_on_launch = false
tags = {
Name = "ecom-INT-${element(split("-", element(var.azs, count.index)), 2)}"
}
}
data "aws_subnet" "priv-subnet-details" {
vpc_id = aws_vpc.ecom-vpc.id
count = length(var.private-subnets)
filter {
name = "tag:Name"
values = ["ecom-INT-${element(split("-", element(var.azs, count.index)), 2)}"] # insert values here
}
}
现在我必须通过查找名称标签或 cidr 块将子网 ID 传递到 aws 实例资源下面
resource "aws_instance" "ecom-instances" {
count = 3*var.instance_count
ami = data.aws_ami.ecom.id
instance_type = "t3.micro"
subnet_id = <how to fetch>
tags = {
Name = "ecom-${element(var.service-names, count.index)}-service"
Service = "${element(var.service-names, count.index)}"
}
vpc_security_group_ids = [aws_security_group.ecom-sg["${element(var.service-names, count.index)}"].id]
来自数据源的子网详细信息如下
subnet-details = [
+ {
+ arn = "arn:aws:ec2:ap-south-1:114712064551:subnet/subnet-07fede319193c390d"
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-south-1a"
+ availability_zone_id = "aps1-az1"
+ available_ip_address_count = 251
+ cidr_block = "10.0.1.0/24"
+ customer_owned_ipv4_pool = ""
+ default_for_az = false
+ filter = [
+ {
+ name = "tag:Name"
+ values = [
+ "ecom-INT-1a",
]
},
]
+ id = "subnet-07fede319193c390d"
+ ipv6_cidr_block = null
+ ipv6_cidr_block_association_id = null
+ map_customer_owned_ip_on_launch = false
+ map_public_ip_on_launch = false
+ outpost_arn = ""
+ owner_id = "114712064551"
+ state = "available"
+ tags = {
+ "Name" = "ecom-INT-1a"
}
+ vpc_id = "vpc-0fd6cb4be13de15e8"
},
+ {
+ arn = "arn:aws:ec2:ap-south-1:114712064551:subnet/subnet-0680314588bcd64e6"
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-south-1b"
+ availability_zone_id = "aps1-az3"
+ available_ip_address_count = 251
+ cidr_block = "10.0.2.0/24"
+ customer_owned_ipv4_pool = ""
+ default_for_az = false
+ filter = [
+ {
+ name = "tag:Name"
+ values = [
+ "ecom-INT-1b",
]
},
]
+ id = "subnet-0680314588bcd64e6"
+ ipv6_cidr_block = null
+ ipv6_cidr_block_association_id = null
+ map_customer_owned_ip_on_launch = false
+ map_public_ip_on_launch = false
+ outpost_arn = ""
+ owner_id = "114712064551"
+ state = "available"
+ tags = {
+ "Name" = "ecom-INT-1b"
}
+ vpc_id = "vpc-0fd6cb4be13de15e8"
},
+ {
+ arn = "arn:aws:ec2:ap-south-1:114712064551:subnet/subnet-04f6ab1d3612c4f48"
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-south-1c"
+ availability_zone_id = "aps1-az2"
+ available_ip_address_count = 251
+ cidr_block = "10.0.3.0/24"
+ customer_owned_ipv4_pool = ""
+ default_for_az = false
+ filter = [
+ {
+ name = "tag:Name"
+ values = [
+ "ecom-INT-1c",
]
},
]
+ id = "subnet-04f6ab1d3612c4f48"
+ ipv6_cidr_block = null
+ ipv6_cidr_block_association_id = null
+ map_customer_owned_ip_on_launch = false
+ map_public_ip_on_launch = false
+ outpost_arn = ""
+ owner_id = "114712064551"
+ state = "available"
+ tags = {
+ "Name" = "ecom-INT-1c"
}
+ vpc_id = "vpc-0fd6cb4be13de15e8"
},
]
你能指导我吗
尝试了以下方法,但由于 for_each 包含在应用后计算的子网 ID,terraform 正在抛出 error.So 我想直接传递子网 ID 而不是使用每个
locals {
instance_configs = tomap({
for idx,pair in setproduct(var.service-names, data.aws_subnet.priv-subnet-details.*.id) :
"${pair[0]}-${pair[1]}" => {
service_name = pair[0]
subnet = pair[1]
}
})
}
resource "aws_instance" "ecom-instances" {
for_each = local.instance_configs
ami = data.aws_ami.ecom.id
instance_type = "t3.micro"
tags = {
Name = "ecom-${each.value.service_name}-service"
Service = each.value.service_name
}
vpc_security_group_ids = [aws_security_group.ecom-sg[each.value.service_name].id]
subnet_id = each.value.subnet
}
根据您的描述,听起来您的问题可以更直接地建模为对象映射,其中每个元素代表特定可用性区域的设置:
variable "availability_zones" {
type = map(object({
private_subnet = string
public_subnet = string
}))
}
然后您可以为其分配一个如下所示的值,以便清楚地指示哪些 CIDR 范围属于哪些可用性区域:
availability_zones = {
ap-south-1a = {
private_subnet = "10.0.1.0/24"
public_subnet = "10.0.4.0/28"
}
ap-south-1b = {
private_subnet = "10.0.2.0/24"
public_subnet = "10.0.5.0/28"
}
ap-south-1c = {
private_subnet = "10.0.3.0/24"
public_subnet = "10.0.6.0/28"
}
}
现在您拥有了一个直接符合 for_each
创建这些子网的期望的数据结构。例如,对于您的私有子网:
resource "aws_subnet" "ecom_private" {
for_each = var.availability_zones
vpc_id = aws_vpc.ecom-vpc.id
cidr_block = each.value.private_subnet
availability_zone = each.key
map_public_ip_on_launch = false
tags = {
Name = "ecom-INT-${split("-", each.key)[2]}"
}
}
这种方法的一个优点是 aws_subnet.ecom_private
现在是从可用性区域到子网对象的映射。正如我在对 data
块回读它也使用 resource
块管理的相同对象;相反,我们直接引用 resource
块的结果,以便 Terraform 可以看到对象是如何连接的,从而产生正确的操作顺序。
对于问题的第二部分,听起来您想为每个服务 和每个子网 声明一个 aws_instance
实例。为了通过 for_each
实现这一点,我们需要派生一个新的数据结构,每个服务和子网的组合都有一个元素。要找到两个集合的所有组合,我通常会使用 the setproduct
function,在这种情况下我们可能会这样使用:
locals {
service_subnets = {
for pair in setproduct(var.service_names, values(aws_subnet.ecom_private)) :
"${pair[0]}:${pair[1].availability_zone}" => {
service_name = pair[0]
subnet = pair[1]
}
}
}
此表达式创建从包含服务名称和可用区名称的复合键到包含服务名称和子网对象之一的对象的映射。此数据结构现在具有适合声明此集合的每个元素 aws_instance
的形状。请注意,这与您尝试的不同,因为密钥仅根据直接在配置中定义的数据构建:服务名称和可用性区域名称。您的示例不起作用,因为您尝试使用子网 ID,而 EC2 直到子网创建后才确定。
resource "aws_instance" "ecom" {
for_each = local.service_subnets
ami = data.aws_ami.ecom.id
instance_type = "t3.micro"
subnet_id = each.value.subnet.id
vpc_security_group_ids = [aws_security_group.ecom_sg[each.value.service_name].id]
tags = {
Name = "ecom-${each.value.service_name}-service"
Service = each.value.service_name
}
}
因此,这些实例将具有这样的地址,其中的键仅包含您在配置中直接定义的数据:
aws_instance.ecom["valid:ap-south-1a"]
aws_instance.ecom["valid:ap-south-1b"]
aws_instance.ecom["valid:ap-south-1c"]
aws_instance.ecom["jsc:ap-south-1a"]
aws_instance.ecom["jsc:ap-south-1b"]
aws_instance.ecom["jsc:ap-south-1c"]
aws_instance.ecom["test:ap-south-1a"]
aws_instance.ecom["test:ap-south-1b"]
aws_instance.ecom["test:ap-south-1c"]