使用 JQ 将字符串数组转换为字典?

Convert an array of strings to a dictionary with JQ?

我试图将 AWS public IP 范围转换为可与 Terraform external 数据提供程序一起使用的格式,以便我可以创建基于 AWS public CIDR。提供程序需要一个具有以下格式的 JSON 对象:

{"string": "string"}

Here 是 public 范围 JSON 文档的片段:

{
  "syncToken": "1589917992",
  "createDate": "2020-05-19-19-53-12",
  "prefixes": [
    {
      "ip_prefix": "35.180.0.0/16",
      "region": "eu-west-3",
      "service": "AMAZON",
      "network_border_group": "eu-west-3"
    },
    {
      "ip_prefix": "52.94.76.0/22",
      "region": "us-west-2",
      "service": "AMAZON",
      "network_border_group": "us-west-2"
    },
    // ...
]

我可以成功提取我关心的范围,[.prefixes[] | select(.region == "us-west-2") | .ip_prefix] | sort | unique,它给了我这个:

[
  "100.20.0.0/14",
  "108.166.224.0/21",
  "108.166.240.0/21",
  "13.248.112.0/24",
  ...
]

我不知道如何使用 jq 将其转换为任意键控对象。为了正确使用数组对象,我需要将它转换为字典,类似于 {"arbitrary-key": "100.20.0.0/14"},这样我就可以像这样在 Terraform 中使用它:

data "external" "amazon-ranges" {
  program = [
    "cat",
    "${path.cwd}/aws-ranges.json"
    ]
}

resource "aws_default_security_group" "allow-mysql" {
  vpc_id = aws_vpc.main.id

  ingress {
    description = "MySQL"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = [
      values(data.external.amazon-ranges.result)
    ]
  }
}

将 AWS public IP 范围文档提取到具有任意键的单个对象的最有效方法是什么?

以下脚本使用 .ip_prefix 作为键,因此可能避免了对 sort|unique 的需要。它产生:

{
  "35.180.0.0/16": "35.180.0.0/16",
  "52.94.76.0/22": "52.94.76.0/22"
}

脚本

#!/bin/bash

function data {
    cat <<EOF
{
  "syncToken": "1589917992",
  "createDate": "2020-05-19-19-53-12",
  "prefixes": [
    {
      "ip_prefix": "35.180.0.0/16",
      "region": "eu-west-3",
      "service": "AMAZON",
      "network_border_group": "eu-west-3"
    },
    {
      "ip_prefix": "52.94.76.0/22",
      "region": "us-west-2",
      "service": "AMAZON",
      "network_border_group": "us-west-2"
    }
    ]
}
EOF
}

data | jq '
  .prefixes 
  | map(select(.region | test("west"))
        | {(.ip_prefix): .ip_prefix} )
  | add '

在 Terraform 中获取 AWS IP 范围数据有一个更好的选择,即使用 aws_ip_ranges data source,而不是试图用 external 数据源和 jq.

上述链接文档中的示例显示了与您在此处尝试执行的操作类似但稍微复杂的操作:

data "aws_ip_ranges" "european_ec2" {
  regions  = ["eu-west-1", "eu-central-1"]
  services = ["ec2"]
}

resource "aws_security_group" "from_europe" {
  name = "from_europe"

  ingress {
    from_port        = "443"
    to_port          = "443"
    protocol         = "tcp"
    cidr_blocks      = data.aws_ip_ranges.european_ec2.cidr_blocks
    ipv6_cidr_blocks = data.aws_ip_ranges.european_ec2.ipv6_cidr_blocks
  }

  tags = {
    CreateDate = data.aws_ip_ranges.european_ec2.create_date
    SyncToken  = data.aws_ip_ranges.european_ec2.sync_token
  }
}

要准确执行您的操作,您需要执行以下操作:

data "aws_ip_ranges" "us_west_2_amazon" {
  regions  = ["us_west_2"]
  services = ["amazon"]
}

resource "aws_default_security_group" "allow-mysql" {
  vpc_id = aws_vpc.main.id

  ingress {
    description = "MySQL"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = data.aws_ip_ranges.us_west_2_amazon.cidr_blocks
  }
}

但是,这里有两点不好。

首先也是最重要的一点是,您允许从 AWS 在 US-West-2 的所有服务中拥有的每个 IP 地址访问您的数据库。这意味着世界上任何人都能够在 US-West-2 中启动 EC2 实例或 Lambda 函数,然后通过网络访问您的数据库。这是一个非常糟糕的主意。

第二个是,如果 returns 超过 60 个 CIDR 块,您最终将在安全组中拥有超过 60 条规则。 AWS 安全组对每个 IP 地址类型(IPv4 与 IPv6)和每个 ingress/egress:

有 60 个安全组规则的限制

You can have 60 inbound and 60 outbound rules per security group (making a total of 120 rules). This quota is enforced separately for IPv4 rules and IPv6 rules; for example, a security group can have 60 inbound rules for IPv4 traffic and 60 inbound rules for IPv6 traffic. A rule that references a security group or prefix list ID counts as one rule for IPv4 and one rule for IPv6.

来自https://docs.aws.amazon.com/vpc/latest/userguide/amazon-vpc-limits.html#vpc-limits-security-groups

这在技术上是一个软上限,您可以要求 AWS 提高此限制,以换取减少可应用于网络接口的安全组数量,以将安全组规则的最大数量保持在 1000 或以下每个网络接口。不过,这可能不是您想要解决的问题。