← All posts
2 min read

Write a least-privilege S3 read policy

#aws#cli#iam#security#identity
📑 On this page

Part 104 of AWS from Zero. This lesson changes or inspects one IAM concept so the permission model stays understandable.

What we are learning

S3 bucket actions and object actions use different ARN shapes. Least privilege often needs separate statements.

Before you run it

BUCKET="replace-with-private-bucket"
PREFIX="shared/reports"

Use a sandbox bucket and preserve the distinction between the bucket ARN and object ARNs.

The command

cat > s3-read-policy.json <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::$BUCKET",
      "Condition": {"StringLike": {"s3:prefix": ["$PREFIX", "$PREFIX/*"]}}
    },
    {
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::$BUCKET/$PREFIX/*"
    }
  ]
}
EOF

The list condition narrows visible keys, while the object ARN narrows readable objects.

Inspect the result

aws accessanalyzer validate-policy \
  --policy-type IDENTITY_POLICY \
  --policy-document file://s3-read-policy.json

Read the returned ARN, path, IDs, and attachment state instead of checking only the command exit code.

One tiny variation

aws iam create-policy \
  --policy-name AwsZeroReportsRead \
  --policy-document file://s3-read-policy.json

Create the managed policy only after validation and review.

Common mistake

Using only arn:aws:s3:::bucket/prefix/* does not cover s3:ListBucket, because that action targets the bucket resource itself.

Cleanup

rm s3-read-policy.json

If you created the managed policy variation, detach and delete it after testing.

Next, we will learn Understand IAM Action wildcards.

Official AWS CLI reference