Expanding variables inside jq

It took me 3.5 hours to figure out the hard way that JQ supports variables in a simple but peculiar way.

I hope it really helps somebody else.

How to do it

Here’s what the data set looks like, btw:

$ gcloud auth list --format=json
[
  {
    "account": "inger.klekacz@workaccount.com",
    "status": ""
  },
  {
    "account": "ingernet@personalaccount.com",
    "status": "ACTIVE"
  }
]

Now, remember, bash doesn’t expand variables inside single quotes:

$ tmp_authed_user=$(gcloud auth list --format=json | jq -r '.[] | select(.status=="ACTIVE") | .account')

$ echo '$tmp_authed_user'
$tmp_authed_user            # boo hiss

$ echo "$tmp_authed_user"
ingernet@personalaccount.com 

Hurray but still not enough, yet, because we still have to somehow jam it between two single quotes like jq likes.

$ jqstring="'.bindings[] | select(.members[] | contains("'"'${tmp_authed_user}'"'")) | .role'"     # kill me now

Let’s see if it runs, I guess?

$ echo $jqstring
'.bindings[] | select(.members[] | contains("ingernet@personalaccount.com")) | .role'  

I mean, I’ll try it? Hold my beer

$ gcloud projects get-iam-policy my-fancy-project --format=json | jq $jqstring
jq: error: syntax error, unexpected INVALID_CHARACTER, expecting $end (Unix shell quoting issues?) at <top-level>, line 1:
'.bindings[]                 

wheeeeeeee

Well, dear reader, listen close: I was definitely overthinking things. Here’s how you do it:

# unset that ungodly jqstring, ain't nobody got time for that
$ unset jqstring

# set the user variable
$ tmp_authed_user=$(gcloud auth list --format=json | jq -r '.[] | select(.status=="ACTIVE") | .account')

# pass it into the jq command as an argument, making sure to wrap it in quotes first - 
# notice that i named the arg "authed_user" to show that it's actually getting used 
# inside the scope of jq instead of just being the bash variable

$ gcloud projects get-iam-policy my-fancy-project --format=json | jq --arg authed_user "${tmp_authed_user}" -r '.bindings[] | select(.members[] | contains($authed_user)) | .role'
roles/owner
roles/resourcemanager.projectMover
roles/secretmanager.admin

Easy peasy. Right? Uggh.

Why would you do that?

The most immediate application that comes to mind is if you’re looping through a list of users and want to get the IAM credentials for all of them (since that’s what I was trying to do when I went down this rabbit hole). But I imagine this will be helpful for running a jq query against any list of strings - for x in y, etc.

Caveats

The code above doesn’t grab ALL of the credentials - only the IAM creds assigned at the GCP project level. As of right now, the gcloud SDK doesn’t pull in creds inherited from folders, which is a drag. But this gist isn’t about that. Anyway, just a heads up.