Harden an AWS Account with IAM Controls
Audit, lock down, and monitor your AWS account with IAM security controls.
Introduction
30 Second Summary
Every time someone creates a cloud account, it starts wide open. Without security controls, any misconfigured permission or forgotten access key is an invitation for trouble.
In this project, you will harden an AWS account by auditing its current security posture, enforcing least-privilege access, enabling audit logging, and wiring up automated alerts that fire when sensitive actions occur.
What You'll Build
A production-style security baseline for your AWS account using IAM controls, audit logging, and event-driven alerting.
By the end of this project, you'll have:
- A credential report documenting your account's before-and-after security state.
- A custom IAM policy enforcing least-privilege access for a developer role.
- A CloudTrail trail logging all management API activity to S3, with an EventBridge rule that triggers email alerts on sensitive IAM events.
- Secret Mission: Enable IAM Access Analyzer to detect any public or cross-account access to your resources.
Are there any prerequisites?
You need an AWS account (free tier eligible) and familiarity with navigating the AWS Console at the Cloud Practitioner level. No prior CLI experience is required.
Install and Configure the AWS CLI
Every AWS security hardening task you do in this project depends on the AWS CLI. The CLI gives you programmatic access to your account, which means you can run audit commands, generate reports, and manage IAM resources without clicking through the console for every action.
In this step, you will install the AWS CLI, connect it to your AWS account using access keys, and generate a credential report that captures your account's current security state.
In this step, get ready to:
- Install AWS CLI v2 on your Mac and verify it works.
- Configure the CLI with your AWS credentials so it can talk to your account.
- Generate a credential report that documents your account's before-state security posture.
Install AWS CLI v2 on macOS
The AWS CLI is a command-line tool that lets you interact with AWS services from your terminal. You need version 2 for this project.
- Open Terminal by pressing Cmd+Space to open Spotlight, typing Terminal, and pressing Enter.
- Check if the AWS CLI is already installed by running this command:
aws --version
✔️ I see aws-cli/2.x.x
Your AWS CLI v2 is already installed and ready to use. Move on to the next substep.
ⓧ I see aws-cli/1.x.x
You have an older version of the AWS CLI installed. Version 1 and version 2 use the same aws command name, so you need to uninstall v1 first, then install v2.
- Uninstall v1 using the same method you originally installed it with. If you used pip, run:
pip3 uninstall awscli
- If you installed v1 using the bundled installer, remove it by running:
sudo rm -rf /usr/local/aws
sudo rm /usr/local/bin/aws
- After uninstalling, confirm it is gone by running aws --version again. You should see a "command not found" message.
- Now install AWS CLI v2 by running these commands:
curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
sudo installer -pkg AWSCLIV2.pkg -target /
What do these commands do?
The curl command downloads the official AWS CLI v2 installer package from Amazon's servers. The sudo installer command runs the macOS package installer, which places files in /usr/local/aws-cli and creates a symlink at /usr/local/bin/aws.
- Your terminal will ask for your macOS login password when you run sudo. Type it and press Enter. You will not see characters appear as you type. This is normal.
- Verify the installation by running:
aws --version
You should see output starting with aws-cli/2. followed by version details.
ⓧ Command not found
The AWS CLI is not installed on your machine. Let's install it now.
- Download and install the AWS CLI v2 by running these commands:
curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
sudo installer -pkg AWSCLIV2.pkg -target /
What do these commands do?
The curl command downloads the official AWS CLI v2 installer package from Amazon's servers. The sudo installer command runs the macOS package installer, which places files in /usr/local/aws-cli and creates a symlink at /usr/local/bin/aws.
- Your terminal will ask for your macOS login password when you run sudo. Type it and press Enter. You will not see characters appear as you type. This is normal.
- Verify the installation by running:
aws --version
You should see output starting with aws-cli/2. followed by version details.
Still seeing command not found?
Close your terminal window completely and open a new one. The symlink created by the installer needs a fresh shell session to take effect.
If it still does not work, check that /usr/local/bin is in your PATH by running echo $PATH.
Help me troubleshoot the AWS CLI installation on macOS.
Configure the CLI with your AWS credentials
The CLI needs credentials to authenticate with your AWS account. You will create an access key in the IAM console and then feed it to the CLI.
- Switch to your browser where you are signed into the AWS Management Console.
- In the search bar at the top of the console, type IAM and select IAM from the results.
- In the left navigation pane, click Users.
- Click on your IAM admin user's name to open the user details page.
What if I only have a root account?
If you do not see any users listed, you are likely using only the root account. In the upper-right corner of the console, click your account name or number and choose Security Credentials. You can create an access key from there.
For this project, using root credentials temporarily is acceptable. In a real production environment, you would create a dedicated IAM admin user first.
- Click the Security credentials tab.
- Scroll down to the Access keys section and click Create access key.
- On the best practices page, select Command Line Interface (CLI) as your use case.
- Check the confirmation checkbox at the bottom, then click Next.
- Skip the optional description tag and click Create access key.
- You will see your Access Key ID and Secret Access Key. Click Download .csv file to save them securely. Keep this page open for the next step.
This is your only chance to see the Secret Access Key
AWS only displays the Secret Access Key once, at the moment of creation. If you close this page without saving it, you will need to delete the key and create a new one. Download the CSV file or copy both values to a secure location before continuing.
- Switch back to your terminal and configure the CLI by running:
aws configure
What will aws configure ask for?
The command prompts you for four pieces of information, one at a time:
- AWS Access Key ID: paste the Access Key ID from the CSV you downloaded.
- AWS Secret Access Key: paste the Secret Access Key from the CSV you downloaded.
- Default region name: enter us-east-1 (or your preferred region).
- Default output format: enter json.
- Verify your configuration by running:
aws sts get-caller-identity
You should see a JSON response containing your Account ID, user ARN, and UserId. This confirms the CLI can authenticate with your AWS account.
Seeing an error about invalid credentials?
Double-check that you pasted the Access Key ID and Secret Access Key correctly with no extra spaces. You can re-run aws configure to re-enter them.
If you see "The security token included in the request is invalid", make sure you are not also setting an AWS_SESSION_TOKEN environment variable in your terminal.
Help me troubleshoot my AWS CLI credentials configuration.
Generate your first credential report via CLI
A credential report is a CSV snapshot of every user in your AWS account, showing whether they have passwords enabled, MFA active, and when their access keys were last rotated. You will generate one now as your "before" baseline.
- Start generating the credential report by running:
aws iam generate-credential-report
What does this command do?
This tells AWS to compile a fresh credential report for your account. The response will show a State field set to either STARTED or COMPLETE. If it says STARTED, wait a few seconds and run the command again until it shows COMPLETE.
- Once the report state is COMPLETE, download and decode it by running:
aws iam get-credential-report --query 'Content' --output text | base64 --decode > credential-report-before.csv
What does this command do?
AWS returns the credential report as a base64-encoded string. The --query 'Content' flag extracts just the encoded content. Piping through base64 --decode converts it to readable CSV. The > credential-report-before.csv saves the result to a file in your current directory.
- Confirm the file was created by running:
ls credential-report-before.csv
You should see credential-report-before.csv listed in the output.
- Open the CSV to inspect its contents by running:
cat credential-report-before.csv
Review the columns that matter most for security auditing: user, password_enabled, mfa_active, access_key_1_active, and access_key_1_last_rotated.
What do these columns tell you?
- user: the IAM user name (or <root_account> for the root user).
- password_enabled: whether the user can log in to the AWS Console.
- mfa_active: whether multi-factor authentication is enabled. This should be true for every user.
- access_key_1_active: whether the user has an active access key.
- access_key_1_last_rotated: when the access key was last changed. Keys that have never been rotated are a security risk.
Save this as portfolio evidence
This report is your "before" snapshot. In the final step of the project, you will generate an "after" report to show the security improvements you made. The comparison is excellent evidence for a cloud security portfolio.
Seeing an access denied error?
Your IAM user needs the iam:GenerateCredentialReport and iam:GetCredentialReport permissions. If you are using an admin user with AdministratorAccess, you already have these. If you created a limited user, attach the IAMReadOnlyAccess managed policy.
Help me fix a permissions error when generating the credential report.
Your CLI is installed, configured, and you have your baseline security snapshot saved. Next up, you will use this report to identify security gaps and strengthen your account's IAM controls.
Audit and Strengthen IAM Controls
Your CLI is configured and you have a credential report snapshot saved locally. Now it's time to put that report to work.
In this step, you will review the credential report for security gaps, enforce a stronger password policy, and create a least-privilege IAM group that limits what developers can do in your account. These are the foundational controls that every cloud security engineer implements on day one.
In this step, get ready to:
- Audit your credential report for MFA gaps, stale keys, and inactive users.
- Strengthen your account password policy with enterprise-grade requirements.
- Create a least-privilege IAM group with a custom read-only policy.
Review your credential report findings
The credential report you generated in the previous step is a CSV file containing the security posture of every IAM user in your account. Your job now is to check it for common security gaps that attackers exploit.
- Open credential-report-before.csv by running this command in your terminal:
open credential-report-before.csv
This opens the file in your default spreadsheet application (Numbers on macOS). If you prefer viewing it in the terminal, you can run cat credential-report-before.csv instead.
- Check the mfa_active column for every user. Any value showing false means that user has no MFA protection.
- Check the access_key_1_last_rotated column. Any key that has never been rotated (or was rotated more than 90 days ago) is a risk.
- Check the password_last_used column. Users with passwords who have never logged in may be stale accounts that should be removed.
- Make a mental note of any findings you spot. These are the security gaps you will document later as your "before" state.
What am I looking for?
Real-world credential audits focus on three categories: accounts without MFA (easy takeover targets), access keys that have never been rotated (long-lived secrets), and dormant accounts that nobody uses but still have active credentials.
Even if your account only has one user right now, documenting these checks proves you understand the methodology.
Strengthen the account password policy
AWS accounts start with a permissive default password policy. Strengthening it ensures that any IAM users created in the future must use complex, regularly rotated passwords.
- In the AWS Management Console, type IAM in the top search bar and select IAM from the results.
- In the left navigation pane, click Account settings.
- In the Password policy section, click Edit.
- Select Custom to enable a custom password policy.
- Set Minimum password length to 14.
- Check the box for Require at least one uppercase letter.
- Check the box for Require at least one lowercase letter.
- Check the box for Require at least one number.
- Check the box for Require at least one non-alphanumeric character.
- Check Enable password expiration and set it to 90 days.
- Check Prevent password reuse and set Number of passwords to remember to 12.
- Click Save changes.
Why these specific values?
A 14-character minimum with complexity requirements makes brute-force attacks exponentially harder. The 90-day rotation and 12-password reuse prevention align with common enterprise security frameworks like CIS Benchmarks.
These settings only affect future password changes. Existing passwords remain valid until the user's next rotation.
- Verify your new password policy via the CLI by running this command:
aws iam get-account-password-policy
You should see a JSON response showing your updated settings, including MinimumPasswordLength: 14, MaxPasswordAge: 90, and PasswordReusePrevention: 12.
Seeing a NoSuchEntity error?
This means no custom password policy was saved. Return to the IAM Console, click Account settings, and confirm the Password policy section shows your custom settings. If it still shows the AWS default, repeat the steps above and make sure you clicked Save changes.
help me troubleshoot the NoSuchEntity error when getting my IAM password policy
Create a least-privilege IAM group and policy
The Principle of Least Privilege means giving users only the permissions they need to do their job. You will now create a "Developers" group with a custom policy that allows read-only access to EC2, S3, and CloudWatch.
- Create the IAM group by running this command:
aws iam create-group --group-name Developers
You should see a JSON response containing the group's GroupName and Arn.
Seeing an EntityAlreadyExists error?
This means a group named Developers already exists in your account. You can continue to the next step and attach the policy to the existing group.
help me resolve the EntityAlreadyExists error when creating an IAM group
- Create the custom policy file by running this command:
cat > developer-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:Describe*",
"ec2:Get*",
"s3:Get*",
"s3:List*",
"cloudwatch:Describe*",
"cloudwatch:Get*",
"cloudwatch:List*"
],
"Resource": "*"
}
]
}
EOF
What does this policy allow?
This policy grants read-only access across three services. The Describe*, Get*, and List* action patterns let users view resources without modifying anything.
A developer assigned to this group can check EC2 instance status, browse S3 bucket contents, and read CloudWatch metrics. They cannot launch instances, delete objects, or change alarms.
- Confirm the file was created by running:
ls developer-policy.json
You should see developer-policy.json listed in the output.
- Create the IAM policy from your file by running this command:
aws iam create-policy --policy-name DeveloperReadOnly --policy-document file://developer-policy.json
The output includes a Policy object with an Arn field. Copy the full ARN value. You need it for the next command.
Finding your policy ARN
The ARN looks like arn:aws:iam::123456789012:policy/DeveloperReadOnly where the number is your AWS account ID. Copy it directly from the terminal output.
- Attach the policy to the Developers group by running this command:
aws iam attach-group-policy --group-name Developers --policy-arn [[POLICY_ARN="your DeveloperReadOnly policy ARN from the previous output"]]
- Verify the policy is attached by running:
aws iam list-attached-group-policies --group-name Developers
You should see DeveloperReadOnly listed under AttachedPolicies in the JSON output.
Don't see DeveloperReadOnly in the output?
Make sure the ARN you pasted in the attach command matches the ARN from the create-policy output exactly. Run aws iam list-policies --query 'Policies[?PolicyName==`DeveloperReadOnly`].Arn' to look up the correct ARN.
help me figure out why my policy isn't attaching to the Developers group
✔️ Awesome, I've got everything!
Great. Your Developers group is ready with a least-privilege policy attached.
ⓧ I'd like to double check the full code
Here is the complete developer-policy.json file for comparison:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:Describe*",
"ec2:Get*",
"s3:Get*",
"s3:List*",
"cloudwatch:Describe*",
"cloudwatch:Get*",
"cloudwatch:List*"
],
"Resource": "*"
}
]
}
Your account now has a documented security baseline, a hardened password policy, and a least-privilege group ready for developers. Next up, you will enable CloudTrail to log every API call made in your account so nothing goes unnoticed.
Enable CloudTrail Audit Logging
Your IAM controls are in place. You have a strong password policy and a least-privilege group ready for developers.
Now you need visibility into what is actually happening in your account. Without audit logging, someone could create a new admin user or delete a security policy and you would never know. In this step, you will enable CloudTrail to record every management API call across all regions and store those logs in S3.
In this step, get ready to:
- Create an S3 bucket to store CloudTrail logs.
- Create a CloudTrail trail that logs all management events across all regions.
- Verify CloudTrail is actively recording by testing with a temporary IAM user.
Create an S3 bucket for CloudTrail logs
CloudTrail needs a dedicated S3 bucket to store log files. The bucket also requires a specific policy that grants the CloudTrail service permission to write logs into it.
- Create a uniquely named S3 bucket by running this command:
aws s3 mb s3://cloudtrail-logs-[[ACCOUNT_ID="your-12-digit-AWS-account-ID"]]-security-lab
Why does the bucket name include your account ID?
S3 bucket names must be globally unique across all AWS accounts. Including your account ID ensures no naming conflicts with other AWS users.
- Verify the bucket was created by running:
aws s3 ls | grep cloudtrail-logs
You should see your new bucket listed in the output with today's date.
Bucket creation failed?
If you see a BucketAlreadyExists error, someone else has claimed that bucket name. Double-check you replaced the placeholder with your actual 12-digit account ID.
Help me troubleshoot this issue
Next, you need to apply a bucket policy that allows the CloudTrail service to write log files. Without this policy, CloudTrail cannot deliver logs to your bucket.
- Create a file called bucket-policy.json with the following content:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSCloudTrailAclCheck20150319",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:GetBucketAcl",
"Resource": "arn:aws:s3:::cloudtrail-logs-[[ACCOUNT_ID="your-12-digit-AWS-account-ID"]]-security-lab"
},
{
"Sid": "AWSCloudTrailWrite20150319",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::cloudtrail-logs-[[ACCOUNT_ID="your-12-digit-AWS-account-ID"]]-security-lab/AWSLogs/[[ACCOUNT_ID="your-12-digit-AWS-account-ID"]]/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}
What does this bucket policy do?
This policy grants two permissions to the cloudtrail.amazonaws.com service principal:
- s3:GetBucketAcl lets CloudTrail verify it has permission to write to the bucket before delivering logs.
- s3:PutObject lets CloudTrail write the actual log files into the AWSLogs/<account-id>/ path inside the bucket.
- Apply the bucket policy by running:
aws s3api put-bucket-policy --bucket cloudtrail-logs-[[ACCOUNT_ID="your-12-digit-AWS-account-ID"]]-security-lab --policy file://bucket-policy.json
- Verify the policy was applied by running:
aws s3api get-bucket-policy --bucket cloudtrail-logs-[[ACCOUNT_ID="your-12-digit-AWS-account-ID"]]-security-lab
You should see the JSON policy you just applied printed in the terminal output.
Access denied when applying the policy?
Make sure the IAM user you configured in aws configure has s3:PutBucketPolicy permission. If you are using an admin user, this should work without issues.
Help me fix this error
Create a CloudTrail trail
With your bucket and policy in place, you can now create the trail itself. You will do this in the CloudTrail Console so you can see all the configuration options visually.
- In the AWS Management Console, search for CloudTrail in the search bar and open the CloudTrail service.
- On the CloudTrail dashboard, click **Trails** in the left navigation pane.
- Click **Create trail**.
- For **Trail name**, enter security-audit-trail.
- For **Storage location**, select **Use existing S3 bucket**.
- In the **Trail log bucket name** field, enter the name of the bucket you just created: cloudtrail-logs-<your-account-id>-security-lab.
- For **Log file SSE-KMS encryption**, leave it disabled (SSE-S3 encryption is sufficient for this project).
- Leave the default settings for **Additional settings** and **CloudWatch Logs**.
- Click **Next**.
On the **Choose log events** page, you will configure which events the trail records.
- Keep the default **Management events** selected.
- In the **Management events** area, ensure both **Read** and **Write** are checked.
- Do NOT enable **Data events**. Data events log every S3 object-level or Lambda invocation and cost money beyond the free tier.
- Leave **Insights events** and **Network activity events** at their defaults (not logged).
- Click **Next**.
- On the **Review and create** page, confirm your settings and click **Create trail**.
What is a multi-region trail?
Trails created via the console are multi-region by default. This means CloudTrail records management events across every enabled AWS Region in your account and delivers them all to your single S3 bucket.
The first copy of management events is delivered free in each region. This is why the project stays within the free tier.
- After creation, verify the trail status shows **Logging** on the **Trails** page.
Data events cost money
Data events (S3 object access, Lambda invocations) always incur charges regardless of free tier. Management events are the only event type where the first copy is free.
Trail status shows an error?
If the trail shows an S3 delivery error, double-check your bucket policy. The bucket name in the policy must match the bucket name in the trail exactly, including your account ID.
Help me troubleshoot my trail
Verify CloudTrail is recording
A trail that shows "Logging" is good, but you should confirm events actually appear in the history. You will create a temporary IAM user to generate a test event, then look for it in CloudTrail.
- Create a temporary test user by running:
aws iam create-user --user-name test-audit-user
What does this command do?
This creates a new IAM user called test-audit-user. The creation triggers a CreateUser API event that CloudTrail should record. You will look for this event to prove the trail is working.
CloudTrail typically delivers events within 5 to 10 minutes. Wait at least 5 minutes before checking.
- Return to the CloudTrail console.
- In the left navigation pane, click **Event history**.
- In the **Lookup attributes** dropdown, select **Event name**.
- In the search field, enter CreateUser and press **Enter**.
- Confirm you see a CreateUser event for test-audit-user in the results.
What should I see?
You should see a row with the Event name CreateUser, your username as the User name, and a timestamp from the last few minutes. This proves CloudTrail is actively recording your account activity.
- Clean up the test user by running:
aws iam delete-user --user-name test-audit-user
- Verify the user was deleted by running:
aws iam get-user --user-name test-audit-user
You should see a NoSuchEntity error, confirming the test user no longer exists.
Don't see your CreateUser event?
CloudTrail events can take up to 15 minutes to appear in Event history. If you do not see the event yet, wait a few more minutes and refresh the page.
Also confirm your trail is set to log management events (Read and Write) and that the trail status shows Logging.
Help me find my event
Your account now has full audit logging in place. Every management API call across all regions is being recorded and stored in your S3 bucket. Next up, you will wire up automated email alerts so you get notified the instant someone performs a sensitive IAM action.
Set Up Automated Security Alerts
In the previous step, you activated CloudTrail to record every API call in your AWS account. That gives you a permanent audit log.
Logs are only useful if someone reviews them. In this step, you will build an automated alerting pipeline that sends you an email the moment someone performs a sensitive IAM action. This uses EventBridge to detect specific CloudTrail events and SNS to deliver notifications to your inbox.
In this step, get ready to:
- Create an SNS topic and subscribe your email to it.
- Create an EventBridge rule that matches sensitive IAM API calls.
- Test the full alert pipeline by triggering a real IAM event.
Create an SNS topic and email subscription
Amazon SNS (Simple Notification Service) is a messaging service that can push notifications to email, SMS, or other endpoints. You need a topic (a communication channel) and a subscription (a recipient) before EventBridge can send alerts.
- Create the SNS topic by running this command in your terminal:
aws sns create-topic --name security-alerts
What does this command do?
This creates a new SNS topic named security-alerts. The output includes a TopicArn value (something like arn:aws:sns:us-east-1:123456789012:security-alerts). You will need this ARN in the next command.
- Copy the TopicArn value from the output. You will use it in the next command.
- Subscribe your email address to the topic by running:
aws sns subscribe --topic-arn [[TOPIC_ARN="your TopicArn from the previous command"]] --protocol email --notification-endpoint [[YOUR_EMAIL="your-email@example.com"]]
What does this command do?
The --protocol email flag tells SNS to deliver messages via email. The --notification-endpoint flag specifies the email address that receives alerts.
The response will show "SubscriptionArn": "pending confirmation" because email subscriptions require you to confirm from your inbox before they become active.
- Open your email inbox and look for a message from AWS Notifications.
- Click the Confirm subscription link in the email.
You should see a confirmation page in your browser that says your subscription is confirmed.
Don't see the confirmation email?
- Check your spam or junk folder. AWS notification emails sometimes get filtered.
- Make sure you typed your email address correctly in the subscribe command. If you made a typo, run the subscribe command again with the correct address.
- Wait up to 2 minutes. The email may take a moment to arrive.
Still stuck? Help me troubleshoot my SNS email subscription.
Create an EventBridge rule for sensitive IAM events
EventBridge watches for events flowing through your AWS account and routes matching events to targets like SNS. You will create a rule that matches specific IAM API calls recorded by CloudTrail and forwards them to your security-alerts topic.
- Open the Amazon EventBridge console at https://console.aws.amazon.com/events/.
- In the left navigation pane, choose Rules.
- Choose Create rule.
- Enter iam-security-alerts as the rule name.
- For Event bus, select default.
- For Rule type, choose Rule with an event pattern.
- Choose Next.
Why the default event bus?
When an AWS service in your account emits an event, it always goes to your account's default event bus. Since CloudTrail sends IAM events to this bus automatically, your rule needs to listen there.
- For Event source, choose AWS services.
- Under Event pattern, for AWS service, select IAM from the dropdown.
- For Event type, select AWS API Call via CloudTrail.
- Choose Specific operation(s).
- Enter CreateUser and press Enter.
- Enter CreateAccessKey and press Enter.
- Enter AttachUserPolicy and press Enter.
- Enter DeleteTrail and press Enter.
- Choose Next.
Why these four operations?
Each operation represents a high-risk action. CreateUser and CreateAccessKey could indicate unauthorized access being created. AttachUserPolicy could be a privilege escalation. DeleteTrail could be an attacker trying to cover their tracks by disabling audit logging.
- For Target types, choose AWS service.
- For Select a target, choose SNS topic from the dropdown.
- For Topic, select security-alerts.
- Choose Next.
- Choose Next again to skip the tags page.
- Review the details and choose Create rule.
You should see a success banner confirming that the rule iam-security-alerts was created.
Rule creation failed?
- Make sure you selected default for the event bus. Custom event buses will not receive CloudTrail events.
- Confirm you chose AWS API Call via CloudTrail as the event type. Other event types will not match IAM API calls.
- Verify your SNS topic security-alerts exists. If the target dropdown is empty, go back and create the topic first.
Still stuck? Help me troubleshoot my EventBridge rule creation.
Test the alert pipeline
The best way to trust your security alerts is to verify they actually fire. You will trigger the rule by performing one of the monitored actions and then confirm the email arrives.
- Trigger an alert by creating a temporary IAM user:
aws iam create-user --user-name alert-test-user
What happens after this command?
CloudTrail records the CreateUser API call. EventBridge detects the event on the default event bus. Because it matches your rule's pattern, EventBridge forwards it to your security-alerts SNS topic. SNS then emails you.
This can take 2-5 minutes. EventBridge processes CloudTrail events in near real-time, but there is a small delay for the event to propagate.
- Wait 2-5 minutes, then check your email inbox for a message from AWS Notifications.
- Verify the email contains event details including who performed the action, the event name (CreateUser), and the timestamp.
- Take a screenshot of the email alert for your portfolio.
No email after 5 minutes?
- Confirm your SNS subscription is active by checking for a subscription confirmation email from earlier. Without confirming, no messages will be delivered.
- Verify your CloudTrail trail is still in Logging status. EventBridge relies on CloudTrail to emit the events.
- Check the EventBridge rule is enabled. In the EventBridge console, select your rule and confirm the status shows Enabled.
- Check your spam folder. SNS notification emails sometimes get filtered.
Still stuck? Help me figure out why my EventBridge alert email isn't arriving.
- Clean up the test user by running:
aws iam delete-user --user-name alert-test-user
This removes the temporary user you just created. Always clean up test resources after verifying.
Your automated security pipeline is live. Any time someone creates a user, generates an access key, attaches a policy, or tries to delete your audit trail, you will know about it within minutes. Next up, you will take on the Secret Mission to extend your detection capabilities even further.
Secret mission
Enable IAM Access Analyzer
You've hardened your account with password policies, least-privilege groups, audit logging, and automated alerts. But how do you know if any of your resources are accidentally public or shared with external accounts right now? In this secret mission, you'll enable IAM Access Analyzer to continuously monitor for unintended external access to your resources.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project's ongoing cost is effectively $0 for a low-volume personal account. The only potential charge is S3 storage for CloudTrail logs, which amounts to pennies per month for a single-user account.
Resources you used:
- S3 bucket: cloudtrail-logs-<account-id>-security-lab (stores CloudTrail log files).
- CloudTrail trail: security-audit-trail (multi-region management event logging).
- SNS topic: security-alerts (email subscription for security notifications).
- EventBridge rule: iam-security-alerts (triggers on sensitive IAM actions).
- IAM Access Analyzer: external-access-analyzer (external access type, free).
- Local files: credential-report-before.csv, credential-report-after.csv, developer-policy.json.
Keep everything running
No action needed. Choose this if you want to keep your security controls active and continue monitoring your account.
- The first copy of CloudTrail management events is free in each region. S3 storage for logs will be minimal for a personal account (pennies per month).
- SNS provides 1,000 free email notifications per month. EventBridge management events are ingested free.
- IAM Access Analyzer external access type is free. The IAM password policy and Developers group are free and actively improve your account security.
Pause - I'll come back to this later
Stop log delivery and disable alerting to reduce any minimal S3 storage accumulation while keeping the configuration intact.
- Stop the CloudTrail trail from delivering new logs by running:
- Disable the EventBridge rule so it stops triggering alerts by running:
aws cloudtrail stop-logging --name security-audit-trail
aws events disable-rule --name iam-security-alerts
When you return, resume logging and re-enable the rule:
aws cloudtrail start-logging --name security-audit-trail
aws events enable-rule --name iam-security-alerts
What stays active while paused?
The IAM password policy, Developers group, SNS topic, and Access Analyzer all remain configured. Only log delivery and event-driven alerting are paused.
Delete - I don't want to use this again
Remove all project resources and start fresh. The IAM password policy and Developers group are free and improve your account security, so the instructions below leave them in place.
- Delete the IAM Access Analyzer by running:
aws accessanalyzer delete-analyzer --analyzer-name external-access-analyzer
- Stop the CloudTrail trail and delete it by running:
aws cloudtrail stop-logging --name security-audit-trail
aws cloudtrail delete-trail --name security-audit-trail
- Remove the EventBridge rule's target, then delete the rule. First, find your target ID by running:
aws events list-targets-by-rule --rule iam-security-alerts
- Copy the Id value from the output, then remove the target and delete the rule by running:
aws events remove-targets --rule iam-security-alerts --ids "your-target-id"
aws events delete-rule --name iam-security-alerts
- Delete the SNS topic (this also deletes all its subscriptions) by running:
aws sns delete-topic --topic-arn your-topic-arn
Find your SNS topic ARN
If you did not save your topic ARN from earlier, retrieve it by running aws sns list-topics and looking for the topic named security-alerts.
- Empty and delete the S3 bucket by running:
aws s3 rm s3://cloudtrail-logs-your-account-id-security-lab --recursive
aws s3 rb s3://cloudtrail-logs-your-account-id-security-lab
- Delete your local project files by running:
rm credential-report-before.csv credential-report-after.csv developer-policy.json
What about the password policy and Developers group?
Both are free and actively improve your account security. The password policy only takes effect on the next password change or user creation. The Developers group with its DeveloperReadOnly policy is ready for when you add team members.
Nice Work!
Nice Work!
You just hardened an AWS account with production-grade security controls. From credential auditing to automated alerting, you now have hands-on proof that you can implement the IAM fundamentals employers expect from cloud security engineers.
You've learned how to:
- Implement a least-privilege IAM group with a custom policy following the Principle of Least Privilege.
- Enable CloudTrail audit logging across all regions for full API visibility.
- Build an automated alerting pipeline using EventBridge and SNS that notifies you of sensitive IAM actions in real time.
- Secret Mission: Deploy IAM Access Analyzer to continuously monitor for unintended public or cross-account access to your resources.
Ready to quiz yourself?