Terraform:基于键过滤映射列表

10

我正在实现一个安全组模块,它将通过获取和过滤CIDR和源安全组ID来创建安全组规则。

当前的模块配置。

security_group_module.tf

resource "aws_security_group" "this" {
  name                   = var.name
  description            = var.description
  vpc_id                 = var.vpc_id
  revoke_rules_on_delete = var.revoke_rules_on_delete
}

## CIDR Rule

resource "aws_security_group_rule" "cidr_rule" {
  count = length(var.security_group_rules)

  type              = var.security_group_rules[count.index].type
  from_port         = var.security_group_rules[count.index].from_port
  to_port           = var.security_group_rules[count.index].to_port
  protocol          = var.security_group_rules[count.index].protocol
  cidr_blocks       = var.security_group_rules[count.index].cidr_block
  description       = var.security_group_rules[count.index].description
  security_group_id = aws_security_group.this.id
}

## Source_security_group_id Rule

resource "aws_security_group_rule" "source_sg_id_rule" {
  count = length(var.security_group_rules)

  type              = var.security_group_rules[count.index].type
  from_port         = var.security_group_rules[count.index].from_port
  to_port           = var.security_group_rules[count.index].to_port
  protocol          = var.security_group_rules[count.index].protocol
  source_security_group_id = var.security_group_rules[count.index].source_security_group_id
  description       = var.security_group_rules[count.index].description
  security_group_id = aws_security_group.this.id
}

main.tf

module "sample_sg" {
  source            = "./modules/aws_security_group"
  name              = "test-sg"
  vpc_id            = "vpc-xxxxxx"

  security_group_rules = [
    { type = "ingress", from_port = 22, to_port = 22, protocol = "tcp", cidr_block = [var.vpc_cidr], description = "ssh" },
    { type = "ingress", from_port = 80, to_port = 80, protocol = "tcp", cidr_block = [var.vpc_cidr], description = "http" },
    { type = "ingress", from_port = 0, to_port = 0, protocol = "-1", source_sg_id = "sg-xxxx", description = "allow all" }
    { type = "egress",  from_port = 0, to_port = 0, protocol = "-1", source_sg_id = "sg-xxxx", description = "allow all" }
  ]
}

因此,这里的问题陈述是当我在模块中调用具有上述地图列表的安全组规则时,它应该检查它是否为source_sg_id或cidr。

然后过滤这些地图并将其传递到模块中的相应资源。

例如:

module ""{
...

  security_group_rules = [
    { type = "ingress", from_port = 22, to_port = 22, protocol = "tcp", cidr_block = [var.vpc_cidr], description = "ssh" },
    { type = "ingress", from_port = 0, to_port = 65535, protocol = "-1", source_sg_id = "sg-xxxx", description = "allow all" }
  ]
}

这些规则应该被检查,并将第一个规则传递给CIDR规则,第二个规则传递给Source_security_group_id规则。 我考虑将其制作成以下形式。
locals {

  sid_rules = some_function{var.security_group_rules, "source_security_group_id"}
  cidr_rules = some_function{var.security_group_rules, "cidr"}
}


resource "aws_security_group_rule" "cidr_rule" {
  count = count(local.cidr_rules)

  ....
  cidr_blocks       = local.cidr_rules[count.index].cidr_block
  ....
}


resource "aws_security_group_rule" "sid_rule" {
  count = count(local.sid_rules)

  ....
  source_security_group_id  = local.sid_rules[count.index].source_sg_id
  ....
}

因此,我正在寻找一种基于关键字从列表中过滤地图的方法。

我尝试过使用查找,但在字符串列表的情况下并没有帮助。

5个回答

9
我想出了一个巧妙的方法。 假设我想从一个宠物列表中筛选出只有猫这种动物 kind = "cat",以下是具体操作:
variable "pets" {
  type = list(object({
    name = string
    kind = string
  }))
  default = [
    {
      name = "Fido"
      kind = "dog"
    },
    {
      name = "Max"
      kind = "dog"
    },
    {
      name = "Milo"
      kind = "cat"
    },
    {
      name = "Simba"
      kind = "cat"
    }
  ]
}
  1. 首先,将宠物列表转换为一个使用索引 tostring(i) 作为键的宠物映射 pets_map。这将在第3步中用于查找筛选后的宠物。
locals {
  pets_map = { for i, pet in var.pets : tostring(i) => pet }
}
  1. 接下来,通过循环遍历 pets_map 中的键,并将不符合条件 pet.kind == "cat" 的相应键设置为空字符串,创建一个过滤后的键列表。然后对该列表进行压缩,以移除其中的空字符串。
locals {
  cats_keys = compact([for i, pet in local.pets_map : pet.kind == "cat" ? i : ""])
}
  1. 循环遍历经过筛选的键 cats_keys 并从 pets_map 中查找相应的宠物。现在你已经得到了只包含猫咪的宠物列表,kind = "cat"
locals {
  cats     = [for key in local.cats_keys : lookup(local.pets_map, key)]
}

您现在可以使用local.cats访问猫,这将为您提供以下地图。
{
  name = "Milo"
  kind = "cat"
},
{
  name = "Simba"
  kind = "cat"
}

以下是完整的示例。
variable "pets" {
  type = list(object({
    name = string
    kind = string
  }))
  default = [
    {
      name = "Fido"
      kind = "dog"
    },
    {
      name = "Max"
      kind = "dog"
    },
    {
      name = "Milo"
      kind = "cat"
    },
    {
      name = "Simba"
      kind = "cat"
    }
  ]
}

locals {
  pets_map = { for i, pet in var.pets : tostring(i) => pet }
  cats_keys = compact([for i, pet in local.pets_map : pet.kind == "cat" ? i : ""])
  cats     = [for key in local.cats_keys : lookup(local.pets_map, key)]
}

2
考虑创建另一个模块来处理规则,并在该模块内设置安全组资源。
module "security_groups" {
  count             = length(var.security_group_rules)
  source_sg_id_rule = var.security_group_rules[count.index].source_sg_id_rule
}

接下来,在新模块中,使用计数语句作为测试来创建可选项:

resource "aws_security_group_rule" "source_sg_id_rule" {
    count = length(var.source_sg_id_rule) == 0 ? 0 : 1

    type              = var.type
    from_port         = var.from_port
    to_port           = var.to_port
    protocol          = var.protocol
    source_security_group_id = var.source_security_group_id
    description       = var.description
    security_group_id = var.security_group_id
}

这将创建一个资源数组,其中包含一个或零个项目,并且会删除任何零列表。

在使用模块创建安全规则时要小心,如果需要的话,您可能还需要通过 count 将它们设置为可选项。https://github.com/hashicorp/terraform-provider-aws/issues/12450 - ctpenrose
在使用模块创建安全规则时,请谨慎操作。如果需要,您可能还需要通过 count 将其设置为可选项。请参考此链接:https://github.com/hashicorp/terraform-provider-aws/issues/12450 - undefined

2

给定:

[{foo: "...", baz: "..."}, ...]

如果你想要这个列表中某个元素的baz,可以使用以下代码:

element([
    for o in [{"foo"="kick","baz"="5"},{"foo"="bar","baz"="100"}] : 
        o if o.foo == "bar"
],0).baz

1
感谢@dan-monego的回复。
我通过单个模块解决了它。
以下是模块文件。

aws_sg_module.tf


# Security group
##########################

resource "aws_security_group" "this" {
  name                   = var.name
  description            = var.description
  vpc_id                 = var.vpc_id
  revoke_rules_on_delete = var.revoke_rules_on_delete

  tags = merge(
    {
      "Name" = format("%s", var.name)
    },
    local.default_tags,
    var.additional_tags
  )
}

resource "aws_security_group_rule" "cidr" {
  count = var.create ? length(var.cidr_sg_rules) : 0

  type              = var.cidr_sg_rules[count.index].type
  from_port         = var.cidr_sg_rules[count.index].from
  to_port           = var.cidr_sg_rules[count.index].to
  protocol          = var.cidr_sg_rules[count.index].protocol
  cidr_blocks       = var.cidr_sg_rules[count.index].cidr
  description       = var.cidr_sg_rules[count.index].description
  security_group_id = local.this_sg_id
}

resource "aws_security_group_rule" "source_sg" {
  count = var.create ? length(var.source_sg_rules) : 0

  type                     = var.source_sg_rules[count.index].type
  from_port                = var.source_sg_rules[count.index].from
  to_port                  = var.source_sg_rules[count.index].to
  protocol                 = var.source_sg_rules[count.index].protocol
  source_security_group_id = var.source_sg_rules[count.index].source_sg_id
  description              = var.source_sg_rules[count.index].description
  security_group_id        = local.this_sg_id
}
resource "aws_security_group_rule" "self" {
  count = var.create ? length(var.self_sg_rules) : 0

  self              = true
  type              = var.source_sg_rules[count.index].type
  from_port         = var.source_sg_rules[count.index].from
  to_port           = var.source_sg_rules[count.index].to
  protocol          = var.source_sg_rules[count.index].protocol
  description       = var.source_sg_rules[count.index].description
  security_group_id = local.this_sg_id
}

使用以下模块块调用它。

security_groups.tf

module "stack_sg" {
  source            = "./modules/aws_security_group"
  name                = "stack-sg"

  vpc_id = module.network.vpc_id

  cidr_sg_rules = [
    { type = "ingress", from = 80, to = 80, protocol = "tcp",  cidr = [module.network.vpc_cidr], description = "http" },
    { type = "egress", from = 0, to = 65535, protocol = "-1",  cidr = ["0.0.0.0/0"], description = "allow all " }
  ]

  source_sg_rules = [
    { type = "ingress", from = 0, to = 65535, protocol = "tcp", source_sg_id = module.alb_sg.sg_id, description = "alb" }
  ]
}

0
为了使用特定键值过滤地图列表,您可以使用以下简单语句:
假设:
- `key` 是您要过滤的地图键 - `val` 是该键的值 - `list` 是原始地图列表
element([ 
    for element in list) : env 
      if element.key == "val" 
   ], 0)

以上语句的结果将会是一个映射表。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接