Skip to main content
In this lesson you’ll learn how to design S3 bucket (resource) policies to grant precise access to objects and actions inside a bucket. The demo covers:
  • Using multiple browser sessions to test access from different identities.
  • Creating a bucket and uploading objects/folders.
  • Inspecting an IAM user policy that only lists buckets.
  • Creating bucket policies to allow GetObject and DeleteObject for specific prefixes.
  • Allowing anonymous (public) read access for a folder after disabling Block Public Access.
  • Granting a user from another AWS account permissions to list and delete objects.
Because we need multiple identities to test effective permissions, the demo uses three browser tabs to emulate three users:
  • Blue tab: account one / user1 (bucket owner)
  • Green tab: account one / user2
  • Yellow tab: account two / admin
If you forget which tab represents which user, check the account/user label in the AWS Console header.

1 — Create the bucket and upload example objects

In the blue tab (account one / user1):
  1. Create a bucket named kk-resource-policies.
  2. Keep defaults: Block all public access enabled, versioning disabled, and do not use ACLs (legacy).
  3. Upload a set of files and folders, for example:
    • file1.txt, file2.txt, file3.txt, secondfile.txt
    • folders: logs/, media/, traces/
Example object metadata for file1.txt:

2 — Test initial access

  • As the bucket creator (blue tab / user1), open an object via the console’s “Open” action — it should succeed because you are the authenticated owner.
  • If you copy an object URL and open it in a private/incognito window (anonymous user), you should see Access Denied because the bucket remains locked down by default.

3 — Inspect user2’s IAM policy

Switch to the green tab (account one / user2) and inspect the user’s IAM policies. In this demo, user2 only has a policy that allows listing buckets and bucket contents:
A screenshot of the AWS Identity and Access Management (IAM) console showing the details for a user named "user2," including the ARN, creation date, and that console/access keys are not enabled. The Permissions tab is open and shows a customer inline policy called "list-bucket."
User2’s inline IAM policy (attached to the user):
With that policy, user2 can list buckets (s3:ListAllMyBuckets) and request ListBucket (which may require the bucket ARN to succeed), but cannot GetObject or DeleteObject without an explicit bucket policy or additional IAM permissions.

4 — Listing the bucket contents as user2

In the S3 console (green tab), user2 can see the kk-resource-policies bucket and list objects. However, attempting to open an object (perform GetObject) results in Access Denied — expected, since no GetObject permission exists yet.
A screenshot of the Amazon S3 console showing the "kk-resource-policies" bucket with a list of objects and folders (file1.txt, file2.txt, file3.txt, logs/, media/, secondfile.txt, traces/). The UI shows toolbar actions like Upload, Create folder, and object details (type, last modified, size).

5 — Add a bucket policy to grant user2 read access to logs/

Back in the blue tab (account one / user1), open the bucket’s Permissions → Bucket policy editor and add a statement that grants user2 s3:GetObject only within the logs/ prefix. Example minimal statement (Principal is the user ARN):
Reference: the S3 API Reference lists S3 actions and the resources they require. For example, GetObject is the API action used to retrieve an object:
A screenshot of the AWS documentation site showing the Amazon Simple Storage Service (S3) API Reference, with a left navigation menu and a long list of S3 operations (GetBucket*, GetObject, ListBuckets, etc.) in the main content area.

6 — Verify user2 can read logs/ but not other prefixes

  • Save the bucket policy, switch to the green tab (user2), navigate into the logs/ prefix and open a log object. The Open action should now succeed (download or display depends on the object’s content type).
  • Confirm user2 still cannot access objects in other prefixes; for example, opening file1.txt should still return Access Denied.

7 — Allow user2 to delete objects in traces/

To let user2 delete objects under a different prefix such as traces/, add another statement to the bucket policy:
After saving:
  • Deleting traces/trace1 as user2 should succeed.
  • Deleting file1.txt (outside traces/) should still fail with Access Denied.

8 — Action vs Resource: bucket-level vs object-level

Some S3 actions operate on buckets, others on objects. Supply the matching ARN(s) in the Resource element. If you include both bucket-level and object-level actions in a single statement, provide both ARN types in the Resource array. S3 evaluates and matches each action to the appropriate resource. Example single-statement that mixes actions (note both resource ARNs included):

9 — Allow public (anonymous) GetObject for media/

To make objects in media/ publicly readable, add a statement with "Principal": "*" and Action: "s3:GetObject" that targets media/*. Note: Block Public Access settings may prevent adding public policies — you must intentionally disable or adjust them.
Block Public Access is a safety mechanism to prevent accidental public exposure. Only disable or relax the specific checks you need, and confirm your objects are intended to be public before applying a policy that grants anonymous access.
To edit Block Public Access settings, use the bucket settings UI and uncheck the specific protections you intend to override:
A screenshot of the Amazon S3 console showing the "Edit Block public access (bucket settings)" page with checkboxes for various block-public-access options. The panel includes descriptions of each setting and a "Save changes" button at the bottom.
Example bucket policy statement to allow public read of media/*:
After disabling the required Block Public Access checks and saving the policy, objects under media/ become reachable via their public object URLs. Objects outside media/ remain restricted.

10 — Combined example bucket policy

A combined example of the statements shown above:

11 — Grant cross-account access (account two → this bucket)

To allow a user from another AWS account to perform actions against this bucket, add a statement with their user ARN as the Principal. In the demo the external user is arn:aws:iam::040497317401:user/admin. Example cross-account statement to allow listing the bucket and deleting objects in logs/:

12 — Test cross-account access with CloudShell (account two)

In the yellow tab (account two / admin), use AWS CloudShell or the AWS CLI configured for that admin user. Initially, listing the bucket will return Access Denied:
After saving the cross-account statement, the admin can list bucket contents:
Attempting to delete a file outside the allowed prefix (should fail):
Deleting an object in logs/ (should succeed):

13 — Summary and best practices

  • Use bucket (resource) policies to grant cross-account or anonymous access; use IAM policies for permissions tied to a specific IAM identity.
  • Be granular: grant only the actions and prefixes required (least privilege).
  • Remember action vs resource scope: supply bucket ARNs for bucket-level actions and object ARNs (with /*) for object-level actions.
  • Block Public Access protects against accidental exposure — only disable or relax the checks you intentionally need.
  • When testing permissions, use separate browser sessions or CLI profiles (as demonstrated) to verify the effective permissions from different identities.
When building bucket policies, refer to the S3 API Reference to confirm whether an action is bucket-level or object-level, and include the correct ARN(s) in each statement’s Resource element.

Watch Video