如何根据terraform中的cidr块提取子网ID? [英] How to pull a subnet id based on the cidr block in terraform?
问题描述
我在一个区域中创建了 vpc 和 3 个子网.我有 3 个名为 jsc、valid、test 的服务,我必须为每个服务创建 3 个实例,我必须从每个区域 cidr 块传递子网 ID
I have create vpc and 3 subnets in a region.I have 3 services named jsc,valid,test and i have to create 3 instances for each service and i have to pass subnet id from each zone cidr block
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 实例资源
Now i have to pass the subnet id to below aws instance resource by either lookup a Name Tag or cidr block
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 from data source is as below
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 抛出错误.所以我想直接传递子网 ID 而不是用于每个
Tried the below method but since for_each is containing the subnet id's which compute after apply,terraform is throwing error.So i want to directly pass the subnet-id instead of using for each
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
}
推荐答案
从您的描述看来,您的问题可以更直接地建模为对象映射,其中每个元素代表特定可用区的设置:
From your description it sounds like your problem could be modeled more directly as a map of objects where each element represents the settings for a particular availability zone:
variable "availability_zones" {
type = map(object({
private_subnet = string
public_subnet = string
}))
}
然后您可以为它分配一个如下所示的值,以便清楚地表明哪些 CIDR 范围属于哪些可用区:
You could then assign this a value like the following in order to clearly indicate which CIDR ranges belong to which availability zones:
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
期望的数据结构来创建这些子网.例如,对于您的私有子网:
Now you have a data structure that directly matches the expectations of for_each
to create these subnets. For example, for your private subnets:
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 配置通常不应使用 数据回读
使用 resource
块阻止它也管理的相同对象;相反,我们直接参考 resource
块的结果,以便 Terraform 可以看到对象是如何连接的,从而产生正确的操作顺序.
An advantage of this approach is that aws_subnet.ecom_private
is now a map from availability zones to subnet objects. As I mentioned in my answer to your previous question, a particular Terraform configuration should typically not read back with a data
block the same objects it's also managing with resource
blocks; instead, we refer to the results of the resource
blocks directly so that Terraform can see how the objects are connected and thus produce the correct order of operations.
对于问题的第二部分,您似乎想为每个服务和声明一个 aws_instance
实例.为了使用 for_each
实现这一点,我们需要派生出一种新的数据结构,该数据结构的每个服务和子网组合都有一个元素.要查找两组的所有组合,我通常使用 setproduct
函数,在这种情况下我们可以这样使用:
For the second part of your problem it sounds like you want to declare an aws_instance
instance per service and per subnet. To achieve that with for_each
we need to derive a new data structure that has one element per combination of service and subnet. To find all combinations of two sets I'd typically use the setproduct
function, which in this case we might use like this:
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 直到子网创建后才会决定.
This expression creates a mapping from compound keys containing both a service name and an availability zone name to objects containing both the service name and one of the subnet objects. This data structure is now of a suitable shape to declare aws_instance
per element of this collection. Note that this is different than what you tried because the keys are built only from data defined directly in the configuration: service names and availability zone names. Your example didn't work because you tried to use the subnet id, which EC2 doesn't decide until after the subnet has already been created.
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
}
}
因此,这些实例将具有如下地址,其中的键仅包含您在配置中直接定义的数据:
These instances will therefore have addresses like this, where the keys include only data that you've defined directly in your configuration:
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"]
这篇关于如何根据terraform中的cidr块提取子网ID?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!