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

时间:2020-05-21 17:15:08

标签: json amazon-web-services terraform jq terraform-provider-aws

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

{"string": "string"}

Here是公共范围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公共IP范围文档提取到单个对象中,最有效的方法是什么?

2 个答案:

答案 0 :(得分:2)

以下脚本使用.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 '

答案 1 :(得分:0)

还有一种更好的方法来获取Terraform中的AWS IP Range数据,即使用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中通过所有服务使用AWS拥有的每个IP地址访问数据库。这意味着世界上任何人都可以在US-West-2中启动EC2实例或Lambda函数,然后可以通过网络访问您的数据库。这是一个非常糟糕的主意。

第二个是,如果返回的CIDR块超过60个,那么您的安全组将最终获得60条以上的规则。每个IP地址类型(IPv4与IPv6)以及每个入口/出口,AWS安全组的限制为60个安全组规则:

每个安全组可以有60条入站规则和60条出站规则(总共有120条规则)。此配额是针对IPv4规则和IPv6规则分别执行的;例如,一个安全组可以有60条针对IPv4流量的入站规则和60条针对IPv6流量的入站规则。引用安全组或前缀列表ID的规则对于IPv4来说算作一个规则,对于IPv6来说算作一个规则。

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

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