Monitor IAM and S3 Security
Use IAM roles and CloudTrail to investigate least-privilege S3 access.
Introduction
30 Second Summary
When a cloud application can reach private files, a successful upload tells only half the story. You also need evidence showing where it can write and which damaging actions stay blocked.
In this project, you will build a small security lab using AWS Identity and Access Management (IAM) to limit where an Amazon EC2 application can write in Amazon S3. Your Python probe produces allowed and denied requests for AWS CloudTrail to preserve as audit evidence.
What You'll Build
Your finished demo shows an approved S3 write succeeding, blocked operations returning AccessDenied, and matching CloudTrail records ready for investigation.
By the end of this project, you'll have:
- A controlled least-privilege test where approved/report.txt succeeds. The probe returns AccessDenied for outside/first.txt. It also proves that deletion stays blocked.
- An identity proof that shows the application using temporary credentials from IamS3ProbeRole. No long-lived AWS access keys are stored on the instance.
- An audit evidence pack that connects CloudTrail records to the same allowed and denied requests. Your SECURITY-REPORT.md supports a security walkthrough. A color-coded architecture diagram makes the design easy to explain.
- Secret Mission: a permissions boundary that caps the role even while an intentionally broad S3 policy is attached.
Are there any prerequisites?
You need administrator access to an AWS lab account and a Windows device with Windows PowerShell. The first step checks OpenSSH Client for Windows, so you can continue even when its current status is unknown.
Before We Start
Before any AWS resources exist, this checkpoint commits you to the authorization rule that the lab must prove. The rule allows IamS3ProbeRole to use s3:PutObject only for arn:aws:s3:::your-unique-bucket-name/approved/* while out-of-prefix writes plus s3:DeleteObject remain denied.
Verify OpenSSH and Prepare Evidence
Your future cloud workload needs a reliable remote connection. Your investigation also needs a place for evidence before any chargeable resource exists.
Windows PowerShell lets you inspect the installed client without touching AWS. OpenSSH gives your machine the command used to connect later.
In this step, get ready to:
- Verify the OpenSSH client capability.
- Confirm the ssh command prints usage.
- Create the iam-s3-capstone evidence workspace with audit-gap.txt inside it.
Check the OpenSSH client
Windows exposes OpenSSH as an optional capability. Checking its state first keeps the installation path precise.
- Press the Windows key to open Windows search.
- Type PowerShell into the search field.
- Select Run as administrator beside the Windows PowerShell result.
- Approve the Windows permission prompt.
- Inspect the installed OpenSSH capabilities by running this command:
Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'
What Does This Check Show?
- Get-WindowsCapability checks the capability inventory for your Windows installation.
- Where-Object filters the results to capability names that match OpenSSH.
- OpenSSH.Client~~~~0.0.1.0 identifies the client capability used later to connect to the Linux instance.
✔️ The client shows Installed
Good. The Installed state confirms your Windows device already has the OpenSSH client.
ⓧ The client shows NotPresent
The NotPresent state means Windows needs the client capability before it can make an SSH connection.
- Install only the OpenSSH client by running this command:
Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
What Does This Command Do?
Add-WindowsCapability adds the named optional Windows capability. The command targets the OpenSSH client used for outbound connections.
- Repeat the capability check by running:
Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'
Why Run the Check Again?
The second check reads the installed state after Windows applies the change. It gives you direct evidence that the client is ready.
You should now see Installed beside OpenSSH.Client~~~~0.0.1.0.
Capability Check Failed?
- Confirm you selected the elevated PowerShell option before running the capability command.
- Retry the installation from a network connection that permits Windows optional capability downloads.
- Get targeted help by using Help me troubleshoot my OpenSSH Client capability check or installation in elevated Windows PowerShell..
- Share the relevant PowerShell output with the NextWork community if device policies still block the installation.
Confirm the SSH command responds
SSH usage output proves that PowerShell can find the installed client executable. This check catches a broken command path before an EC2 instance exists.
Before you run it, what do you expect a working SSH client to print with no destination? Hold that prediction.
- Test the OpenSSH client by running:
ssh
What Does This Command Prove?
Running ssh without a destination asks the client to show its command usage. A usage response confirms that PowerShell found the executable.
You should see SSH usage in PowerShell. That output confirms your local connection tool is ready.
No SSH Usage Output?
- Repeat the capability check to confirm the client state still reads Installed.
- Restart PowerShell after a fresh OpenSSH installation.
- Use Help me understand why PowerShell cannot invoke ssh after OpenSSH Client shows Installed. for help with the command path.
Create the evidence workspace
A dedicated evidence workspace keeps local observations separate from the cloud resources you create later. The first file records the object-level request used to expose the audit visibility gap.
- Create the iam-s3-capstone folder on your Desktop and switch into it by running these commands:
New-Item -Path "$HOME\Desktop\iam-s3-capstone" -ItemType Directory -Force
Set-Location -Path "$HOME\Desktop\iam-s3-capstone"
What Do These Commands Do?
- New-Item creates the evidence folder on your Desktop.
- -Force returns the existing folder safely when you repeat the setup.
- Set-Location moves the current PowerShell session into that folder.
You should see the iam-s3-capstone directory in the command output. Your PowerShell prompt should now end with the same folder name.
- Create audit-gap.txt with the required evidence marker by running:
Set-Content -Path .\audit-gap.txt -Value "audit-gap-probe"
What Does This Command Save?
Set-Content creates audit-gap.txt in the current folder. It writes audit-gap-probe as the file's single line.
Before you read the file, what single line do you expect PowerShell to return? Hold that prediction.
- Read the saved evidence file by running:
Get-Content -Path .\audit-gap.txt
What Does This Check Prove?
Get-Content reads the file from your current folder. Seeing the marker confirms that the file exists with the expected content.
You should see audit-gap-probe in PowerShell. Your first audit artifact is now ready for the security experiment.
Evidence File Missing?
- Confirm your PowerShell prompt ends with iam-s3-capstone before reading the file.
- Repeat the Set-Content command if the file was created in another folder.
- Use Help me find or recreate audit-gap.txt in my iam-s3-capstone Desktop folder. for help checking the local path.
✔️ Awesome, I've got everything!
All set. audit-gap.txt is saved inside your Desktop evidence workspace with the required marker.
ⓧ I'd like to double check the full code
- Compare audit-gap.txt with this complete reference:
audit-gap-probe
Your local client is ready. Your evidence workspace can now capture the IAM decisions you test next.
Build and Simulate Least-Privilege IAM
Your Windows workstation is ready to capture evidence. Now you can define the access boundary that the workload eventually receives.
A least-privilege policy needs a testable access matrix before an application receives it. This step establishes that matrix before any EC2 instance exists.
You will secure an Amazon S3 bucket with AWS Identity and Access Management (IAM). IAM Access Analyzer validates the policy structure. IAM Policy Simulator predicts how the role handles each request.
In this step, get ready to:
- Create a private S3 workload bucket containing audit-gap.txt.
- Author a prefix-scoped policy and attach it to an EC2 service role.
- Inspect the role trust relationship and simulate three authorization requests.
Create the private workload bucket
The AWS Management Console gives you one place to create the workload bucket and confirm its security settings. The bucket holds the objects protected by your IAM policy.
This is the first point where the lab can begin generating small AWS charges. The cleanup section shows how to remove every resource when the investigation is complete.
- Sign in to the AWS Management Console with your lab administrator account.
- Select US East (N. Virginia) from the Region selector.
- Enter Amazon S3 in the console search bar.
- Select Amazon S3 from the search results.
- Choose Buckets in the left navigation.
- Choose Create bucket.
- Select General purpose as the bucket type.
- Enter a globally unique bucket name: your-unique-bucket-name.
Why must the name be unique?
General purpose S3 bucket names are shared across the AWS partition. A short personal suffix helps your name remain unique.
The name must contain 3 to 63 characters. Use lowercase letters, numbers, periods, or hyphens.
- Keep Object Ownership set to Bucket owner enforced.
- Keep all four Block Public Access settings enabled.
- Keep default encryption set to SSE-S3.
- Choose Create bucket.
Good progress. Your workload bucket now starts with private ownership controls and server-side encryption.
- Select your-unique-bucket-name from the bucket list.
- Choose Upload.
- Choose Add files.
- Browse to the existing iam-s3-capstone evidence workspace.
- Select audit-gap.txt.
- Choose Upload.
You should see audit-gap.txt in the bucket's object list. The upload creates the object-level request used by the audit experiment.
Bucket or upload unavailable?
- Add a lowercase personal suffix if AWS reports that the bucket name is unavailable.
- Confirm that the Region selector still shows US East (N. Virginia).
- Confirm that audit-gap.txt still exists inside the local iam-s3-capstone workspace.
Still stuck? Help me troubleshoot why I cannot create my private S3 bucket or upload audit-gap.txt.
Create the policy and EC2 role
An identity-based policy defines which API actions a role may request. Its resource scope limits those actions to specific AWS resources.
Your policy grants one action beneath one object prefix. Requests outside that prefix have no matching allow statement.
- Press the Windows key to open Windows search.
- Type Notepad into Windows search.
- Press Enter to open Notepad.
- Create the policy template by copying this code into the new Notepad document:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "WriteOnlyToApprovedPrefix",
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::your-unique-bucket-name/approved/*"
}
]
}
What does this policy do?
- The Version value selects the current IAM policy language version.
- The s3:PutObject action permits object uploads.
- The resource ARN limits that permission to objects beneath approved/* in one bucket.
- The missing s3:DeleteObject action leaves deletion outside the policy's allowed capabilities.
- Choose File in Notepad.
- Choose Save As.
- Select the existing iam-s3-capstone evidence workspace.
- Enter s3-role-policy.json in the File name field.
- Select All files in the Save as type field.
- Choose Save.
- Return to the iam-s3-capstone workspace in File Explorer.
You should see s3-role-policy.json beside audit-gap.txt.
File saved as text?
- Enable file name extensions in File Explorer if the complete file name is hidden.
- Rename the file if it ends with .json.txt.
- Confirm that Notepad used All files as the save type.
Need help? Help me fix my s3-role-policy.json file name or JSON structure.
✔️ Awesome, I've got everything!
Your reusable policy template is saved beside the existing audit evidence file.
ⓧ I'd like to double check the full code
- Compare your complete s3-role-policy.json file with this reference:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "WriteOnlyToApprovedPrefix",
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::your-unique-bucket-name/approved/*"
}
]
}
What should match?
Check the capitalization and punctuation exactly. The resource must end with /approved/*.
The local file remains a reusable template. The policy created in IAM needs the actual workload bucket name.
- Return to the AWS Management Console.
- Enter IAM in the console search bar.
- Select IAM from the search results.
- Choose Policies in the left navigation.
- Choose Create policy.
- Choose the JSON policy editor.
- Paste the contents of s3-role-policy.json into the editor.
- Replace your-unique-bucket-name with your-unique-bucket-name.
What should validation show?
IAM Access Analyzer groups findings under Security, Errors, Warnings, and Suggestions. Errors identify policy syntax or structural problems.
Security warnings identify policy elements that can grant risky access. This policy should have no errors or security warnings before you create it.
- Review the IAM Access Analyzer validation findings.
- Resolve every finding listed under Errors.
- Resolve every finding listed under Security.
- Choose Next.
- Enter IamS3ApprovedPrefixWrite as the policy name.
- Choose Create policy.
The customer managed policy now grants one narrowly scoped S3 capability. Its resource points only to the workload bucket's approved prefix.
An Amazon EC2 service role lets a future instance receive temporary credentials. The role holds the policy without storing long-lived access keys on the workload.
- Choose Roles in the IAM left navigation.
- Choose Create role.
- Select AWS service as the trusted entity type.
- Select EC2 as the use case.
- Choose Next.
- Select IamS3ApprovedPrefixWrite from the permissions policy list.
- Choose Next.
- Enter IamS3ProbeRole as the role name.
The role summary should show EC2 as the trusted service. It should also show IamS3ApprovedPrefixWrite as the attached permissions policy.
- Choose Create role.
How does the role reach EC2?
Creating an EC2 role in the IAM console also creates an instance profile with the same name. EC2 uses that profile to make the role available to an instance.
Applications on the instance receive automatically refreshed temporary credentials. No long-lived access key needs to exist on the server.
That is the policy layer complete. The future probe has a role with one approved write path and no destructive permission.
Inspect the trust and simulate access
A trust policy defines which principal can assume a role. The role's identity policy defines what the resulting session can request.
- Select IamS3ProbeRole from the IAM role list.
- Choose the Trust relationships tab.
You should see ec2.amazonaws.com as the trusted service principal. The trust action should be sts:AssumeRole.
- Choose the Permissions tab.
- Confirm that IamS3ApprovedPrefixWrite appears in the permissions policy list.
Before you simulate the requests, which action and resource pair should receive the one allowed result?
- Open IAM Policy Simulator from IAM.
- Select Roles as the principal type.
- Select IamS3ProbeRole as the principal.
- Select Amazon S3 as the service.
- Select s3:PutObject as the action.
- Enter arn:aws:s3:::your-unique-bucket-name/approved/report.txt as the resource ARN.
- Run the simulation.
You should see an allowed result. The action matches the policy and the object sits beneath the approved prefix.
- Replace the resource ARN with arn:aws:s3:::your-unique-bucket-name/outside/report.txt.
- Run the simulation again.
You should see a denied result. The requested object ARN falls outside the policy's approved prefix.
- Select s3:DeleteObject as the action.
- Enter arn:aws:s3:::your-unique-bucket-name/approved/report.txt as the resource ARN.
- Run the simulation again.
You should see another denied result. The resource matches the approved prefix, but the policy never allows deletion.
Why are the two requests denied?
IAM requires a matching allow statement for the requested action and resource. A missing match produces an implicit deny.
The outside-prefix write fails the resource check. The delete fails the action check.
Simulator results look different?
- Confirm that the selected principal is IamS3ProbeRole.
- Check that the approved resource ends with /approved/report.txt.
- Check that the outside resource ends with /outside/report.txt.
- Confirm that the policy resource ends with /approved/*.
Need another pair of eyes? Help me diagnose unexpected IAM Policy Simulator results for my EC2 role.
You have proved the authorization matrix before launching the workload. Next, you will expose the S3 audit gap and configure the data events needed to investigate these requests.
Enable S3 Data Event Auditing
Your IAM simulation now predicts the approved write plus two denied operations. The next security question is whether the audit record can prove what happened.
AWS CloudTrail separates management activity from Amazon S3 object data events. You will test the default view before creating the narrow trail your investigation needs.
In this step, get ready to:
- Prove whether Event history contains the earlier object upload.
- Create the security report that will hold your authorization evidence.
- Configure a trail that records selected S3 object data events.
Test the default audit view
The earlier upload gives you a real request to investigate. Its presence or absence tells you whether Event history provides enough evidence for this lab.
Before you search, do you think the object upload appears in the default history?
- Find CloudTrail by using the search field in the AWS Management Console.
- Select Event history in CloudTrail.
- Set the lookup attribute to the event name.
- Enter PutObject as the lookup value.
- Review the results for the earlier upload of audit-gap.txt.
You will find no matching object upload. That missing record is the intended visibility gap.
Why is the upload absent?
Event history contains the most recent 90 days of management events. S3 object operations such as PutObject are data events.
S3 object data events are not populated in Event history. A trail needs an explicit data-event selector to capture them.
- Use a Windows text editor to create SECURITY-REPORT.md inside the iam-s3-capstone evidence folder from earlier.
- Paste this first section into SECURITY-REPORT.md:
# IAM and S3 Security Report
Region: us-east-1
Workload bucket: your-unique-bucket-name
EC2 role: IamS3ProbeRole
Permissions policy: IamS3ApprovedPrefixWrite
CloudTrail trail: IamS3ProbeTrail
## Security objective
The EC2 workload may put objects only beneath `approved/*`. Writes elsewhere and all deletes remain denied.
## IAM design
- Trusted service principal: `ec2.amazonaws.com`
- Allowed action: `s3:PutObject`
- Allowed resource: `arn:aws:s3:::your-unique-bucket-name/approved/*`
- Long-lived access keys stored on the instance: No
- Intentionally omitted action: `s3:DeleteObject`
What does this report section capture?
- The header identifies the Region used by the lab.
- The resource names connect the report to your bucket policy and trail.
- The security objective defines the approved object prefix.
- The IAM design records which S3 action remains deliberately omitted.
- Save SECURITY-REPORT.md.
- Review the beginning of the file in your text editor.
You should see the report title followed by the lab identifiers. The IAM design should end with the omitted s3:DeleteObject action.
Does the report have the wrong filename?
- Check that the filename ends with .md.
- Remove any extra .txt extension added by your text editor.
- Keep the file inside the existing iam-s3-capstone evidence folder.
Still stuck? Help me check why my SECURITY-REPORT.md file has the wrong name or location.
- Place your cursor after the Intentionally omitted action line.
- Add the prediction tables and audit-gap section by pasting:
## Predicted and observed authorization
| Test | Predicted | Observed | Evidence |
| --- | --- | --- | --- |
| Put `outside/first.txt` | Denied | Pending | Add probe output |
| Put `approved/report.txt` | Allowed | Pending | Add probe output |
| Delete `approved/report.txt` | Denied | Pending | Add probe output |
## Audit visibility gap
CloudTrail Event history result for the first object upload: Add observation.
Reason: S3 object operations are data events and are not populated in Event history.
## CloudTrail evidence
| Field | Allowed PutObject | Denied request |
| --- | --- | --- |
| `eventTime` | Pending | Pending |
| `userIdentity.arn` | Pending | Pending |
| `eventName` | Pending | Pending |
| `requestParameters.bucketName` | Pending | Pending |
| `requestParameters.key` | Pending | Pending |
| `errorCode` | Not present on success | Pending |
| `errorMessage` | Not present on success | Pending |
What do these tables prove?
- The authorization matrix preserves your three policy predictions before the live tests begin.
- The audit-gap section records why the earlier upload was absent from Event history.
- The CloudTrail table names the fields you will collect from the later data-event records.
- Save SECURITY-REPORT.md.
- Review the authorization matrix in your text editor.
You should see three predicted outcomes. You should also see separate columns for the allowed request and the denied request.
Is the Markdown table misaligned?
- Confirm that every table row begins with a vertical bar.
- Confirm that every table row ends with a vertical bar.
- Restore any backticks that are missing around field names or object keys.
Need another pair of eyes? Help me compare my authorization and CloudTrail tables with the required Markdown structure.
- Place your cursor after the errorMessage table row.
- Complete the report template by pasting:
## Findings
1. Add the exact ARN mismatch that caused the first denial.
2. Explain why changing the key was safer than broadening the policy.
3. Explain why the delete remained denied after the successful write.
4. Record the difference between Event history and trail data events.
## Architecture narrative
1. The EC2 service principal assumes `IamS3ProbeRole` through its trust policy.
2. The instance profile supplies temporary role credentials to the Boto3 probe.
3. The identity policy permits `PutObject` only beneath `approved/*`.
4. CloudTrail records the selected S3 data events and delivers compressed JSON evidence to a separate log bucket.
5. The security report connects the principal, action, resource, and result for recruiter-friendly review.
LinkedIn caption: Built an IAM-first AWS security lab that uses an EC2 instance role, prefix-scoped S3 permissions, IAM simulation, and CloudTrail data events to prove and investigate least-privilege outcomes.
## Secret Mission
- Permissions boundary: Not attempted
- Temporary broad policy result: Not attempted
- Effective-permissions conclusion: Not attempted
## Cleanup
- Trail deleted or intentionally retained: Pending
- EC2 instance terminated or intentionally retained: Pending
- S3 buckets emptied and deleted or intentionally retained: Pending
- IAM policies and role deleted or intentionally retained: Pending
How will the remaining sections help?
- The findings prompts turn raw authorization results into security conclusions.
- The architecture narrative connects the role to its temporary credentials.
- The cleanup section preserves the final status of every chargeable resource.
- Save SECURITY-REPORT.md.
- Scroll to the bottom of the report in your text editor.
You should see the Secret Mission placeholders followed by four cleanup records. Your evidence template is now ready for the live authorization results.
Are sections missing from the report?
- Confirm that ## Findings follows the CloudTrail evidence table.
- Confirm that ## Cleanup is the final heading.
- Compare the complete file using the double-check tab below.
Need help finding the difference? Help me compare my SECURITY-REPORT.md file with the complete template.
✔️ Awesome, I've got everything!
Your SECURITY-REPORT.md file now contains the full evidence template. Keep it in the iam-s3-capstone folder for the later investigation.
ⓧ I'd like to double check the full code
Compare your SECURITY-REPORT.md file with this complete version:
# IAM and S3 Security Report
Region: us-east-1
Workload bucket: your-unique-bucket-name
EC2 role: IamS3ProbeRole
Permissions policy: IamS3ApprovedPrefixWrite
CloudTrail trail: IamS3ProbeTrail
## Security objective
The EC2 workload may put objects only beneath `approved/*`. Writes elsewhere and all deletes remain denied.
## IAM design
- Trusted service principal: `ec2.amazonaws.com`
- Allowed action: `s3:PutObject`
- Allowed resource: `arn:aws:s3:::your-unique-bucket-name/approved/*`
- Long-lived access keys stored on the instance: No
- Intentionally omitted action: `s3:DeleteObject`
## Predicted and observed authorization
| Test | Predicted | Observed | Evidence |
| --- | --- | --- | --- |
| Put `outside/first.txt` | Denied | Pending | Add probe output |
| Put `approved/report.txt` | Allowed | Pending | Add probe output |
| Delete `approved/report.txt` | Denied | Pending | Add probe output |
## Audit visibility gap
CloudTrail Event history result for the first object upload: Add observation.
Reason: S3 object operations are data events and are not populated in Event history.
## CloudTrail evidence
| Field | Allowed PutObject | Denied request |
| --- | --- | --- |
| `eventTime` | Pending | Pending |
| `userIdentity.arn` | Pending | Pending |
| `eventName` | Pending | Pending |
| `requestParameters.bucketName` | Pending | Pending |
| `requestParameters.key` | Pending | Pending |
| `errorCode` | Not present on success | Pending |
| `errorMessage` | Not present on success | Pending |
## Findings
1. Add the exact ARN mismatch that caused the first denial.
2. Explain why changing the key was safer than broadening the policy.
3. Explain why the delete remained denied after the successful write.
4. Record the difference between Event history and trail data events.
## Architecture narrative
1. The EC2 service principal assumes `IamS3ProbeRole` through its trust policy.
2. The instance profile supplies temporary role credentials to the Boto3 probe.
3. The identity policy permits `PutObject` only beneath `approved/*`.
4. CloudTrail records the selected S3 data events and delivers compressed JSON evidence to a separate log bucket.
5. The security report connects the principal, action, resource, and result for recruiter-friendly review.
LinkedIn caption: Built an IAM-first AWS security lab that uses an EC2 instance role, prefix-scoped S3 permissions, IAM simulation, and CloudTrail data events to prove and investigate least-privilege outcomes.
## Secret Mission
- Permissions boundary: Not attempted
- Temporary broad policy result: Not attempted
- Effective-permissions conclusion: Not attempted
## Cleanup
- Trail deleted or intentionally retained: Pending
- EC2 instance terminated or intentionally retained: Pending
- S3 buckets emptied and deleted or intentionally retained: Pending
- IAM policies and role deleted or intentionally retained: Pending
Create the dedicated trail
A trail needs its own delivery bucket. Reusing the workload bucket would cause every log delivery to create another matching PutObject data event.
Billing starts for matching data events. The narrow scope in this lab keeps the audit volume small.
- Return to CloudTrail in the AWS Management Console.
- Select Trails.
- Choose Create trail.
- Set the trail name to IamS3ProbeTrail.
- Select the option that applies the trail to all Regions.
- Select the option to create a new S3 bucket.
- Enter this unique name for the new destination bucket: your CloudTrail log bucket name.
- Create the trail.
Good progress. IamS3ProbeTrail now sends its logs to a separate S3 bucket.
What did you create?
- The multi-Region trail provides one audit configuration across AWS Regions.
- The separate bucket stores compressed CloudTrail log files.
- The trail still needs a data-event selector before it records S3 object requests.
Could not create the trail?
- Confirm that the trail name is exactly IamS3ProbeTrail.
- Choose a globally unique name for the separate S3 log bucket.
- Confirm that you are still using the AWS lab administrator account.
Need help with the failed creation? Help me troubleshoot why my CloudTrail trail or separate S3 log bucket could not be created.
Filter the S3 data events
An advanced selector controls which data events enter the trail. Your selector will include only object writes and object deletes for the workload bucket.
- Select IamS3ProbeTrail from the Trails page.
- Open the trail's event-selector editor.
- Choose advanced event selectors.
How does the selector stay narrow?
The category condition limits the selector to data events. The resource condition limits those events to S3 objects.
The action conditions keep only writes and deletes. The ARN condition limits those requests to one workload bucket.
- Set eventCategory to Data.
- Set resources.type to AWS::S3::Object.
- Add PutObject to the eventName condition.
- Add DeleteObject to the eventName condition.
- Set the resources.ARN condition to StartsWith arn:aws:s3:::your-unique-bucket-name/.
- Save the advanced selector.
Before you verify the trail, which two object operations do you expect its selector to contain?
- Return to the Trails page.
- Select IamS3ProbeTrail.
- Review the trail's logging status.
- Review the saved advanced selector.
You should see that logging is enabled for IamS3ProbeTrail.
What should I see?
- The eventCategory field shows Data.
- The resources.type field shows AWS::S3::Object.
- The eventName field contains PutObject plus DeleteObject.
- The resources.ARN condition begins with arn:aws:s3:::your-unique-bucket-name/.
- The destination is your CloudTrail log bucket name.
That is the audit foundation in place. The trail is ready to capture the allowed and denied S3 requests produced later.
Is the selector incomplete?
- Edit the selector if either PutObject or DeleteObject is missing.
- Check that the object ARN ends with a forward slash after the bucket name.
- Confirm that the resource type is AWS::S3::Object.
Still seeing the wrong scope? Help me troubleshoot my CloudTrail advanced selector for S3 object data events.
Your audit path is ready before the workload starts. Next, you will launch the role-backed probe on Amazon EC2.
Launch the Role-Backed Probe
Your CloudTrail trail is ready to capture selected object activity. The next proof has to come from an Amazon EC2 workload using temporary credentials from its IAM role.
The first live Amazon S3 request tests whether the policy protects the approved object path. You will send that request from a role-backed probe so no long-lived access keys reach the instance.
In this step, get ready to:
- Launch an Ubuntu EC2 instance with the required role-backed security configuration.
- Prepare the instance for the Python probe over SSH.
- Run the probe against an out-of-scope object key.
Launch the Ubuntu instance
An instance profile gives applications on EC2 temporary role credentials. Boto3 can discover those credentials automatically without a credentials file.
- Return to the AWS Management Console from earlier.
- Confirm the Region selector shows US East (N. Virginia).
- Enter EC2 in the console search bar.
- Select EC2 from the search results.
- Click Launch instance.
- Select Ubuntu under Application and OS Images.
- Choose the latest Canonical Ubuntu Server 24.04 LTS image whose name matches ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*.
- Select t3.micro under Instance type.
- Expand Advanced details.
- Select IamS3ProbeRole in the IAM instance profile field.
- Set the root volume size to 8 GiB under Configure storage.
- Select gp3 as the root volume type.
The private key controls SSH access to this instance. Store the downloaded file inside your local iam-s3-capstone evidence folder so it stays easy to find.
- Click Create new key pair in the Key pair (login) section.
- Enter your EC2 key pair name in the Key pair name field.
- Select .pem as the Private key file format.
- Click Create key pair.
- Move the downloaded .pem file into your local iam-s3-capstone folder.
Why does the key stay local?
The key proves your identity when OpenSSH connects to the instance. The application itself receives separate temporary credentials through IamS3ProbeRole.
- Expand Network settings by clicking Edit.
- Select Create security group.
- Enable Allow SSH traffic from.
- Choose My IP as the SSH source.
- Confirm the generated IPv4 source ends with /32.
- Click Launch instance.
A few minutes of pending status is normal while the instance boots Ubuntu.
- Click View all instances.
- Wait until your new instance shows Running in the Instance state column.
- Select the instance.
- Confirm its IAM role is IamS3ProbeRole.
How does the role reach the probe?
The IAM console created an instance profile for IamS3ProbeRole. EC2 uses that profile to make automatically refreshed temporary credentials available to applications on the instance.
Instance not ready?
- Confirm the instance is in us-east-1.
- Check that the instance state has changed from Pending to Running.
- Confirm IamS3ProbeRole appears in the instance details.
Still stuck? Help me diagnose why my Ubuntu EC2 instance is not reaching the Running state with IamS3ProbeRole attached.
Connect and prepare Python
The OpenSSH client gives you a secure shell on the instance from PowerShell. Ubuntu images use ubuntu as the default login name.
- Select the running instance in the EC2 console.
- Click Connect.
- Select the SSH client tab.
- Confirm the example command uses ubuntu before the public DNS name.
- Click Copy beside the example SSH command.
- Return to the PowerShell window from earlier.
- Paste the copied SSH command into PowerShell.
- Press Enter to connect.
The first connection can ask you to confirm the host identity. This confirmation records the instance as a trusted SSH host on your Windows device.
- Accept the host authenticity prompt if PowerShell displays one.
You should see a shell prompt beginning with ubuntu@. That prompt confirms PowerShell is controlling the new instance.
SSH connection failing?
- Confirm the downloaded .pem file is in the folder referenced by the copied SSH command.
- Check that the instance still has a public IPv4 address.
- Confirm the security group SSH source matches your current public IPv4 address with a /32 suffix.
Need a hand? Help me troubleshoot my PowerShell OpenSSH connection to an Ubuntu EC2 instance.
The probe needs Python with virtual-environment support. Updating the package index first lets Ubuntu locate the current package metadata.
- Refresh Ubuntu's package index by running this command in the SSH session:
sudo apt update
What does this command do?
This command refreshes the package information used by Ubuntu. It prepares the instance to install python3-full from the configured repositories.
- Install the full Python runtime with virtual-environment support by running:
sudo apt install -y python3-full
What does this command install?
The python3-full package supplies the default Python runtime for Ubuntu Server 24.04 LTS. It also includes the support needed to create a virtual environment.
- Check the installed Python version by running:
python3 --version
What does this command check?
The version check confirms which Python interpreter the instance resolves through python3.
✔️ I see version 3.12 or higher
Python is ready. Your instance has the required runtime for the pinned Boto3 package.
ⓧ I see an older version
An older version indicates that the instance does not match the intended Ubuntu Server image. Recreating this fresh instance is safe because the probe files have not been added yet.
- Return to the EC2 console.
- Terminate the new instance.
- Repeat the launch instructions with the latest Canonical image matching ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*.
- Reconnect as ubuntu.
- Repeat the Python package installation above.
ⓧ Command not found
The Python package did not install successfully. Refresh the package index before repeating the installation.
- Install the required package by running these commands:
sudo apt update
sudo apt install -y python3-full
What do these commands do?
The first command refreshes Ubuntu's package information. The second installs the full Python runtime with virtual-environment support.
- Repeat the Python version check after the installation completes.
Python installation failing?
- Confirm the SSH session still shows a prompt beginning with ubuntu@.
- Check that the package-index refresh completed before the installation command.
- Confirm the instance can reach the Ubuntu package repositories through its network connection.
Still blocked? Help me troubleshoot installing python3-full on my Ubuntu Server 24.04 LTS EC2 instance.
A virtual environment keeps the probe's packages separate from the operating system. Activating it also makes the python command point at the isolated environment.
- Create the virtual environment by running the first command below.
- Activate the virtual environment by running the second command below:
python3 -m venv .venv
source .venv/bin/activate
What do these commands do?
- The first command creates an isolated Python environment inside .venv.
- The second command activates that environment for the current SSH session.
You should see (.venv) at the start of the shell prompt. That marker confirms the isolated environment is active.
Virtual environment not active?
- Confirm the python3-full installation completed successfully.
- Check that the activation command was run from the folder containing .venv.
Need help? Help me diagnose why my Python virtual environment is not showing in my Ubuntu shell prompt.
Build and run the probe
The probe uses Boto3 to call STS for the active principal. It also calls S3 with credentials discovered from the instance profile.
- Create requirements.txt in the connected SSH session by running:
nano requirements.txt
What does this command do?
The command opens requirements.txt in the terminal editor. Nano creates the file when it does not already exist.
- Add the pinned dependency to requirements.txt by copying this line:
boto3==1.43.93
Why pin Boto3?
The version pin keeps the probe on Boto3 1.43.93 for a repeatable environment. Installing Boto3 also supplies its Botocore dependency for service error handling.
- Press Ctrl+O to write requirements.txt.
- Press Enter to confirm the file name.
- Press Ctrl+X to close Nano.
- Install the pinned dependency into the active virtual environment by running:
python -m pip install -r requirements.txt
What does this command install?
The command reads the version pin from requirements.txt. It installs Boto3 with the compatible Botocore package inside .venv.
The installation should complete without a dependency error. Your prompt should still begin with (.venv).
Dependency installation failing?
- Confirm the prompt begins with (.venv).
- Check that requirements.txt contains exactly boto3==1.43.93.
- Confirm the Python version is 3.12 or higher.
Still stuck? Help me troubleshoot installing Boto3 1.43.93 from requirements.txt inside my Ubuntu virtual environment.
- Create iam_s3_probe.py in the connected SSH session by running:
nano iam_s3_probe.py
What does this command do?
The command opens a new iam_s3_probe.py file in Nano. You will assemble the probe in one editing session.
- Build iam_s3_probe.py by pasting the next three code chunks in order:
import argparse
import json
import sys
import boto3
import botocore
AWS_REGION = "us-east-1"
def build_clients():
session = boto3.session.Session(region_name=AWS_REGION)
return session.client("sts"), session.client("s3")
def run_operation(s3_client, operation, bucket, key):
if operation == "put":
response = s3_client.put_object(
Bucket=bucket,
Key=key,
Body=b"IAM and S3 security probe\n",
)
else:
response = s3_client.delete_object(Bucket=bucket, Key=key)
return response["ResponseMetadata"]["HTTPStatusCode"]
What does this part do?
- The imports provide command-line parsing, JSON output, process exit handling, AWS service clients, and service error handling.
- The AWS_REGION value keeps the service clients in us-east-1.
- The build_clients() function creates STS and S3 clients from the instance-role session.
- The run_operation() function sends either a put request or a delete request for the chosen object.
def main():
parser = argparse.ArgumentParser(
description="Test an EC2 IAM role against one Amazon S3 object."
)
parser.add_argument("operation", choices=("put", "delete"))
parser.add_argument("bucket")
parser.add_argument("key")
args = parser.parse_args()
sts_client, s3_client = build_clients()
identity = sts_client.get_caller_identity()
result = {
"principal": identity["Arn"],
"operation": args.operation,
"resource": f"arn:aws:s3:::{args.bucket}/{args.key}",
}
What does this part do?
- The parser accepts an operation, bucket name, and object key from the command line.
- The STS client reads the active principal without requiring a separate permission.
- The result object records the principal, requested operation, and exact S3 object ARN.
try:
result["http_status"] = run_operation(
s3_client,
args.operation,
args.bucket,
args.key,
)
result["outcome"] = "ALLOWED"
exit_code = 0
except botocore.exceptions.ClientError as error:
result["outcome"] = "DENIED"
result["error_code"] = error.response["Error"]["Code"]
result["error_message"] = error.response["Error"]["Message"]
result["http_status"] = error.response["ResponseMetadata"][
"HTTPStatusCode"
]
exit_code = 2
print(json.dumps(result, indent=2))
return exit_code
if __name__ == "__main__":
sys.exit(main())
What does this part do?
- The try path records the HTTP status and an allowed outcome when S3 accepts the request.
- The error path captures the AWS error code, message, and HTTP status when authorization fails.
- The final lines print the complete result as formatted JSON and return a nonzero exit code for a denied request.
- Press Ctrl+O to write iam_s3_probe.py.
- Press Enter to confirm the file name.
- Press Ctrl+X to close Nano.
✔️ Awesome, I've got everything!
Your dependency file and probe are saved on the instance. Keep the virtual environment active for the live authorization test.
ⓧ I'd like to double check the full code
import argparse
import json
import sys
import boto3
import botocore
AWS_REGION = "us-east-1"
def build_clients():
session = boto3.session.Session(region_name=AWS_REGION)
return session.client("sts"), session.client("s3")
def run_operation(s3_client, operation, bucket, key):
if operation == "put":
response = s3_client.put_object(
Bucket=bucket,
Key=key,
Body=b"IAM and S3 security probe\n",
)
else:
response = s3_client.delete_object(Bucket=bucket, Key=key)
return response["ResponseMetadata"]["HTTPStatusCode"]
def main():
parser = argparse.ArgumentParser(
description="Test an EC2 IAM role against one Amazon S3 object."
)
parser.add_argument("operation", choices=("put", "delete"))
parser.add_argument("bucket")
parser.add_argument("key")
args = parser.parse_args()
sts_client, s3_client = build_clients()
identity = sts_client.get_caller_identity()
result = {
"principal": identity["Arn"],
"operation": args.operation,
"resource": f"arn:aws:s3:::{args.bucket}/{args.key}",
}
try:
result["http_status"] = run_operation(
s3_client,
args.operation,
args.bucket,
args.key,
)
result["outcome"] = "ALLOWED"
exit_code = 0
except botocore.exceptions.ClientError as error:
result["outcome"] = "DENIED"
result["error_code"] = error.response["Error"]["Code"]
result["error_message"] = error.response["Error"]["Message"]
result["http_status"] = error.response["ResponseMetadata"][
"HTTPStatusCode"
]
exit_code = 2
print(json.dumps(result, indent=2))
return exit_code
if __name__ == "__main__":
sys.exit(main())
Before you run the probe, do you think the role can write to outside/first.txt? Make your prediction before you continue.
- Test the out-of-scope object key by running:
python iam_s3_probe.py put your-unique-bucket-name outside/first.txt
The denial is expected
The JSON output should identify an assumed-role principal for IamS3ProbeRole. It should also contain "outcome": "DENIED" and "error_code": "AccessDenied".
The requested resource ends in outside/first.txt. The identity policy allows writes only beneath approved/*.
Probe output different?
- Confirm the shell prompt begins with (.venv).
- Check that the principal in the JSON output names IamS3ProbeRole.
- Confirm the requested key is exactly outside/first.txt.
Need help interpreting the result? Help me troubleshoot why my role-backed S3 probe did not return the expected AccessDenied result.
You have now turned the prefix restriction into a live authorization result from temporary role credentials.
Your probe is running with IamS3ProbeRole. Next, you will correct the object key without broadening the policy and connect both outcomes to CloudTrail evidence.
Verify Access and Investigate CloudTrail
Your first live probe showed that IAM rejects a write outside the approved object path. You now have a concrete denial to investigate.
The next goal is to prove that the same role can write to the approved Amazon S3 prefix without changing the policy. Changing the object key preserves least privilege.
You will also prove that successful writes do not grant destructive access. AWS CloudTrail then connects each authorization result to an audit record.
In this step, get ready to:
- Prove the approved write succeeds with the existing policy.
- Prove destructive access remains denied.
- Connect the live outcomes to CloudTrail records in your security report.
Match the key to the approved prefix
The failed request targeted the resource ARN arn:aws:s3:::your-unique-bucket-name/outside/first.txt. The policy allows s3:PutObject only beneath arn:aws:s3:::your-unique-bucket-name/approved/*.
- Compare the failed resource ARN with the allowed resource ARN.
- Keep IamS3ApprovedPrefixWrite unchanged.
Before you run the next request, do you expect changing only the key to produce an allowed result?
- Test the approved object path by running this command in the Ubuntu terminal from earlier:
python iam_s3_probe.py put your-unique-bucket-name approved/report.txt
What does this request prove?
- The put operation asks the probe to call s3:PutObject.
- The approved/report.txt key falls beneath the policy resource ending in approved/*.
- The probe uses temporary credentials from IamS3ProbeRole through the existing instance profile.
You should see a JSON result containing the assumed-role principal plus "outcome": "ALLOWED". That result proves the action and exact object path both match the policy.
Still seeing a denied write?
- Check that you replaced your-unique-bucket-name with the exact workload bucket name.
- Check that the key is exactly approved/report.txt.
- Confirm the output principal contains IamS3ProbeRole.
Still stuck? Help me compare my approved PutObject request with the IAM policy resource.
Test the implicit delete denial
The role has no statement allowing s3:DeleteObject. That missing permission produces an implicit deny even though the same role created the object.
Before you run the delete request, do you expect ownership of the successful write to change the authorization decision?
- Test deletion of the approved object by running this command:
python iam_s3_probe.py delete your-unique-bucket-name approved/report.txt
What does this request prove?
- The delete operation makes run_operation() call delete_object().
- The identity policy contains no allowance for the required delete action.
- The probe catches the service error and prints its authorization details before returning exit code 2.
You should see "outcome": "DENIED" plus "error_code": "AccessDenied". This is the intended result.
- Return to the Amazon S3 console from earlier.
- Select your workload bucket.
- Open the approved prefix.
- Confirm report.txt is still listed.
You have now proved both sides of the policy boundary. The role can write beneath the approved prefix while the object remains protected from deletion.
Cannot find the approved object?
- Return to the approved write output and confirm it contains "outcome": "ALLOWED".
- Check that the S3 console shows the same workload bucket used by the probe.
- Check that you opened the approved prefix.
Need help? Help me determine why approved/report.txt is missing after my allowed PutObject request.
Trace the requests in CloudTrail
CloudTrail records the selected S3 object requests in compressed log files. The matching events connect the assumed role to each action and resource.
Log delivery averages about five minutes and is not guaranteed to arrive immediately. A short wait here is normal while CloudTrail delivers the newest records.
- Return to the Amazon S3 console.
- Select the separate CloudTrail log bucket from earlier.
- Open the AWSLogs folder.
- Open the folder for your AWS account ID.
- Open the CloudTrail folder.
- Open the us-east-1 folder.
- Follow the current YYYY/MM/DD folder path.
- Wait about five minutes if the newest requests are not visible.
Before you inspect the newest records, which fields do you expect the successful request and denied requests to share?
- Download the newest .json.gz object.
- Extract the JSON file with the archive tool available on your Windows device.
- Open the extracted JSON file in a text editor.
- Locate the denied PutObject event for outside/first.txt.
- Locate the allowed PutObject event for approved/report.txt.
- Locate the denied DeleteObject event for approved/report.txt.
- Compare the event fields across all three records.
How do the records become evidence?
- The userIdentity.arn field identifies the assumed IamS3ProbeRole session.
- The eventName field distinguishes PutObject from DeleteObject.
- The requestParameters.bucketName field identifies the workload bucket.
- The requestParameters.key field shows whether the request targeted outside/first.txt or approved/report.txt.
- The denied records include errorCode and errorMessage fields describing the authorization failure.
The allowed PutObject record should have no authorization error fields. The denied requests should contain AccessDenied evidence tied to the same assumed role.
- Return to SECURITY-REPORT.md in the local evidence workspace from earlier.
- Update the Predicted and observed authorization table with all three live outcomes.
- Complete the Audit visibility gap section with the earlier missing object-level request.
- Record the exact ARN mismatch under Findings.
- Fill the CloudTrail evidence table with the matching event fields.
- Explain why changing the object key preserved least privilege under Findings.
- Explain why the delete request remained denied under Findings.
- Confirm the Architecture narrative connects the instance role credentials to the policy decision and audit evidence.
That completes the security evidence chain. Your report now connects the principal, action, resource, authorization result, and CloudTrail record for the same workload.
Cannot find the newest events?
- Wait another five minutes before checking the current date folder again.
- Confirm IamS3ProbeTrail is still logging to the separate log bucket.
- Confirm the trail selector includes PutObject and DeleteObject for the workload bucket.
Still missing an event? Help me trace my S3 probe requests through the CloudTrail log path and advanced event selector.
Secret mission
Cap Future Permissions with a Boundary
A narrow role can gain broader permissions when someone attaches another identity policy later. Add a permissions boundary, test it against an intentionally broad Amazon S3 policy, then remove the risky policy while keeping the cap in place.
Clean Up Your Resources
Clean Up Your Resources
These AWS resources can keep accruing charges while they run or retain data. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Cost warning
A running Amazon EC2 instance accrues compute charges. Its public IPv4 address accrues a separate hourly charge.
Amazon EBS storage remains billable while the root volume exists.
Amazon S3 storage remains billable while objects remain. AWS CloudTrail data events can add usage charges.
An unreachable trail destination can trigger delivery retries for up to 30 days. The deletion order below removes the trail before its destination bucket.
Resources you used:
- Private S3 workload bucket your-unique-bucket-name.
- Separate S3 log bucket your CloudTrail log bucket name.
- CloudTrail trail IamS3ProbeTrail.
- Running t3.micro Ubuntu EC2 instance.
- Attached 8 GiB gp3 root volume.
- Public IPv4 address assigned to the running instance.
- Security group used to restrict SSH access.
- EC2 key pair used for SSH authentication.
- IAM service role IamS3ProbeRole.
- Instance profile for IamS3ProbeRole.
- Customer managed policy IamS3ApprovedPrefixWrite.
- Customer managed permissions boundary IamS3ApprovedBoundary.
- Local iam-s3-capstone evidence folder.
- Downloaded .pem private key file.
Keep everything running
No action needed. Choose this if you are still testing the workload or collecting audit evidence.
- Keep the Ubuntu EC2 instance running while you need live access to the probe.
- Keep IamS3ProbeTrail logging while you need new S3 data events.
- Retain both S3 buckets while you need the workload objects or audit logs.
- Expect EC2 compute charges to continue while the instance runs.
- Expect storage charges to continue while the EBS volume or S3 objects remain.
Pause - I'll come back to this later
Shut down active services to reduce charges while keeping your evidence and configuration for later.
- Return to Amazon EC2 in the AWS Management Console from earlier.
- Select the role-backed Ubuntu instance.
- Stop the instance from its instance state controls.
- Confirm the instance state is stopped.
- Return to Trails in AWS CloudTrail.
- Select IamS3ProbeTrail.
- Turn off logging for the trail.
- Confirm the trail shows logging disabled.
The stopped instance no longer incurs compute charges. Its EBS volume remains billable.
Your S3 objects also remain billable while stored.
- Resume the lab by starting the Ubuntu instance.
- Resume audit collection by turning on logging for IamS3ProbeTrail.
- Reconnect from PowerShell with the instance's current SSH connection details.
Delete - I don't want to use this again
Remove all project resources to stop future charges. Deletion is permanent, so the sequence begins by preserving your final report.
Preserve your evidence:
- Download a final copy of SECURITY-REPORT.md to a location outside the iam-s3-capstone folder.
- Confirm the downloaded report opens from your Windows device.
Remove the audit trail first:
- Return to Trails in AWS CloudTrail.
- Select IamS3ProbeTrail.
- Delete the trail.
- Confirm IamS3ProbeTrail no longer appears in the trail list.
Remove the S3 buckets:
- Return to the S3 bucket list from earlier.
- Select the log bucket your CloudTrail log bucket name.
- Empty the log bucket.
- Delete the empty log bucket.
- Select the workload bucket your-unique-bucket-name.
- Empty the workload bucket.
- Delete the empty workload bucket.
- Confirm neither bucket appears in the bucket list.
Remove the EC2 resources:
- Return to Amazon EC2 in the AWS Management Console.
- Select the role-backed Ubuntu instance.
- Terminate the instance from its instance state controls.
- Confirm the instance reaches the terminated state.
- Confirm the 8 GiB gp3 root volume no longer appears in the volume list.
- Delete the root volume if it remains after termination.
- Confirm the public IPv4 address is no longer attached to the terminated instance.
- Delete the security group created for SSH access.
- Delete the EC2 key pair created for the instance.
Remove the IAM design:
- Return to Roles in IAM.
- Select IamS3ProbeRole.
- Remove IamS3ApprovedBoundary as the role's permissions boundary.
- Detach IamS3ApprovedPrefixWrite from the role.
- Return to Policies in IAM.
- Delete IamS3ApprovedPrefixWrite.
- Delete IamS3ApprovedBoundary.
- Delete IamS3ProbeRole.
- Delete the IamS3ProbeRole instance profile if it remains after the role deletion.
Remove the local files:
- Return to File Explorer on Windows.
- Locate the iam-s3-capstone evidence folder created earlier.
- Right-click the folder.
- Select Delete.
- Locate the downloaded .pem private key file.
- Delete the private key unless you deliberately reuse it for another instance.
Nice Work!
Nice Work!
You did it! Your AWS security lab now proves IAM least privilege with live Amazon S3 requests. AWS CloudTrail turns those outcomes into audit evidence you can trace back to the assumed role.
You've learned how to:
- Built a role-backed Amazon EC2 probe that uses temporary credentials to write only beneath approved/* in your private S3 bucket.
- Predicted least-privilege decisions with IAM Policy Simulator. Confirmed the same matrix through live Boto3 calls. One approved-prefix write succeeded. The out-of-prefix write plus delete remained denied.
- Enabled narrow S3 data events in AWS CloudTrail. Connected the assumed role's authorization outcomes to SECURITY-REPORT.md.
- Completed the optional Secret Mission by applying the permissions boundary IamS3ApprovedBoundary to IamS3ProbeRole. Proved the effective intersection keeps approved-prefix PutObject allowed. Out-of-prefix PutObject plus DeleteObject remained denied after attaching TemporaryBroadS3Access.
Ready to quiz yourself?