2

I'm trying to filter the output of 'aws elb describe-tags' and get a LoadBalancerName by matching three tags. I'm not seeing how I can select a specific element of an object list and compare both Key and Value. I have three objects to look against, and if they're all a match I need to return LoadBalancerName.

Here's an example output of three load balancers, with only one having the correct set of tags.

{
    "TagDescriptions": [
        {
            "Tags": [
                {
                    "Value": "production",
                    "Key": "environment"
                },
                {
                    "Value": "widget",
                    "Key": "service"
                },
                {
                    "Value": "widget_xyz",
                    "Key": "customer_prefix"
                },
                {
                    "Value": "widget_xyz-widget-production",
                    "Key": "Name"
                }
            ],
            "LoadBalancerName": "widget-xyz-widget-prod"
        },
        {
            "Tags": [
                {
                    "Value": "widget-xyz-stage-widget-ConsulStack-DKJSADKJS",
                    "Key": "aws:cloudformation:stack-name"
                },
                {
                    "Value": "stage",
                    "Key": "environment"
                },
                {
                    "Value": "arn:aws:cloudformation:us-east-1:123456789:stack/widget-xyz-stage-widget-ConsulStack-DKJSADKJS/d46ad520-92e7-11e5-a975-500150b34c7c",
                    "Key": "aws:cloudformation:stack-id"
                },
                {
                    "Value": "widget",
                    "Key": "service"
                },
                {
                    "Value": "widget_xyz",
                    "Key": "customer_prefix"
                },
                {
                    "Value": "ELB",
                    "Key": "aws:cloudformation:logical-id"
                },
                {
                    "Value": "widget_xyz-widget-stage",
                    "Key": "Name"
                }
            ],
            "LoadBalancerName": "widget-xyz-ELB-SDKJSDKJSADKJAS"
        },
        {
            "Tags": [
                {
                    "Value": "widget-xyz-prod-widget-ConsulStack-DLFJEIJNWDKD",
                    "Key": "aws:cloudformation:stack-name"
                },
                {
                    "Value": "prod",
                    "Key": "environment"
                },
                {
                    "Value": "arn:aws:cloudformation:us-east-1:123456789:stack/widget-xyz-prod-widget-ConsulStack-DLFJEIJNWDKD/ab2292f0-9398-11e5-b0f6-50d501114c2c",
                    "Key": "aws:cloudformation:stack-id"
                },
                {
                    "Value": "widget",
                    "Key": "service"
                },
                {
                    "Value": "widget_xyz",
                    "Key": "customer_prefix"
                },
                {
                    "Value": "ELB",
                    "Key": "aws:cloudformation:logical-id"
                },
                {
                    "Value": "widget_xyz-widget-prod",
                    "Key": "Name"
                }
            ],
            "LoadBalancerName": "widget-xyz-ELB-SKDJSKDJSAKDJAS"
        }
    ]
}

I've successfully implemented my query, but unsafely. It's returning the LoadBalancerName as long as any three values match my search pattern. I would like to search for a specific Key, and then compare the Value.

Here's my unsafe query that is successful on the snippit on gist. It returns widget-xyz-widget-prod, which is the parameter I'm looking to get.

jq --raw-output '.TagDescriptions[] | select(.Tags[].Value=="widget_xyz") | select(.Tags[].Value=="widget") | select(.Tags[].Value=="production") | .LoadBalancerName'

It should return if all three conditions are true:

Key == "service" && Value == "widget"
Key == "environment" && Value == "production"
Key == "customer_prefix" && Value == "widget_xyz"

As you can see in my query above I am only comparing Value.

UPDATE: I've been able to construct a query that filters against matching both Key and Value from one object, but I'm still trying to work on matching more than one object.

.TagDescriptions[] | select(.Tags[].Key=="customer_prefix" and .Tags[].Value == "widget_xyz") | .LoadBalancerName

ANOTHER UPDATE: Ok, so, I've been able to hack a query together. I feel as if I'm still missing a piece of the puzzle, and this query can be greatly simplified using some slick feature of jq that I have yet to understand.

.TagDescriptions[] | [select(.Tags[].Key == "customer_prefix" and .Tags[].Value == "widget_xyz")][] | [select(.Tags[].Key == "environment" and .Tags[].Value == "production")][] | [select(.Tags[].Key == "service" and .Tags[].Value == "widget")][] | .LoadBalancerName

2
  • Hi brian! Can you add an example of what the expected output would be like? Commented Nov 28, 2015 at 17:51
  • hi Santiago! I've edited my question to include a more complete json snippit. I've hopefully explained my question a little better. I have a working query but it's unsafe. I'm looking to improve this query to make sure a specific Key has a specific Value. right now my query returns as long as any three Values match my three constraints. Commented Nov 28, 2015 at 18:29

2 Answers 2

4

The Tags array are perfect for creating an object out of for easy access. Make things easy on yourself and do so. Then access to the values would be significantly easier. Then you could easily test to see if your conditions are satisfied.

.TagDescriptions[] | select(
    .Tags | from_entries | [
        .service == "widget",
        .environment == "production",
        .customer_prefix == "widget_xyz"
    ] | all
).LoadBalancerName
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you - I really like this approach.
0

There's more than one way to skin a jq cat, but one of the main keys to obtaining a crisp solution is the ability to define helper functions, which in fact can be nested. Here at any rate is a solution that uses a helper function which does have an inner function.

# Does "obj" match any of the objects in the input array?
def anymatch(obj):
  # Do all the key-value pairs in obj also occur in the input?
  def match(obj):
    . as $in
    | obj as $o
    | all( $o|keys[]; $in[.] == $o[.]);
  any(.[]; match(obj));

.TagDescriptions[]
| select( .Tags
        | (anymatch({"Key":"customer_prefix", "Value": "widget_xyz"})
           and anymatch({"Key":"environment", "Value": "production"})
           and anymatch({"Key":"service", "Value": "widget"} ) ))
| .LoadBalancerName

For the given input, this produces: "widget-xyz-widget-prod"

(By the way, I don't think your "ANOTHER UPDATE" solution is a valid solution to the stated problem, at least as I understand it.)

2 Comments

Thank you. Regarding the solution stated in "ANOTHER UPDATE", could you please share what you see wrong?
Try swapping a pair of values within the .Tags of the "widget-xyz-widget-prod" block.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.