【问题标题】:terraform: filter list of maps based on keyterraform:根据键过滤地图列表
【发布时间】:2021-05-03 12:00:00
【问题描述】:

我正在实现一个安全组模块,这样它将通过获取和过滤 cidr 和 source_security_group_id 创建安全组规则来创建安全组规则。

当前模块配置。

安全组模块.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
  ....
}

所以,我正在寻找一种方法来根据 key

从列表中过滤地图

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

【问题讨论】:

    标签: terraform terraform-provider-aws terraform0.12+ hcl


    【解决方案1】:

    我想出了一个聪明的方法来做到这一点。

    假设我正在尝试从宠物列表中仅过滤属于猫 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. 接下来创建分别匹配条件pet.kind == "cat"的键的过滤列表 通过循环 pets_map 中的键并将不匹配的相应键设置为 空字符串。然后压缩从列表中删除空字符串的列表。
    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
      }
      

      然后,在新模块中,使用 count 语句作为测试来创建可选项目:

      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
      }
      

      这会将资源创建为一个包含一个或零个项目的数组,并删除所有零个列表。

      【讨论】:

        【解决方案3】:

        感谢@dan-monego 的回复。

        我用单个模块 itslef 整理出来。

        以下是模块文件。

        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" }
          ]
        }
        

        【讨论】:

          【解决方案4】:

          为了使用特定键值过滤地图列表。您可以使用以下简单语句:

          假设:

          • key 是您要过滤的地图键
          • val 是键的值
          • list 是原始地图列表
          element([ 
              for element in list) : env 
                if element.key == "val" 
             ], 0)
          

          上述语句的结果将是一个地图。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-05-17
            • 2020-09-11
            • 2020-02-06
            • 2017-04-22
            • 2021-11-25
            相关资源
            最近更新 更多