【问题标题】:How to join fields from one array level with fields from a nested array level when using filters in JMESPath在 JMESPath 中使用过滤器时,如何将一个数组级别的字段与嵌套数组级别的字段连接起来
【发布时间】:2019-07-17 07:30:32
【问题描述】:

我正在尝试从 aws ec2 describe-security-groups 输出中提取一些数据,寻找具有以值开头的描述(即 Temp-JDoe ...)的入口规则,使用过滤器来限制结果返回。我得到了我想要的结果,但是在一个带有嵌入式数组的深度嵌套数组中。我想将第一级的所有值与嵌套级别的值结合起来,并用“:”连接,这样我就可以遍历生成的多行字符串,创建一个 aws ec2 revoke-security-group-ingress命令删除这些规则。

我以为我已经煞费苦心地解决了一个复杂的 JMESPath 查询的每个部分,其中每个部分独立工作,但是当结合起来时,我遇到了一个我无法弄清楚的错误。

我目前还有两个过滤器,当第二个数组级别的匹配集为空时,这似乎是消除第一级数组值所必需的,这看起来很复杂,我想知道是否有更优雅的方法来获得相同的结果.

我们的目标是创建一对 bash 脚本,当云管理员必须在未知位置工作时,它们可以运行以快速添加和删除临时安全组规则到堡垒主机安全组。我认为这可能是其他人会遇到的问题。在创建时,我们要先清除所有具有“Temp-JDoe *”描述的规则,然后创建所需的具有“Temp-JDoe (SSH)”描述的包含当前 IP 的集合,因此当工作完成时,规则可以再次通过此查询找到并删除。

大部分灵感都来自这里:https://opensourceconnections.com/blog/2015/07/27/advanced-aws-cli-jmespath-query/

我在这里发现了一个类似的问题:JMESPath - Joining items in a nested array,但由于我使用了过滤器,这不是一个很好的匹配。

这个语句返回我想要的数据。显示 json 输出格式以显示我要折叠和加入的结构:

prefix=Temp
username=JDoe
aws ec2 describe-security-groups --group-id $sg \
                                 --query 'SecurityGroups[].IpPermissions[?not_null(IpRanges[?Description!=`null`]|[?starts_with(Description, `'$prefix-$username'`) == `true`])].[IpProtocol,FromPort,ToPort,IpRanges[?Description!=`null`]|[?starts_with(Description, `'$prefix-$username'`) == `true`].[CidrIp,Description]]' \
                                 --profile $profile --region $region --output json

有了这个原始输出:

[
    [
        [
            "tcp",
            22,
            22,
            [
                [
                    "77.111.222.223/32",
                    "Temp-JDoe (SSH)"
                ]
            ]
        ],
        [
            "udp",
            1194,
            1194,
            [
                [
                    "77.111.222.223/32",
                    "Temp-JDoe (VPN-UDP)"
                ]
            ]
        ]
    ]
]

我需要进入这个最终形式:

udp:1194:1194:70.185.154.223/32:Temp-JDoe (VPN-UDP)
tcp:22:22:70.185.154.223/32:Temp-JDoe (SSH)

虽然这个带有连接的语句在没有过滤器的情况下可以限制数组数据(在外层获取所有数据,在内层获取第一个):

aws ec2 describe-security-groups --group-id $sg \
                                 --query 'SecurityGroups[].IpPermissions[].[join(`:`,[IpProtocol,to_string(FromPort),to_string(ToPort),IpRanges[0].join(`:`,[CidrIp,Description])])]' \
                                 --profile $profile --region $region --output text

示例输出(错误的记录,正确的格式):

[
    [
        "tcp:22:22:111.55.111.123/32:Home-FName (SSH)"
    ],
    [
        "udp:1194:1194:111.55.111.123/32:Home-FName (VPN-UDP)"
    ],
    [
        "tcp:943:943:70.185.154.223/32:Home-JDoe (Console)"
    ],
    [
        "tcp:443:443:111.55.111.123/32:Home-FName (VPN-TCP)"
    ],
    [
        "icmp:-1:-1:192.66.55.0/24:Office-NewYork (ICMP)"
    ]
]

一旦我结合了两个工作子集来过滤我想要的数据,然后加入它,它就不起作用了,第二个加入有问题。这是合并后的声明:

aws ec2 describe-security-groups --group-id $sg \
                                 --query 'SecurityGroups[].IpPermissions[?not_null(IpRanges[?Description!=`null`]|[?starts_with(Description, `'$prefix-$username'`) == `true`])].[join(`:`,[IpProtocol,to_string(FromPort),to_string(ToPort),IpRanges[?Description!=`null`]|[?starts_with(Description, `'$prefix-$username'`) == `true`].join(`:`,[CidrIp,Description])])]' \
                                 --profile $profile --region $region --output text

还有错误信息:

In function join(), invalid type for value: ['70.185.154.223/32:Temp-JDoe (VPN-UDP)'], expected one of: ['array-string'], received: "list"

【问题讨论】:

    标签: jmespath


    【解决方案1】:

    这是我自己想出来的。在嵌套的 IpRanges 数组中,[0] 将第一个元素作为字符串返回,而 [?starts...] 过滤器表示法返回一个字符串数组,即使它只是一个字符串。因此,我们可以通过管道获取第一个元素:

    aws ec2 describe-security-groups --group-id $sg \
                                     --query 'SecurityGroups[].IpPermissions[?not_null(IpRanges[?Description!=`null`]|[?starts_with(Description, `'$prefix-$username'`) == `true`])].[join(`:`,[IpProtocol,to_string(FromPort),to_string(ToPort),IpRanges[?Description!=`null`]|[?starts_with(Description, `'$prefix-$username'`) == `true`]|[0].join(`:`,[CidrIp,Description])])]' \
                                     --profile $profile --region $region --output text
    

    请注意,这确实存在一个缺点 - 它只返回可能有多个 CIDR 且具有匹配描述的第一个。但是,由于这个查询已经足够复杂,而且对于我的用例来说,一个通常是唯一的结果,我只是在脚本中循环,该脚本调用它以在删除第一次迭代中找到的内容后检查其他规则。

    如果有一种更高效、更简单的方法来运行这两个过滤器,删除第一个 not_null,并将带有 null IpRanges 的结果通过管道传送到第二个查询,以寻找修剪过的那个,这仍然会很好。也许改天……

    【讨论】:

      猜你喜欢
      • 2019-12-02
      • 1970-01-01
      • 1970-01-01
      • 2023-03-10
      • 2018-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多