How do I iterate over CIDR blocks in JQ? - json

Basically I'm trying to iterate through my AWS security groups to find any CIDR's using 0.0.0.0/0.
Here is my example JSON file:
{
"SecurityGroups": [
{
"IpPermissionsEgress": [],
"Description": "AWS OpsWorks load balancer - do not change or delete",
"IpPermissions": [
{
"PrefixListIds": [],
"FromPort": 22,
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"ToPort": 22,
"IpProtocol": "tcp",
"UserIdGroupPairs": [],
"Ipv6Ranges": []
},
{
"PrefixListIds": [],
"FromPort": 80,
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"ToPort": 80,
"IpProtocol": "tcp",
"UserIdGroupPairs": [],
"Ipv6Ranges": []
},
{
"PrefixListIds": [],
"FromPort": 443,
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"ToPort": 443,
"IpProtocol": "tcp",
"UserIdGroupPairs": [],
"Ipv6Ranges": []
}
],
"GroupName": "AWS-OpsWorks-LB-Server",
"OwnerId": "056146032236",
"GroupId": "sg-7dd13739"
},
{
"IpPermissionsEgress": [
{
"IpProtocol": "-1",
"PrefixListIds": [],
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"UserIdGroupPairs": [],
"Ipv6Ranges": []
}
],
"Description": "SG for bastion hosts",
"Tags": [
{
"Value": "bastion-host-sg",
"Key": "Name"
}
],
"IpPermissions": [
{
"PrefixListIds": [],
"FromPort": 80,
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"ToPort": 80,
"IpProtocol": "tcp",
"UserIdGroupPairs": [],
"Ipv6Ranges": []
},
{
"PrefixListIds": [],
"FromPort": 1991,
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"ToPort": 1991,
"IpProtocol": "tcp",
"UserIdGroupPairs": [],
"Ipv6Ranges": []
},
{
"PrefixListIds": [],
"FromPort": 8080,
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"ToPort": 8080,
"IpProtocol": "tcp",
"UserIdGroupPairs": [],
"Ipv6Ranges": []
},
{
"PrefixListIds": [],
"FromPort": 1194,
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"ToPort": 1194,
"IpProtocol": "udp",
"UserIdGroupPairs": [],
"Ipv6Ranges": []
},
{
"PrefixListIds": [],
"FromPort": 22,
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"ToPort": 22,
"IpProtocol": "tcp",
"UserIdGroupPairs": [],
"Ipv6Ranges": [
{
"CidrIpv6": "::/0"
}
]
},
{
"PrefixListIds": [],
"FromPort": 30,
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"ToPort": -1,
"IpProtocol": "icmp",
"UserIdGroupPairs": [],
"Ipv6Ranges": []
},
{
"PrefixListIds": [],
"FromPort": 1194,
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"ToPort": 1194,
"IpProtocol": "tcp",
"UserIdGroupPairs": [],
"Ipv6Ranges": []
},
{
"PrefixListIds": [],
"FromPort": 53,
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"ToPort": 53,
"IpProtocol": "udp",
"UserIdGroupPairs": [],
"Ipv6Ranges": []
},
{
"PrefixListIds": [],
"FromPort": 53,
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"ToPort": 53,
"IpProtocol": "tcp",
"UserIdGroupPairs": [],
"Ipv6Ranges": []
},
{
"PrefixListIds": [],
"FromPort": 443,
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"ToPort": 443,
"IpProtocol": "tcp",
"UserIdGroupPairs": [],
"Ipv6Ranges": []
},
{
"PrefixListIds": [],
"FromPort": 8,
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"ToPort": -1,
"IpProtocol": "icmp",
"UserIdGroupPairs": [],
"Ipv6Ranges": []
}
],
"GroupName": "bastion-host-sg",
"VpcId": "vpc-effd0e8a",
"OwnerId": "056146032236",
"GroupId": "sg-0f60196a"
}
]
}
Here is the command I'm trying to run, but get an error:
$ cat sg-small.json | jq '.SecurityGroups[].IpPermissions[].IpRanges[] | map(select(any(.CidrIp == "0.0.0.0/0")))'
jq: error (at <stdin>:227): Cannot iterate over string ("0.0.0.0/0")
Any thoughts as to why this is happening. That method seems to work for other things that aren't IP addresses.

Your expression:
.SecurityGroups[].IpPermissions[].IpRanges[]
is just a stream of CidrIp objects, so that's not what you want.
The following will select the SecurityGroups that meet the criterion:
.SecurityGroups[]
| select(any( .IpPermissions[].IpRanges[]; .CidrIp == "0.0.0.0/0"))
Whether this is precisely what you want is unclear since you haven't specified that. Please see http://stackoverflow.com/help/mcve

The reason you're seeing the error
Cannot iterate over string ("0.0.0.0/0")
is because with your data the first portion of your filter
.SecurityGroups[].IpPermissions[].IpRanges[]
generates a sequence of objects
{
"CidrIp": "0.0.0.0/0"
}
...
The map portion of your filter iterates over the values in each of these objects, passing
"0.0.0.0/0"
to the expression select(any(.CidrIp == "0.0.0.0/0")) where any tries to iterate over all the values within the string and fails with the error you observe.
If you only want to see a sequence of {"CidrIp":...} objects as above you can eliminate the map and any:
.SecurityGroups[].IpPermissions[].IpRanges[]
| select(.CidrIp == "0.0.0.0/0")
If you would rather collect those objects into an array you could remove the any and move some of the iteration into the map e.g.
.SecurityGroups
| map(.IpPermissions[].IpRanges[] | select(.CidrIp == "0.0.0.0/0"))
producing
[
{
"CidrIp": "0.0.0.0/0"
},
....
The explict construction of the result array is easier to see if you replace map with its definition. Since map(f) is defined as [ .[] | f ] the above filter is the same as this one:
.SecurityGroups
| [ .[] | .IpPermissions[].IpRanges[] | select(.CidrIp == "0.0.0.0/0") ]

Related

jq: How to substitute "null" from the output? [duplicate]

This question already has answers here:
Get or default function in JQ?
(2 answers)
Closed 2 years ago.
I have the following JSON:
{
"SecurityGroups": [
{
"Description": "launch-wizard-6 created 2018-10-25T13:59:20.092+03:00",
"GroupName": "launch-wizard-6",
"IpPermissions": [
{
"IpProtocol": "-1",
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"Ipv6Ranges": [],
"PrefixListIds": [],
"UserIdGroupPairs": []
},
{
"FromPort": 22,
"IpProtocol": "tcp",
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"Ipv6Ranges": [],
"PrefixListIds": [],
"ToPort": 22,
"UserIdGroupPairs": []
}
],
"OwnerId": "XXXXXXXXXXXX",
"GroupId": "sg-054368f50a6b4fea4",
"IpPermissionsEgress": [
{
"IpProtocol": "-1",
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"Ipv6Ranges": [
{
"CidrIpv6": "::/0"
}
],
"PrefixListIds": [],
"UserIdGroupPairs": []
}
],
"VpcId": "vpc-6ea82a08"
}
]
}
When I run the following jq command:
aws ec2 describe-security-groups --group-id ${group_id} --profile ${profile} --region ${region} --output json | jq -r '.SecurityGroups[].IpPermissions[] | [ (.FromPort|tostring), .IpProtocol, .IpRanges[].CidrIp // .UserIdGroupPairs[].GroupId // "" ] | #tsv'
I get the following output:
null -1 0.0.0.0/0
22 tcp 0.0.0.0/0
I'd like to remove the null from the output, how can it be done?
Add a fallback for the .FromPort key:
.FromPort // ""
So the command becomes;
.SecurityGroups[].IpPermissions[] | [ (.FromPort // "" ?), .IpProtocol, .IpRanges[].CidrIp ] | #tsv
Which yields to
-1 0.0.0.0/0
22 tcp 0.0.0.0/0
Try it online!

How to display a map which contains two values of two different keys?

As part of a shell script I'm writing, I'm querying AWS (cli) to pull information regarding available security group names and ids, like so:
aws ec2 describe-security-groups | jq -r '.SecurityGroups[]'
{
"IpPermissionsEgress": [
{
"IpProtocol": "-1",
"PrefixListIds": [],
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"UserIdGroupPairs": [],
"Ipv6Ranges": []
}
],
"Description": "default VPC security group",
"IpPermissions": [
{
"PrefixListIds": [],
"FromPort": 80,
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"ToPort": 80,
"IpProtocol": "tcp",
"UserIdGroupPairs": [],
"Ipv6Ranges": [
{
"CidrIpv6": "::/0"
}
]
},
{
"PrefixListIds": [],
"FromPort": 22,
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"ToPort": 22,
"IpProtocol": "tcp",
"UserIdGroupPairs": [],
"Ipv6Ranges": []
},
{
"PrefixListIds": [],
"FromPort": -1,
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"ToPort": -1,
"IpProtocol": "icmp",
"UserIdGroupPairs": [],
"Ipv6Ranges": []
}
],
"GroupName": "default",
"VpcId": "vpc-b3c29bcb",
"OwnerId": "506490286752",
"GroupId": "sg-83db2ef7"
}
And using jq, I'm trying to return a list of maps which displays the info like so:
GroupName , GroupId
This is what I've tried:
aws ec2 describe-security-groups | jq -r '.SecurityGroups[] | to_entries[] | [ .GroupName.value , .GroupId.value]'
Using the above method returns:
[
null,
null
]
[
null,
null
]
In the example, the delimiter is "," but I'd like the displayed output to be like so (example):
"default - sg-abd837s"
How can it be done by using jq?
Using string interpolation:
.SecurityGroups[] | "\(.GroupName) - \(.GroupId)"
There should be no need to use to_entries:
.SecurityGroups[]
| [ .GroupName, .GroupId ]
| join(" - ")
produces:
"default - sg-abd837s"

jq iteration and conditions

I have some json that I need to parse using jq. I am trying to extract the
SecurityGroups>GroupId if any of the SecurityGroups>IpPermissions>IpRanges>CidrIp matches a certain IP.
For instance, searching for 11.11.11.11 should return sg-3jf32kj3j. There is a possibility that multiple SecurityGroups will contain that IP. I need to return every GroupId that does.
Is this even possible with jq alone or will it require bash as well?
I'm finding jq syntax confusing compared to just doing this with something like Python.
{
"SecurityGroups": [{
"OwnerId": "111111111",
"Description": "default VPC security group",
"GroupId": "sg-1a1a1a1a1",
"VpcId": "vpc-1a1a1a1a1",
"IpPermissionsEgress": [{
"IpProtocol": "-1",
"PrefixListIds": [],
"Ipv6Ranges": [],
"UserIdGroupPairs": [],
"IpRanges": [{
"CidrIp": "0.0.0.0/0"
}]
}],
"GroupName": "default",
"IpPermissions": [{
"IpProtocol": "-1",
"PrefixListIds": [],
"Ipv6Ranges": [],
"UserIdGroupPairs": [{
"GroupId": "sg-5df45d5d5",
"UserId": "234234234"
}],
"IpRanges": []
}]
},
{
"OwnerId": "22222222222",
"Description": "EC2 Security Group",
"Tags": [{
"Key": "aws:cloudformation:logical-id",
"Value": "EC2SecurityGroup"
},
{
"Key": "aws:cloudformation:stack-id",
"Value": "arn:aws:cloudformation:us-west-2:111111111:stack/blah-prod-vpc/asdfsdf-j3j3-22j1-39fj-sadfsadf"
},
{
"Key": "Name",
"Value": "blah-production-EC2"
},
{
"Key": "Owner",
"Value": "blah#blah.com"
},
{
"Key": "aws:cloudformation:stack-name",
"Value": "prod-vpc"
},
{
"Key": "Created",
"Value": "2018-05-21T09:40:55-07:00"
}
],
"GroupId": "sg-3jf32kj3j",
"VpcId": "vpc-3kj3f2kj3",
"IpPermissionsEgress": [{
"IpProtocol": "-1",
"PrefixListIds": [],
"Ipv6Ranges": [],
"UserIdGroupPairs": [],
"IpRanges": [{
"CidrIp": "0.0.0.0/0"
}]
}],
"GroupName": "blah-prod-vpc-EC2SecurityGroup-SJHS78F78SSH",
"IpPermissions": [{
"IpProtocol": "tcp",
"ToPort": 80,
"UserIdGroupPairs": [],
"IpRanges": [{
"CidrIp": "0.0.0.0/0"
}],
"FromPort": 80,
"PrefixListIds": [],
"Ipv6Ranges": []
},
{
"IpProtocol": "icmp",
"ToPort": 0,
"UserIdGroupPairs": [],
"IpRanges": [{
"CidrIp": "11.11.11.11/32"
}],
"FromPort": 0,
"PrefixListIds": [],
"Ipv6Ranges": []
},
{
"IpProtocol": "-1",
"PrefixListIds": [],
"Ipv6Ranges": [],
"UserIdGroupPairs": [],
"IpRanges": [{
"CidrIp": "22.22.22.22/16"
},
{
"CidrIp": "33.33.33.33/32"
}
]
},
{
"IpProtocol": "tcp",
"ToPort": 22,
"UserIdGroupPairs": [],
"IpRanges": [{
"CidrIp": "0.0.0.0/0"
}],
"FromPort": 22,
"PrefixListIds": [],
"Ipv6Ranges": [{
"CidrIpv6": "::/0"
}]
},
{
"IpProtocol": "tcp",
"ToPort": 443,
"UserIdGroupPairs": [],
"IpRanges": [{
"CidrIp": "0.0.0.0/0"
}],
"FromPort": 443,
"PrefixListIds": [],
"Ipv6Ranges": []
},
{
"IpProtocol": "icmp",
"ToPort": -1,
"UserIdGroupPairs": [],
"IpRanges": [{
"CidrIp": "44.44.44.44/32"
},
{
"CidrIp": "55.55.55.55/29"
}
],
"FromPort": -1,
"PrefixListIds": [],
"Ipv6Ranges": []
}
]
}
]}
The following filter meets the stated requirements:
.SecurityGroups[]
| select( any(.IpPermissions[].IpRanges[];
.CidrIp | startswith("11.11.11.11/") ) )
| .GroupId
In particular, with your input it yields the required value:
"sg-3jf32kj3j"
You might want to consider more sophisticated pattern-matching, perhaps using:
test("^11.11.11.11($|/)")
If you want the quotation marks to be dropped, consider using the -r command-line option of jq.
Using ..
Here's another solution, but please note that it has completely different semantics:
..
| select( .. | test("^11.11.11.11($|/)")? )
| .GroupId? // empty
may I offer you an alternative solution to your ask? this is how it looks like with the unix utility jtc I have developed recently:
bash $ cat file.json | jtc -w'<^11.11.11.11\/>R: [-5] [GroupId]'
"sg-3jf32kj3j"
bash $
it will also display all groups where this ip address shows up. If you like it, you can find the utility on github.com or google it up with jtc and json keywords

"forcePullImage" param gets set to 'false'

I am trying to build a docker image with Marathon, however when I use this configuration, the "forcePullImage" param gets set to 'false'
{
"id": "name",
"mem": 1024,
"cpus": 0.5,
"instances": 1,
"container": {
"type": "DOCKER",
"volumes": [
{
"containerPath": "/etc/localtime",
"hostPath": "/etc/localtime",
"mode": "RO"
}
],
"docker": {
"image": "dockerimage",
"network": "BRIDGE",
"portMappings": [ {
"containerPort": 8080,
"hostPort": 0,
"servicePort": [PORTNUMBER],
"protocol": "tcp",
"name": "name"
}],
"parameters": [{ "key": "name", "value": "name" }]
},
"forcePullImage": true
},
"healthChecks": [
{
"path": "~/check/",
"portIndex": 0,
"protocol": "HTTP",
"gracePeriodSeconds": 10,
"intervalSeconds": 2,
"timeoutSeconds": 10,
"maxConsecutiveFailures": 10
}],
"labels":{
"HAPROXY_GROUP":"external"
}
}
When it is finally build in the marathon enviroment the config file gets set to this:
{
"id": "name",
"mem": 1024,
"cpus": 0.5,
"instances": 1,
"container": {
"type": "DOCKER",
"volumes": [
{
"containerPath": "/etc/localtime",
"hostPath": "/etc/localtime",
"mode": "RO"
}
],
"docker": {
"image": "dockerimage",
"network": "BRIDGE",
"portMappings": [ {
"containerPort": 8080,
"hostPort": 0,
"servicePort": [PORTNUMBER],
"protocol": "tcp",
"name": "name"
}],
"parameters": [{ "key": "name", "value": "name" }]
},
"forcePullImage": false
},
"healthChecks": [
{
"path": "~/check/",
"portIndex": 0,
"protocol": "HTTP",
"gracePeriodSeconds": 10,
"intervalSeconds": 2,
"timeoutSeconds": 10,
"maxConsecutiveFailures": 10
}],
"labels":{
"HAPROXY_GROUP":"external"
}
}
After it is build I have to manually change the 'false' param to 'true' and after that it actually works, but why is it getting set to false when adding it to marathon and how can I fix this problem?
The Marathon app spec you posted is in fact invalid. If you look at the schema you see that forcePullImage has to be a child of the docker field (not the container field as in your example). The correct usage would be:
"docker": {
"image": "dockerimage",
"forcePullImage": false,
...
}

How do I traverse through the JSON using a JSON path parser?

I have the following JSON response
'{ "person_list":
[
{
"id": "4",
"demographics": {
"address": {
"city": "Tokyo",
"country": "Japan",
"county_or_parish": "some county",
"postal_code": "98765",
"state_or_province": "some state",
"street_addresses": [
"123 Some Street"
]
},
"date_of_birth": "1964-01-30T00:00:00.000Z",
"date_of_death": "2013-01-30T00:00:00.000Z",
"deceased": true,
"emails": [],
"gender": "Male",
"full_name": "Yagami Light M",
"given_names": [
"Light",
"Maes"
],
"family_names": [
"Yagami",
"Hughes"
],
"prefix": "Dr.",
"suffix": "Jr.",
"telecoms": [],
"mrn_aliases": [],
"payer_info": [],
"person_benefit_coverages": []
},
"mara_risk_scores": [
{
"score": 10.0,
"model": "CXCONLAG0"
},
{
"score": 20.0,
"model": "CXCONLAG1"
},
{
"score": 30.0,
"model": "CXCONLAG2"
}
],
"dashboard": {},
"has_flagged_manual_data": true,
"record_ids": []
},
{
"id": "3",
"demographics": {
"emails": [],
"given_names": [],
"family_names": [],
"telecoms": [],
"mrn_aliases": [],
"payer_info": [],
"person_benefit_coverages": []
},
"mara_risk_scores": [],
"dashboard": {},
"has_flagged_manual_data": false,
"record_ids": []
}
]
}'
Now I want to get the demographics of the id's that are equal to the empi_id that I retrieved from another object..
I tried this..
JsonPath.read(jsonObject,"$.person_list[].demographics", filter(where("id").eq(cs.getEmpiId())))
It doesnt seem to work.. Could you please help me with that?
This JSONPath expression should help you get what you need:
$.person_list[?(#.id=='4')].demographics
Replace 4 with the id of the person you're interested in and it should do the trick.