Listing Fastly users that can access a particular service

I was recently to supply a list of non-superuser user accounts that could access a given Service ID in Fastly, and what their role and permission was. This can be achieved with the Fastly CLI and command line utilities like jq.

Listing Fastly users that can access a particular service

I was recently to supply a list of non-superuser user accounts that could access a given Service ID in Fastly, and what their role and permission was.

The Fastly UI is not terribly useful for doing this, if your account is sufficiently large. At present, you would need to enumerate every single user, visit their access controls page, and then review which services they can access, and the permission level granted.

When you have a non-trivial amount of users and services to review, it can take ages to review

Users can be granted a range of roles, and within the engineer role can be granted permissions as a service ID level.

Engineer role allows you to delegate fine grained access to specific services.

Rather than do this by hand (on top of taking loads of time, it would be prone to human error), I wanted to work out how to achieve this through programmatic means.

Step 1 - Setup Fastly CLI

This tool is invaluable for dealing with Fastly on the command line. Installation instructions can be found on Fastly's website.

I then configure the CLI tool with environment variables in my ~/.zshrc:

FASTLY_API_TOKEN=notarealtokenyo
export FASTLY_API_TOKEN
FASTLY_SERVICE_ID=banana
export FASTLY_SERVICE_ID
FASTLY_CUSTOMER_ID=notarealcustomerid
export FASTLY_CUSTOMER_ID
Environment variables to configure the Fastly CLI.

Step 2 - List service auth

This will list all user accounts that have access to a particular service ID $FASTLY_SERVICE_ID :

N.B. This does not include users with access to all services (such as superuser).

fastly service-auth list --json | jq --arg SERVICE_ID "$FASTLY_SERVICE_ID" -r '.Items[] | select( .Service.ID | contains($SERVICE_ID)) | {id: .User.ID, permission: .Permission, created: .CreatedAt}' | jq -s '.' > auth.json
List of Fastly service auth for a given service ID

The output will be similar to:

[
  {
    "id": "16Sw3VMRzrXXXXLQfL6aen",
    "permission": "purge_all",
    "created": "2022-09-20T13:37:43Z"
  },
  {
    "id": "ZFVW99lVnYXXXXbohCv82",
    "permission": "purge_all",
    "created": "2022-05-10T23:29:30Z"
  }
]
List of Fastly service auth for a given service ID in JSON format

Step 3 - List users

Having access to only the user ID is not that great, we still need to match this to a users email address

fastly user list --customer-id $FASTLY_CUSTOMER_ID --json | jq -r '.[] | {id: .ID, email: .Login, role: .Role, locked: .Locked, tfa: .TwoFactorAuthEnabled}' | jq -s '.' > user.json
List users in a given Fastly account

The output will be similar to:

[
  {
    "id": "16Sw3VMRzrXXXXLQfL6aen",
    "email": "somebody@example.com",
    "role": "engineer",
    "locked": false,
    "tfa": true
  },
  {
    "id": "ZFVW99lVnYXXXXbohCv82",
    "email": "anotherperson@example.com",
    "role": "engineer",
    "locked": false,
    "tfa": true
  }
]
List users in a given Fastly account in JSON format

Step 4 - Joining the 2 JSON files together

We now have 2 JSON files, that we need to merge, based on a shared key id and it's value. We can use native JOIN and INDEX in jq to do this (see the stackoverflow post):

jq 'JOIN(INDEX(inputs[];.id);.[];.id;add)' auth.json user.json | jq -s '.' 
Merging 2 JSON files together with a shared key

The output is now pretty much perfect

$ jq 'JOIN(INDEX(inputs[];.id);.[];.id;add)' auth.json user.json | jq -s '.'
[
  {
    "id": "16Sw3VMRzrXXXXLQfL6aen",
    "permission": "purge_all",
    "created": "2022-09-20T13:37:43Z",
    "email": "somebody@example.com",
    "role": "engineer",
    "locked": false,
    "tfa": true
  },
  {
    "id": "ZFVW99lVnYXXXXbohCv82",
    "permission": "purge_all",
    "created": "2022-05-10T23:29:30Z",
    "email": "anotherperson@example.com",
    "role": "engineer",
    "locked": false,
    "tfa": true
  }
]
Merged 2 JSON files together

You can make a CSV (with header row) with some additional effort (see the stackoverflow post):

$ jq 'JOIN(INDEX(inputs[];.id);.[];.id;add)' auth.json user.json | jq -s '.' | jq -r '(map(keys) | add | unique) as $cols | map(. as $row | $cols | map($row[.])) as $rows | $cols, $rows[] | @csv'
"created","email","id","locked","permission","role","tfa"
"2022-09-20T13:37:43Z","somebody@example.com","16Sw3VMRzrXXXXLQfL6aen",false,"purge_all","engineer",true
"2022-05-10T23:29:30Z","anotherperson@example.com","ZFVW99lVnYXXXXbohCv82",false,"purge_all","engineer",true

You could even turn this into a giant single one liner if you want, but I am happy with this being a multi-step process.