Unit 1: How a cloud account is laid out · Lesson 3 of 5 · 20 min

Filter the API like a pro

Use --query, --output and jq to pull exactly the fields an audit needs.

mission

You are
Security engineer at Brightwell.
Situation
Raw JSON from the API is long. You need answers like who owns this bucket or which machines allow old metadata calls.
Goal
Turn long API responses into one line per resource.
Hand in
A one-line command that prints every bucket with its owner tag.

One response, three filters

Click a stage to see what flows out of it
{ "TagSet": [
  { "Key": "data", "Value": "learner-pii" },
  { "Key": "owner", "Value": "growth" } ] }

3 lines. aws s3api get-bucket-tagging --bucket brightwell-learner-exports, before any filter

Each stage keeps less. Step through it.
01

Count with length()

learner@cloud-lab
aws iam list-users --query 'length(Users)'
7

defender tip --query runs JMESPath on the response before printing, so it works the same for every service.

02

Pull one tag

Tags carry ownership and data class. An auditor reads them constantly.

learner@cloud-lab
aws s3api get-bucket-tagging --bucket brightwell-learner-exports
{
    "TagSet": [
        {
            "Key": "data",
            "Value": "learner-pii"
        },
        {
            "Key": "owner",
            "Value": "growth"
        }
    ]
}
aws s3api get-bucket-tagging --bucket brightwell-learner-exports --query 'TagSet[?Key==`owner`].Value' --output text
growth

you should see The bucket holds learner personal data and the growth team owns it.

03

Build a table

A hash with named keys makes a readable table. Here you check which machines still allow the old metadata service.

learner@cloud-lab
aws ec2 describe-instances --query 'Reservations[].Instances[].{name: Tags[?Key==`Name`]|[0].Value, tokens: MetadataOptions.HttpTokens}' --output table
------------------------------
|      DescribeInstances     |
+---------------+------------+
|     name      |  tokens    |
+---------------+------------+
|  web-1        |  required  |
|  web-2        |  required  |
|  jump-1       |  required  |
|  dev-box-1    |  optional  |
|  report-win-1 |  optional  |
|  tutor-api-1  |  required  |
|  db-1         |  required  |
+---------------+------------+

you should see Two machines say optional. You will fix those in the network unit.

04

Reach for jq when the shape gets awkward

jq can split strings and build text, which JMESPath cannot. Here it shortens role ARNs to role names.

learner@cloud-lab
aws lambda list-functions | jq -r '.Functions[] | "\(.FunctionName)\t\(.Role | split("/") | last)"'
invoice-generator	invoice-fn-role
tutor-assistant	tutor-assistant-role
thumbnailer	thumbnailer-role
05

Loop over every bucket

Many checks are per resource. A shell loop with a filtered call per item is the auditor's workhorse.

learner@cloud-lab
for b in $(aws s3api list-buckets --query 'Buckets[].Name' --output text); do echo "$b $(aws s3api get-bucket-tagging --bucket $b --query 'TagSet[?Key==`owner`].Value' --output text)"; done
brightwell-website web
brightwell-course-media content
brightwell-learner-exports growth
brightwell-backups platform
brightwell-tutor-knowledge ai
brightwell-deploy-artifacts platform
brightwell-logs security

you should see Seven lines, one per bucket, each with an owner you can contact about a finding.

What a defender takes away

  1. 1--query filters on the client with JMESPath. [?Key==owner] keeps matching items.
  2. 2--output text prints values ready for shell loops. --output table is for humans.
  3. 3Use jq when you need string work or the output feeds a script.
  4. 4Loop per resource, print one line each, and keep the output for your report.

Questions

0 of 3 solved

Answer each one from what you find on your lab machine.

  1. Show hint

    Loop over the buckets and print the owner tag.

  2. Show hint

    MetadataOptions.HttpTokens