在JMESPath中使用过滤器时,如何将一个数组级别的字段与嵌套数组级别的字段联接在一起

时间:2019-07-17 07:30:32

标签: jmespath

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

我以为我已经尽力解决了一个复杂的JMESPath查询的每个部分,其中每个部分都是独立工作的,但是结合起来,我会发现一个错误,我无法弄清。

我目前还具有两个过滤器,当第二个数组级别的匹配集为null时,消除第一个数组值似乎是必需的,这似乎令人费解,我想知道是否有更优雅的方法来获得相同的结果

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

大多数灵感来自这里: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"

1 个答案:

答案 0 :(得分:0)

我自己弄清楚了。在嵌套的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,然后将具有空IpRanges的结果传递给第二个查询,以查找经过修剪的那个。也许有一天...