Secure an S3 Bucket with IAM Policies

Expose, lock down, and audit an S3 bucket using IAM and CloudTrail.

Introduction

30 Second Summary

Data breaches from misconfigured cloud storage make headlines every year. A single checkbox left unchecked can expose sensitive files to the entire internet.

In this project, you will intentionally misconfigure an S3 bucket to understand the risk firsthand. Then you will lock it down using IAM policies, detect the exposure with IAM Access Analyzer, and audit every action with CloudTrail.

What You'll Build

You will simulate a real-world cloud security scenario by exposing, securing, detecting, and auditing an Amazon S3 bucket using core AWS security services.

By the end of this project, you'll have:

  • A public S3 bucket that you expose to the internet, then harden with Block Public Access and a least-privilege IAM policy.
  • Two IAM users and groups with different permission levels, including a custom JSON policy scoped to a single S3 bucket.
  • An IAM Access Analyzer that automatically detects your misconfigured bucket, plus a CloudTrail audit trail proving who did what and when in your account.
  • Secret Mission: Set up a CloudWatch alarm that alerts you when someone disables Block Public Access on any S3 bucket.

Are there any prerequisites?

You need an AWS account with root or admin-level access. All work in this project is done in the AWS Management Console and AWS CloudShell, so there is nothing to install on your computer.

Set Up Your Working Environment

Every cloud security project starts with a secure baseline. You need to know what "safe" looks like before you can recognize what's dangerous.

In this step, you'll sign into the AWS Management Console, open a browser-based terminal called AWS CloudShell, and confirm your account's default S3 security settings are in place.

In this step, get ready to:
  • Sign in to the AWS Management Console and select the US East (N. Virginia) region.
  • Open AWS CloudShell and verify your credentials.
  • Confirm your account-level S3 Block Public Access settings are enabled.
Sign in to the AWS Management Console
  • Open your browser and navigate to the AWS Management Console.
  • Sign in with your AWS account credentials.
  • In the top-right corner of the console, click the region selector dropdown.
  • Select US East (N. Virginia) us-east-1.

Why US East (N. Virginia)?

This region has the broadest AWS service support. All resources in this project will be created in us-east-1, so selecting the right region now prevents confusion later.

Open AWS CloudShell and verify your credentials

AWS CloudShell is a free, browser-based terminal built into the AWS console. It comes with the AWS CLI pre-installed and uses your console credentials automatically, so there's nothing to configure.

  • Click the CloudShell icon in the bottom-left of the console navigation bar.
  • Wait for the shell environment to initialize. This takes a few seconds.
  • Verify your credentials are working by running this command:
aws sts get-caller-identity

You should see a JSON response with three fields: UserId, Account, and Arn. This confirms your CloudShell session is properly authenticated with your AWS account.

Seeing an error or no output?

If CloudShell shows a blank screen or fails to load, try refreshing the page. CloudShell is available in us-east-1, so make sure you selected the correct region first.

If the command returns an access denied error, make sure you are signed in with an account that has root or admin-level access.

Still stuck? Help me troubleshoot why aws sts get-caller-identity is failing in CloudShell.

Confirm your account-level Block Public Access settings

S3 Block Public Access is a set of four independent security settings that prevent your buckets from being made public. These settings are enabled by default on every AWS account.

  • In the console search bar at the top, type S3 and select S3 from the results.
  • In the left navigation pane, click Block Public Access settings for this account.
  • Confirm all four Block Public Access settings show as On.

Do not change these settings

These account-level settings are your default secure baseline. Changing them would affect every S3 bucket in your account. In the next step, you will only disable Block Public Access at the individual bucket level to simulate a misconfiguration.

What are these four settings?

Each setting blocks a different way a bucket could become public. Together, they form a safety net that catches both new and existing public access configurations.

  • BlockPublicAcls prevents new public ACLs from being applied.
  • IgnorePublicAcls ignores any existing public ACLs.
  • BlockPublicPolicy prevents new public bucket policies from being saved.
  • RestrictPublicBuckets restricts access to buckets with public policies to only AWS services and authorized users.

Your environment is set up and your security baseline is confirmed. In the next step, you'll create an S3 bucket and intentionally expose it to the public to see exactly how data breaches happen.

Create and Intentionally Misconfigure an S3 Bucket

Your environment is set up and CloudShell is ready to go. Now it's time to see what a cloud security breach actually looks like.

In this step, you'll create an S3 bucket and intentionally expose it to the public internet. You'll disable its security protections, attach a public access policy, and confirm that anyone on the internet can read your files.

In this step, get ready to:
  • Create an S3 bucket with Block Public Access disabled.
  • Upload a file containing fake credentials to the bucket.
  • Attach a public bucket policy and confirm the file is accessible to anyone on the internet.
Create an S3 bucket with public access enabled

Every S3 bucket created after April 2023 has Block Public Access enabled by default. This is a safety net that prevents buckets from ever being made public. To simulate a real misconfiguration, you need to explicitly turn it off.

  • In the S3 console, click Create bucket.
  • In the Bucket name field, enter a unique name like cloudsec-demo-yourname-2026, replacing yourname with your actual name.
  • Confirm the AWS Region is set to US East (N. Virginia) us-east-1.
  • Scroll down to Block Public Access settings for this bucket.
  • Uncheck Block all public access.
  • Check the acknowledgement box that appears warning you about public access.
  • Leave all other settings as default and click Create bucket.

Why does the bucket name need to be unique?

S3 bucket names are globally unique across all AWS accounts worldwide. No two buckets in any account can share the same name. Adding your name and a number helps avoid collisions.

Bucket names must be 3 to 63 characters long and can only contain lowercase letters, numbers, and hyphens.

Bucket name already exists?

If you see a "Bucket with the same name already exists" error, someone else in the world already has that name. Try adding extra numbers or initials to make it unique.

Why can't I create my S3 bucket?

Your bucket is created but empty. Now you'll upload a file that simulates sensitive data, so you can see what happens when it's exposed publicly.

  • Open any text editor on your computer (Notepad on Windows, TextEdit on macOS).
  • Type the following fake credential text:
DB_PASSWORD=supersecret123
  • Save the file as sensitive-data.txt on your Desktop.
  • Back in the S3 console, click on your bucket name to open it.
  • Click Upload.
  • Click Add files and select the sensitive-data.txt file from your Desktop.
  • Click Upload.

You should see a green "Upload succeeded" banner at the top of the page. Click Close to return to your bucket's object list.

Don't see your file in the bucket?

Make sure you clicked Upload after adding the file. Just adding the file does not upload it. Also confirm you're looking inside the correct bucket.

Help me troubleshoot my S3 upload.

Attach a public bucket policy

Disabling Block Public Access opens the door, but it doesn't actually make anything public yet. For that, you need a bucket policy. A bucket policy is a JSON document that defines who can access the bucket and what actions they can perform.

  • In the S3 console, click on your bucket name to open it.
  • Click the Permissions tab.
  • Scroll down to the Bucket policy section and click Edit.
  • Delete any existing text in the policy editor, then paste the following policy:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::[[BUCKET_NAME="cloudsec-demo-yourname-2026"]]/*"
        }
    ]
}

What does this policy do?

This IAM policy document grants public read access to every object in your bucket. Here's what each field means:

  • "Effect": "Allow" permits the action instead of denying it.
  • "Principal": "*" means everyone. Any person or system on the internet.
  • "Action": "s3:GetObject" allows downloading or viewing objects.
  • "Resource" targets all objects (/*) inside your specific bucket.
  • Make sure you replaced cloudsec-demo-yourname-2026 in the Resource line with your actual bucket name.
  • Click Save changes.

You should see a Publicly accessible warning badge appear on your bucket. This confirms the policy is active.

Error saving the bucket policy?

If you see an "Access Denied" or "Action does not apply" error, check two things. First, confirm the bucket name in your policy's Resource field matches your actual bucket name exactly. Second, make sure you unchecked Block all public access when you created the bucket. If Block Public Access is still on at the bucket level, S3 will reject any public policy.

Help me fix my S3 bucket policy error.

Test the misconfiguration

The policy is active, but how do you know it actually works? You'll test it by opening the file's public URL in a browser, just like an attacker would.

  • In your bucket, click the Objects tab.
  • Click on sensitive-data.txt to open its detail page.
  • Find the Object URL field and click the copy icon next to it.
  • Open a new browser tab. For the best test, use an incognito or private window so you're not signed into AWS.
  • Paste the Object URL into the address bar and press Enter.

You should see DB_PASSWORD=supersecret123 displayed in plain text in your browser. Anyone with this URL can read this file right now.

Why does this matter?

This is the exact misconfiguration behind many real-world data breaches. Publicly accessible S3 buckets have exposed millions of customer records, credentials, and private files.

The combination of disabling Block Public Access and attaching a wildcard Principal policy is all it takes. In the next steps, you'll detect this risk automatically and lock it down.

Seeing Access Denied instead of the file contents?

Double-check that your bucket policy saved successfully by going back to the Permissions tab. You should see your JSON policy under Bucket policy. If it's empty, the policy didn't save. Re-paste it and make sure the bucket name in the Resource field matches your actual bucket name exactly.

Why am I getting Access Denied on my public S3 object?

You've just proven that your bucket is wide open. In the next step, you'll use IAM Access Analyzer to detect this misconfiguration automatically, just like a security tool would in a real AWS environment.

Detect the Misconfiguration with IAM Access Analyzer

Your S3 bucket is currently exposed to the entire internet. In the last step, you proved it by opening the Object URL in a browser and seeing your fake credentials displayed publicly.

In a real AWS environment, teams manage hundreds of buckets. Manually checking each one for misconfigurations is not realistic. In this step, you will set up an automated tool that detects the problem for you.

What is IAM Access Analyzer?

IAM Access Analyzer continuously scans your AWS resources and identifies any that are shared with external principals (like the public internet or other AWS accounts).

It generates findings that tell you exactly which resource is exposed, which policy grants the access, and who can reach it.

In this step, get ready to:
  • Create an IAM Access Analyzer for external access detection.
  • Review the active finding that flags your public S3 bucket.
  • Confirm the finding in the S3 console dashboard.
Create an external access analyzer

IAM Access Analyzer lives inside the IAM console. You need to create an analyzer before it starts scanning your account for misconfigurations.

  • Open the IAM console in a new tab.
  • In the left navigation pane, under Access analyzer, choose Analyzer settings.
  • Choose Create analyzer.
  • In the Analysis section, choose Resource analysis - External access.
  • Confirm the region is set to US East (N. Virginia).
  • For the analyzer name, enter my-external-analyzer.
  • Under zone of trust, choose Current account.
  • Choose Create analyzer.

Why external access instead of other analyzer types?

IAM Access Analyzer offers several analysis types. External access analysis detects resources shared with principals outside your account, which is exactly the kind of misconfiguration you created.

External access analysis is also completely free. Other analysis types (internal access and unused access) are paid features and are not needed for this project.

What is a zone of trust?

The zone of trust defines what counts as "inside" your boundary. By choosing Current account, you are telling the analyzer that only principals within your own AWS account are trusted. Any access from outside your account (including the public internet) triggers a finding.

Review your Access Analyzer findings

After you create the analyzer, it begins scanning your account. Findings can take up to 30 minutes to appear, but they often show up within 5 minutes.

  • In the left navigation pane under Access analyzer, choose Findings.
  • Look for a finding related to your S3 bucket with a status of Active.
  • Click on the finding to view its details.

The finding details show which policy grants the access and which external principal can reach your bucket. In this case, the principal is everyone on the internet.

What does an Active finding mean?

An Active finding means IAM Access Analyzer detected a resource that is currently accessible by an external entity. Your finding shows that the bucket policy you added in the previous step grants s3:GetObject to * (all principals).

This is the automated proof that your bucket is misconfigured. You did not have to check the bucket manually.

Don't see any findings yet?

Findings can take up to 30 minutes to appear after you create the analyzer. Wait a few minutes and refresh the page.

If you still see nothing after 10 minutes, confirm your analyzer's region matches your bucket's region. Both should be US East (N. Virginia). Also confirm that the bucket's Block Public Access is still disabled and the public bucket policy is still attached.

Help me troubleshoot why my IAM Access Analyzer isn't showing findings for my public S3 bucket.

View findings in the S3 console

IAM Access Analyzer also surfaces its findings directly inside the S3 console. This gives you a second place to spot misconfigured buckets without leaving the service you are managing.

  • Open the S3 console.
  • In the left navigation pane, under Dashboards, choose Access analyzer for S3.
  • Look for your bucket listed under Buckets with public access.

You should see your cloudsec-demo-yourname-2026 bucket listed here. This is the same finding from the IAM console, surfaced directly where you manage your storage.

Bucket not showing up in the dashboard?

The S3 Access Analyzer dashboard depends on the IAM Access Analyzer you created. If you do not see your bucket listed, go back to the IAM console and confirm the analyzer has an Active finding first. The S3 dashboard can take a few extra minutes to sync.

My S3 Access Analyzer dashboard is not showing my bucket under Buckets with public access even though I have an Active finding in IAM Access Analyzer.

You now have automated proof that your bucket is exposed. In the next step, you will lock it down by re-enabling Block Public Access, removing the public policy, and creating IAM users with least-privilege permissions.

Lock Down the Bucket and Enforce Least Privilege

In the previous step, IAM Access Analyzer confirmed what you already suspected. Your S3 bucket is wide open to the public internet, and anyone with the Object URL can read your files.

Now it's time to fix it. You'll re-enable Block Public Access, remove the dangerous bucket policy, and then build a proper access control system using IAM users, groups, and a custom policy. This is the least privilege principle in action: each person gets only the access they need, and nothing more.

In this step, get ready to:
  • Re-enable Block Public Access and delete the public bucket policy.
  • Create IAM user groups with different access levels.
  • Write a custom IAM policy that gives an analyst read-only access to one S3 bucket.
Re-enable Block Public Access and remove the public policy

Right now your bucket has Block Public Access disabled and a policy granting public read access. You'll fix both of these problems and verify the bucket is sealed.

  • Open the S3 console and select your bucket (cloudsec-demo-yourname-2026).
  • Click the Permissions tab.
  • Under Block public access (bucket settings), click Edit.
  • Check Block all public access.
  • Click Save changes.
  • Type confirm in the confirmation field and click Confirm.

Now verify that the bucket is actually locked down.

  • Click the Objects tab.
  • Click on sensitive-data.txt and copy its Object URL.
  • Open a new browser tab (or an incognito window) and paste the URL.

You should see an Access Denied error. The file that was publicly readable moments ago is now blocked.

Still seeing the file contents?

Make sure you checked Block all public access (not just one of the four individual settings) and clicked Save changes. Try opening the URL in a fully incognito or private window to rule out browser caching.

Why is my S3 Object URL still showing the file contents after enabling Block Public Access?

Block Public Access stops new public access, but the old bucket policy is still attached. Time to clean that up too.

  • Go back to the Permissions tab on your bucket.
  • Scroll down to Bucket policy and click Edit.
  • Delete the entire policy text in the editor.
  • Click Save changes.

Your bucket is now fully private. Block Public Access is on and the public policy is gone.

Why do both steps?

Block Public Access is a guardrail that overrides any policy attempting to grant public access. Deleting the bucket policy removes the actual permission grant.

Doing both is a defense-in-depth approach. If someone accidentally disables Block Public Access later, the bucket still stays private because there is no public policy left to take effect.

Create IAM user groups and users

A locked-down bucket is only half the picture. You also need to control who can access it and what they can do.

You'll create two IAM groups with different access levels. Then you'll create a user in each group to simulate a real team structure.

  • Open the IAM console at https://console.aws.amazon.com/iam/.
  • In the left navigation pane, click User groups.
  • Click Create group.
  • Enter SecurityAdmins as the group name.
  • In the Attach permissions policies section, search for AdministratorAccess.
  • Check the box next to AdministratorAccess (the AWS managed policy).
  • Click Create user group.

Now create the second group for analysts.

  • Click Create group again.
  • Enter Analysts as the group name.
  • Do not attach any policy yet. You'll create a custom policy for this group in the next substep.
  • Click Create user group.

Why groups instead of individual permissions?

Attaching policies to groups instead of individual users makes permission management scalable. When a new analyst joins the team, you add them to the Analysts group and they automatically inherit the right permissions.

With both groups ready, create a user for each one. Start with the admin user.

  • In the left navigation pane, click Users.
  • Click Create user.
  • Enter admin-user as the user name.
  • Check Provide user access to the AWS Management Console.
  • Select I want to create an IAM user.
  • Choose Custom password and enter a password you can remember for testing.
  • Uncheck Users must create a new password at next sign-in.
  • Click Next.
  • Select Add user to group and check the box next to SecurityAdmins.
  • Click Next, then click Create user.

Now create the analyst user.

  • Click Create user again.
  • Enter analyst-user as the user name.
  • Check Provide user access to the AWS Management Console.
  • Select I want to create an IAM user.
  • Choose Custom password and enter a password you can remember for testing.
  • Uncheck Users must create a new password at next sign-in.
  • Click Next.
  • Select Add user to group and check the box next to Analysts.
  • Click Next, then click Create user.

You should now see both admin-user and analyst-user listed on the Users page.

Don't see both users?

Check that you clicked Create user (not just Next) on the final step of the user creation wizard. If a user is missing, click Create user to start again.

Help me troubleshoot my IAM user creation

Write a custom IAM policy for the analyst

The admin user already has full access through the AdministratorAccess managed policy. The analyst needs something much more restricted.

You'll write a custom JSON policy that gives the analyst read-only access to your specific S3 bucket and nothing else.

  • In the left navigation pane, click Policies.
  • Click Create policy.
  • Switch to the JSON editor by clicking the JSON tab.
  • Delete any existing text in the editor and paste the following policy:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": "arn:aws:s3:::[[BUCKET_NAME=\"cloudsec-demo-yourname-2026\"]]/*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": "arn:aws:s3:::[[BUCKET_NAME=\"cloudsec-demo-yourname-2026\"]]"
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListAllMyBuckets"
            ],
            "Resource": "*"
        }
    ]
}

What does this policy do?

This policy has three statements, each granting a specific permission.

  • s3:GetObject allows the analyst to download and read individual files inside your bucket. The /* at the end of the ARN means it applies to all objects in the bucket.
  • s3:ListBucket allows the analyst to see the list of files in your bucket. This is scoped to the bucket ARN itself (no /*).
  • s3:ListAllMyBuckets allows the analyst to see the list of buckets in the account. This uses "Resource": "*" because it applies at the account level, not to a specific bucket.

Notice what is missing. There is no s3:PutObject or s3:DeleteObject. The analyst can look but not touch.

  • Click Next.
  • Enter AnalystS3ReadOnly as the policy name.
  • Click Create policy.

You should see a success banner confirming the policy was created.

Seeing a JSON syntax error?

Make sure you replaced the bucket name placeholder with your actual bucket name in both the first and second statements. Check for missing commas between the three statement objects, and make sure every opening brace has a matching closing brace.

Help me fix my IAM policy JSON

Now attach this policy to the Analysts group.

  • In the left navigation pane, click User groups.
  • Click on the Analysts group.
  • Click the Permissions tab.
  • Click Add permissions, then select Attach policies.
  • Search for AnalystS3ReadOnly.
  • Check the box next to it and click Attach policies.

You should see AnalystS3ReadOnly listed under the Analysts group's permissions.

Your bucket is locked down and your IAM users are in place with tightly scoped permissions. Next up, you'll put these permissions to the test using CloudShell and a sign-in as the analyst to verify that least privilege is actually working.

Test and Verify Permissions with CloudShell

In the previous step, you locked down your S3 bucket and built out your IAM users, groups, and policies. Your bucket is now private, and each user has a specific set of permissions.

How do you know it all actually works? In this step, you'll use CloudShell to verify your IAM configuration from the command line. Then you'll sign in as the analyst user to prove that least privilege is enforced.

In this step, get ready to:
  • Verify IAM users and groups using AWS CLI commands in CloudShell.
  • Confirm the correct policies are attached to each group.
  • Test analyst permissions by signing in as analyst-user in a private browser.
Verify IAM users and groups in CloudShell

Security auditors don't just trust the console. They run CLI commands to independently verify that the right users, groups, and policies are in place.

  • Click the CloudShell icon in the bottom-left of the console navigation bar to open your shell.
  • Wait for the shell to finish initializing if it shows a loading message.
  • List all IAM users in your account by running this command:
aws iam list-users --query "Users[].UserName"

You should see admin-user and analyst-user in the output.

  • List all IAM groups by running this command:
aws iam list-groups --query "Groups[].GroupName"

You should see SecurityAdmins and Analysts in the output.

What does --query do?

The --query flag filters the JSON output from the AWS CLI so you only see the fields you care about. Without it, each command returns a wall of metadata like creation dates, paths, and IDs.

Don't see both users or groups?

Go back to the IAM console and confirm that both users (admin-user and analyst-user) and both groups (SecurityAdmins and Analysts) exist. If any are missing, create them following the instructions from the previous step.

Help me troubleshoot my IAM list-users and list-groups output in CloudShell

Verify the policies attached to each group

Confirming users and groups exist is only half the picture. You also need to verify that the correct policies are attached to each group.

  • Check the policies attached to the SecurityAdmins group by running this command:
aws iam list-attached-group-policies --group-name SecurityAdmins

You should see AdministratorAccess listed as an attached policy.

  • Check the policies attached to the Analysts group by running this command:
aws iam list-attached-group-policies --group-name Analysts

You should see AnalystS3ReadOnly listed as an attached policy.

Why verify with the CLI?

Clicking through the console is fine for setup, but CLI verification is how real security auditors work. Commands produce consistent, scriptable output that can be logged and compared over time.

If you ever need to prove to an auditor that the right policies are in place, a saved terminal session is far more convincing than a screenshot.

Seeing an empty result or wrong policy?

Make sure you spelled the group name exactly right, including capitalization. Group names are case-sensitive, so securityadmins will not match SecurityAdmins.

If the output is empty, go back to the IAM console, open the group, and confirm a policy is attached under the Permissions tab.

Help me troubleshoot my IAM group policy verification in CloudShell

Test analyst permissions in a private browser

CLI verification confirms the configuration is correct on paper. Now you need to test it from the analyst's perspective to prove the permissions actually work.

  • In your main browser window (your admin session), open the IAM console.
  • On the IAM Dashboard (the landing page), find the AWS Account section on the right side.
  • Copy the Sign-in URL for IAM users in this account. It looks like https://your-account-id.signin.aws.amazon.com/console.
  • Press Cmd+Shift+N (macOS) or Ctrl+Shift+N (Windows) to open a private or incognito browser window.
  • Paste the sign-in URL you just copied into the address bar and press Enter.
  • The Account ID field should already be filled in. Enter analyst-user as the IAM user name.
  • Enter the custom password you set for analyst-user in the previous step.
  • Click Sign in.

You should land on the AWS Management Console, now signed in as analyst-user. You can confirm this by checking the username in the top-right corner of the console.

  • In this incognito window, type S3 in the search bar at the top and select S3 from the results.
  • Confirm that you can see the list of buckets, including your cloudsec-demo-yourname-2026 bucket.
  • Click on your bucket name to open it.
  • Confirm that you can see sensitive-data.txt listed in the objects.

The analyst can read and list objects. Now test that the analyst cannot do anything beyond that.

  • Click Upload and try to upload any file from your computer.
  • You should see an error message. The upload fails because the AnalystS3ReadOnly policy does not grant write permissions.
  • Go back to the bucket's object list. Select the checkbox next to sensitive-data.txt and click Delete.
  • You should see another error. The delete action is also denied.

What just happened?

The analyst can list buckets, list objects, and download objects. Those are the only three actions your AnalystS3ReadOnly policy allows.

Everything else (upload, delete, change permissions) is denied by default. This is the least privilege principle in action. Users only get exactly the access they need, nothing more.

Analyst can upload or delete?

If the analyst can perform write actions, your policy may be granting more permissions than intended. Go back to the IAM console in your admin session and open the AnalystS3ReadOnly policy. Confirm it only allows s3:GetObject, s3:ListBucket, and s3:ListAllMyBuckets.

Also make sure analyst-user is only in the Analysts group. If the user was accidentally added to SecurityAdmins, it inherits full admin access.

Help me fix my analyst-user permissions so upload and delete are denied

Your IAM setup is verified and least-privilege access is confirmed. Next up, you'll use CloudTrail event history to audit every action you took during this project.

Audit Everything with CloudTrail Event History

Your IAM permissions are verified and working. The analyst can read objects in your secured S3 bucket, but nothing more.

But how would you prove all of these changes happened, and who made them? CloudTrail records every API call in your AWS account, giving you a complete audit trail of who did what, when, and from where.

In this step, get ready to:
  • Open CloudTrail Event History in the AWS Console.
  • Search for S3 policy change events from earlier in the project.
  • Trace IAM user and policy creation events to complete your audit trail.
Open CloudTrail Event History

CloudTrail Event History is always on in every AWS account. It records the last 90 days of management events at no charge, with zero setup required.

  • Switch back to your main browser window where you are signed in with your admin account.
  • Close the incognito/private window where you tested as analyst-user.
  • Open the CloudTrail console by navigating to https://console.aws.amazon.com/cloudtrail/.
  • In the left navigation pane, choose Event history.

You should see a list of recent API events in your account. Each row shows the event name, the user who performed it, and the timestamp.

What is CloudTrail Event History?

Every action in your AWS account, whether from the Console, CLI, or SDK, is an API call under the hood. CloudTrail captures all of these management-level calls automatically.

Event History gives you a searchable, downloadable log of the last 90 days. This is the first place security teams look when investigating an incident.

Search for S3 policy change events

Earlier in this project, you made two critical S3 changes. You attached a public bucket policy, then re-enabled Block Public Access to lock it down.

  • Click the filter dropdown at the top of the Event History page.
  • Select Event name as the filter attribute.
  • Type PutBucketPolicy in the search field and press Enter.
  • Find the event from when you added the public bucket policy in Step 2.
  • Click on the event to expand the full event record.

What does the event record show?

The event record includes the identity of who performed the action, the exact timestamp, the source IP address, and the full API parameters. For PutBucketPolicy, you can see the exact JSON policy that was attached to your bucket.

This is the evidence a security team would use to answer: who made our bucket public, and when?

  • Clear the search field.
  • Type PutPublicAccessBlock in the search field and press Enter.
  • Find the event from when you re-enabled Block Public Access in Step 4.
  • Click on the event to review the details, including the actor identity and timestamp.

You now have proof of both the misconfiguration and the fix, complete with timestamps and actor identities.

Don't see the event?

Make sure the filter dropdown is set to Event name, not another attribute like User name or Resource type. Event names are case-sensitive, so type them exactly as shown.

Also check that the time range at the top of the page covers the period when you performed the action. Event History defaults to showing the last 90 days, but a custom filter may narrow it.

Why can't I find my CloudTrail event?

Search for IAM user and policy creation events

CloudTrail also captured every IAM change you made. You can trace exactly when each user and policy was created.

  • Clear the search field.
  • Type CreateUser in the Event name filter and press Enter.

You should see events for when you created admin-user and analyst-user.

  • Click on one of the CreateUser events to review the details.
  • Clear the search field.
  • Type CreatePolicy in the Event name filter and press Enter.
  • Find the event for when you created the AnalystS3ReadOnly policy.
  • Click on the event to review the full record, including who created the policy and when.

Why does this matter for security?

This is exactly how security teams investigate incidents. If a suspicious IAM user appeared in your account, you could search CloudTrail to find who created it, from which IP address, and at what time.

Every action in your AWS account is recorded. CloudTrail proves who did what, when, and from where.

Secret mission

Set Up a CloudWatch Alarm That Alerts You When Someone Disables Block Public Access

What if someone on your team accidentally disables Block Public Access on a production S3 bucket? In this extension, you will build a real-time alert system that emails you the moment anyone changes Block Public Access settings on any bucket in your account. This is a monitoring pattern used by security teams at organizations of all sizes.

Before you go

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. The IAM users, groups, policies, Access Analyzer, and CloudTrail event history you created in this project are all free. If you completed the Secret Mission, the CloudTrail trail stores logs in S3 at a cost of a few cents per month for low activity.

Cost warning

The CloudTrail trail created in the Secret Mission delivers logs to an S3 bucket, which incurs storage charges. For low-activity accounts, this is only a few cents per month. If you want to avoid any charges, delete the trail and its S3 log bucket in the Delete tab below.

Resources you used:

  • S3 bucket (cloudsec-demo-yourname-2026) with sensitive-data.txt inside.
  • IAM users: admin-user and analyst-user.
  • IAM groups: SecurityAdmins and Analysts.
  • Custom IAM policy: AnalystS3ReadOnly.
  • IAM Access Analyzer: my-external-analyzer.
  • Secret Mission only: CloudTrail trail (security-trail), its S3 log bucket, CloudWatch log group (cloudtrail-logs), metric filter, CloudWatch alarm, and SNS topic.

Keep everything running

No action needed. Choose this if you are still actively exploring or plan to build on this project.

  • IAM users, groups, policies, and Access Analyzer are completely free.
  • CloudTrail event history is always on at no charge.
  • Your S3 bucket with a single text file has negligible storage costs.
  • If you completed the Secret Mission, the CloudTrail trail continues to store logs in S3 at a cost of a few cents per month for low-activity accounts.

Pause - I'll come back to this later

There is nothing to pause in this project. None of the resources you created run ongoing compute processes.

  • IAM resources, Access Analyzer, and CloudTrail event history do not incur compute costs.
  • If you want to reduce the Secret Mission trail costs to zero, delete the trail following the steps in the Delete tab. You can recreate it later.

Delete - I don't want to use this again

Remove all project resources and start fresh. Follow these steps to clean everything up.

  • Open the S3 console, select your cloudsec-demo-yourname-2026 bucket, click Empty to delete all objects, then click Delete to remove the bucket.
  • Open the IAM console, go to Users, and delete admin-user and analyst-user.
  • Go to User groups and delete SecurityAdmins and Analysts.
  • Go to Policies, search for AnalystS3ReadOnly, and delete it.
  • Go to Access analyzer in the left navigation, select Analyzer settings, select my-external-analyzer, and delete it.
  • Secret Mission only: Open the CloudTrail console, go to Trails, and delete security-trail. Then go to S3 and delete the trail's S3 log bucket.
  • Secret Mission only: Open the CloudWatch console, go to Alarms, and delete your alarm. Then go to Logs, select the cloudtrail-logs log group, and delete it along with the metric filter.
  • Secret Mission only: Open the SNS console, go to Topics, and delete the SNS topic you created for alarm notifications.

Mission accomplished

That's a Wrap!

Nice work! You just completed a real cloud security workflow from start to finish.

You've learned how to:

  • Secured an S3 bucket by intentionally exposing it, detecting the misconfiguration with IAM Access Analyzer, and locking it down with Block Public Access and bucket policies.
  • Created IAM users and groups with a custom JSON policy enforcing least-privilege access to a single S3 bucket.
  • Audited every security-relevant action in your account using CloudTrail event history to prove who did what and when.
  • Set up a CloudWatch alarm to get real-time alerts when Block Public Access settings change on any S3 bucket.

Ready to quiz yourself?