使用Terraform中的两个列表生成笛卡尔积

时间:2018-02-15 19:13:08

标签: terraform

我在terraform模块中有两个列表......

ingress_ports = [
    {
      from_port   = 80
      to_port     = 80
      protocol    = "tcp"
    },
    {
      from_port   = 443
      to_port     = 443
      protocol    = "tcp"
    },
    {
      from_port   = 22
      to_port     = 22
      protocol    = "tcp"
    }
]

我还有另一个网络ACL设置列表

result = [
  {
      cidr_block = "1.2.3.4/32"
      from_port   = 80
      to_port     = 80
      protocol    = "tcp"
   },
   {
      cidr_block = "1.2.3.4/32"
      from_port   = 443
      to_port     = 443
      protocol    = "tcp"
    },
    {
      cidr_block = "1.2.3.4/32"
      from_port   = 22
      to_port     = 22
      protocol    = "tcp"
    },
  {
      cidr_block = "5.6.7.8/32"
      from_port   = 80
      to_port     = 80
      protocol    = "tcp"
   },
   {
      cidr_block = "5.6.7.8/32"
      from_port   = 443
      to_port     = 443
      protocol    = "tcp"
    },
    {
      cidr_block = "5.6.7.8/32"
      from_port   = 22
      to_port     = 22
      protocol    = "tcp"
    }
]

我希望将这些列表连接在一起,以便我可以使用结果列表创建网络ACL。

for(int index = 0; index < 13; index++)
{
    if(inFile.nextInt() < 0)
    {
       pos[index] = inFile.nextInt(); 
    }
    else
    {
        neg[index] = inFile.nextInt();
    }
}

这种东西在Terraform中是否可行?

5 个答案:

答案 0 :(得分:1)

这就是我想出的。我记得有一种方法可以使用模和整数除法来执行我想要做的事情。我为入口和出口做了这个,但我只会显示入口。为了帮助在模块中灵活使用它,我在模块外部创建了网络ACL并将其传入。

Example module on Github

变量:

variable "acl-id" {}
variable "offset" {}

variable "ingress-rules" {
  type = "list"
  description = "The List of Ingress Rules. Each item in the list is a map.  The Maps will be joined with the 'ingress-cidr'"
}

variable "ingress-cidr" {
  type = "list"
  description = "List of IPv4 CIDR ranges to apply to all ingress rules"
}

当地人:为了清楚起见,我创造了这些。

locals {
  ingress-cidr-size = "${length( var.ingress-cidr )}"
  ingress-rules-size = "${length( var.ingress-rules )}"

  ingress-join-size = "${local.ingress-cidr-size * local.ingress-rules-size}"

  ingress-joined-rules-cidrs = "${data.null_data_source.ingress-join.*.outputs}"
}

数据:

#Perform a cartesian like join of all of the CIDRs to apply to all of the rules
data "null_data_source" "ingress-join" {

  count = "${local.ingress-join-size}"

  inputs = {
    rule-number = "${count.index + var.offset}"
    cidr-block = "${ var.ingress-cidr[count.index / local.ingress-rules-size] }"
    from-port = "${ lookup ( var.ingress-rules[count.index % local.ingress-rules-size],  "from-port" ) }"
    to-port = "${ lookup ( var.ingress-rules[count.index % local.ingress-rules-size],  "to-port" ) }"
    action = "${ lookup ( var.ingress-rules[count.index % local.ingress-rules-size],  "action" ) }"
    protocol = "${ lookup ( var.ingress-rules[count.index % local.ingress-rules-size],  "protocol" ) }"
  }
}

网络ACL规则配置:

##########################
# Ingress - Maps of rules
##########################
# Takes a list of 'ingress-rules' where each list item is a Map with the following keys
#  action: ether "allow" or "deny"
#  from-port: a port number
#  to-port:   a port number
#  protocol: A string like "tpc" or "-1"
#  rule-number:  A unique value to prevent collisions with other rules
#  cidr-block:  The CIDR that is applied to this rule
resource "aws_network_acl_rule" "ingress-rules-and-cidr-list" {

  count = "${var.create ? local.ingress-join-size : 0}"

  network_acl_id = "${var.acl-id}"
  egress              = false

  rule_number     = "${lookup( local.ingress-joined-rules-cidrs[count.index], "rule-number", "")}"
  rule_action     = "${lookup( local.ingress-joined-rules-cidrs[count.index], "action", "")}"

  cidr_block     = "${lookup(local.ingress-joined-rules-cidrs[count.index], "cidr-block", "")}"

  from_port    = "${lookup(local.ingress-joined-rules-cidrs[count.index], "from-port", "")}"
  to_port     = "${lookup(local.ingress-joined-rules-cidrs[count.index], "to-port", "")}"
  protocol    = "${lookup(local.ingress-joined-rules-cidrs[count.index], "protocol", "")}"

}

答案 1 :(得分:1)

这是我使用的解决方案 下面是您的测试Terraform脚本

provider "aws" {
    region = "us-east-1"
}

variable "lista" {
    default = ["1", "2", "3"]
}

variable "listb" {
    default = ["A", "B", "C", "D"]
}

resource "aws_eip" "eip" {
    count = "${length(var.lista) * length(var.listb)}"
    tags {
        Name = "test-eip ${count.index}. ${element(var.lista, ceil(count.index/length(var.listb)))}:${element(var.listb, count.index)}"
    }
}

测试运行     地形图| grep标签。名称

下面是输出

tags.Name: "0. 1:A"
tags.Name: "1. 1:B"
tags.Name: "2. 1:C"
tags.Name: "3. 1:D"
tags.Name: "4. 2:A"
tags.Name: "5. 2:B"
tags.Name: "6. 2:C"
tags.Name: "7. 2:D"
tags.Name: "8. 3:A"
tags.Name: "9. 3:B"
tags.Name: "10. 3:C"
tags.Name: "11. 3:D"

我的用例是将多个对等映射到多个路由表(每个AZ / NAT一个)。

答案 2 :(得分:0)

我在下面的链接中提出了另一种方法-

https://github.com/hashicorp/terraform/issues/6657#issuecomment-453304599

相同的代码段-

我一直在寻找可合并两个列表的解决方案,并发现使用以下格式的解决方案-

locals {
  a_identifiers = [
    "arn:aws:iam::hhhhjjjjj:role/account_dev",
    "arn:aws:iam::hhhhjjjjj:role/account_stage",
  ]
}
locals {
  b_identifiers = [
    "arn:aws:iam::hhhhjjjjj:role/account_qa",
    "arn:aws:iam::hhhhjjjjj:role/account_prod",
  ]
}
locals {
  combo_identifiers = "${concat(a_identifiers,b_identifiers)}"
}

这是我以后如何使用它的方法-

data "aws_iam_policy_document" "access_policy" {
  statement {
    effect = "Allow"

    principals {
      type = "AWS"

      identifiers = ["${local.combined_identifiers}"]
    }

    actions = [
      "ec2:*",
    ]

    resources = [
      "*",
    ]
  }
}

希望有人会发现它有用

答案 3 :(得分:0)

@grbonk,我明白你在做什么。非常感谢您的指点。刚开始时,很难理解您的代码,因为它具有特定于场景的资源和变量,并且还没有变量值,因此看不到输出。但是,重新阅读有所帮助,下面是我的示例代码,希望有人能对您有所帮助。我将原帖保持原样,以便读者可以看到您指出的简单列表和地图列表的局限性。

我学到了几件事 1. element()仅支持简单列表(平面列表),不支持地图列表 2.当除法用作列表索引时,ceil()是隐式的 3.了解了一些有关null_data_source

的信息

这是代码

provider "aws" {
  region = "us-east-1"
}

variable "players" {
  type = "list"
  default = [
    {name = "Paul Pierce" position = "Small Forward"},
    {name = "Kevin Garnett" position = "Power Forward"},
    {name = "Ray Allen" position = "Shooting Guard"}
  ]
}

variable "teams" {
  type = "list"
  default = [
    {name = "Brooklyn Nets" conference = "eastern"},
    {name = "La Lakers" conference = "western"},
    {name = "Miami Heat" conference = "eastern"}
  ]
}

variable "fit" {
  type = "list"
  default = ["good", "bad", "ugly"]
}

data "template_file" "trade_options" {
  count = "${length(var.players) * length(var.teams)}"
  template = <<EOF
  "$${optionid}. Trade $${player} ($${position}) to $${team} ($${conference}-conference) and its $${fit}"
EOF


  vars = {
    optionid = "${count.index}"
    player = "${lookup(var.players[count.index/length(var.teams)], "name")}"
    position = "${lookup(var.players[count.index/length(var.teams)], "position")}"
    team = "${lookup(var.teams[count.index % length(var.teams)], "name")}"
    conference = "${lookup(var.teams[count.index % length(var.teams)], "conference")}"
    fit = "${var.fit[count.index % length(var.fit)]}"
  }
}

output "trade-options" {
  value = "${data.template_file.trade_options.*.rendered}"
}

以下是运行terraform apply | grep Trade时的输出

"0. Trade Paul Pierce (Small Forward) to Brooklyn Nets (eastern-conference) and its good"
"1. Trade Paul Pierce (Small Forward) to La Lakers (western-conference) and its bad"
"2. Trade Paul Pierce (Small Forward) to Miami Heat (eastern-conference) and its ugly"
"3. Trade Kevin Garnett (Power Forward) to Brooklyn Nets (eastern-conference) and its good"
"4. Trade Kevin Garnett (Power Forward) to La Lakers (western-conference) and its bad"
"5. Trade Kevin Garnett (Power Forward) to Miami Heat (eastern-conference) and its ugly"
"6. Trade Ray Allen (Shooting Guard) to Brooklyn Nets (eastern-conference) and its good"
"7. Trade Ray Allen (Shooting Guard) to La Lakers (western-conference) and its bad"
"8. Trade Ray Allen (Shooting Guard) to Miami Heat (eastern-conference) and its ugly"

答案 4 :(得分:0)

在terraform 0.12中,我们最终可以使用setproduct,这使得这项工作变得更加容易:

cronjobs = [
  {
    schedule_expression   = "cron(5 0 * * ? *)"
    command_and_arguments = ["app/aws-console.sh", "task1"]
    description           = "Hello World"
  },
  {
    schedule_expression   = "cron(0 5 1 * ? *)"
    command_and_arguments = ["app/aws-console.sh", "task2"]
    description           = "Send Bills"
  }
]

environments = ["dev", "stage"]

locals {
  cronjobs_for_all_environments = setproduct(var.cronjobs, var.environments)
}

resource "aws_cloudwatch_event_rule" "cronjob" {
  count               = length(local.cronjobs_for_all_environments)
  name                = "cronjob-${local.cronjobs_for_all_environments[count.index][1]}"
  description         = "${local.cronjobs_for_all_environments[count.index][0].description} (${local.cronjobs_for_all_environments[count.index][1]})"
  schedule_expression = local.cronjobs_for_all_environments[count.index][0].schedule_expression
}