如何根据terraform中的cidr块提取子网ID? [英] How to pull a subnet id based on the cidr block in terraform?

查看:37
本文介绍了如何根据terraform中的cidr块提取子网ID?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在一个区域中创建了 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆