SecurityCost ManagementTutorial

Weekend Project: Audit Your AWS Account in 30 Minutes

Viktor B.

Co-founder & CEO · January 2, 2026 · 8 min read

You set up your AWS account months ago. You've been shipping features, not checking security configurations. Somewhere in the back of your mind, you know there are probably forgotten resources running, IAM users without MFA, and security groups open to the world. But who has time to audit an AWS account?

You do. This audit takes 30 minutes and uses only the AWS CLI. Grab a coffee, open your terminal, and work through it step by step. By the end, you'll know exactly where your account stands — and what to fix first.

Before You Start

Make sure you have the AWS CLI installed and configured with credentials that have read access to IAM, EC2, S3, RDS, and billing. If you're using SSO, run aws sso login first.

Part 1: IAM Security (10 minutes)

Check 1: Root Account MFA

aws iam get-account-summary --query 'SummaryMap.AccountMFAEnabled'

If this returns 0, stop everything and enable MFA on your root account right now. This is the single most critical security control for your AWS account.

Check 2: IAM Users Without MFA

aws iam generate-credential-report
sleep 5
aws iam get-credential-report --query 'Content' --output text | base64 -d | \
  awk -F',' 'NR>1 && $4=="true" && $8=="false" {print "USER WITHOUT MFA: " $1}'

This finds IAM users who have console access but no MFA enabled. Every user on this list is a credential compromise waiting to happen.

Check 3: Old Access Keys

aws iam get-credential-report --query 'Content' --output text | base64 -d | \
  awk -F',' 'NR>1 && $9=="true" {print $1, "Key1 last rotated:", $10}
             NR>1 && $14=="true" {print $1, "Key2 last rotated:", $15}'

Any access key older than 90 days should be rotated. Any key older than 180 days should be investigated — there's a good chance nobody remembers what it's for.

Check 4: Users with AdministratorAccess

aws iam list-users --query 'Users[*].UserName' --output text | \
  tr '\t' '\n' | while read user; do
    aws iam list-attached-user-policies --user-name "$user" \
      --query "AttachedPolicies[?PolicyArn=='arn:aws:iam::aws:policy/AdministratorAccess'].PolicyName" \
      --output text | grep -q "AdministratorAccess" && echo "ADMIN USER: $user"
  done

Fewer admin users is better. Every admin user is a potential full-account compromise.

Part 2: Resource Audit (10 minutes)

Check 5: Running EC2 Instances

aws ec2 describe-instances --filters "Name=instance-state-name,Values=running" \
  --query 'Reservations[*].Instances[*].[InstanceId,InstanceType,LaunchTime,Tags[?Key==`Name`].Value|[0]]' \
  --output table

Review each running instance. Do you recognize all of them? Is there anything running that was supposed to be temporary? Pay attention to launch dates — anything launched more than 3 months ago that isn't tagged as production deserves scrutiny.

Check 6: Unattached EBS Volumes

aws ec2 describe-volumes --filters "Name=status,Values=available" \
  --query 'Volumes[*].[VolumeId,Size,CreateTime]' --output table

Unattached EBS volumes cost money for storage but do nothing useful. These are almost always leftovers from terminated instances. Delete them after confirming you don't need the data (or snapshot them first if you're not sure).

Check 7: Idle RDS Instances

aws rds describe-db-instances \
  --query 'DBInstances[*].[DBInstanceIdentifier,DBInstanceClass,DBInstanceStatus,Engine]' \
  --output table

Check each RDS instance. Is it in use? Is it the right size? An idle db.r5.xlarge costs over $400/month. If it's a development database that nobody's using, consider stopping or deleting it.

Check 8: Public S3 Buckets

for bucket in $(aws s3api list-buckets --query 'Buckets[*].Name' --output text); do
  acl=$(aws s3api get-bucket-acl --bucket "$bucket" --query 'Grants[?Permission==`READ` && Grantee.URI==`http://acs.amazonaws.com/groups/global/AllUsers`]' --output text 2>/dev/null)
  [ -n "$acl" ] && echo "PUBLIC BUCKET: $bucket"
done

Any bucket that appears here is accessible to anyone on the internet. Unless this is intentional (a static website bucket, for example), fix it immediately.

Part 3: Cost Review (10 minutes)

Check 9: Top Services by Cost (Last 30 Days)

aws ce get-cost-and-usage \
  --time-period Start=$(date -v-30d +%Y-%m-%d),End=$(date +%Y-%m-%d) \
  --granularity MONTHLY --metrics UnblendedCost \
  --group-by Type=DIMENSION,Key=SERVICE \
  --query 'ResultsByTime[0].Groups[*].[Keys[0],Metrics.UnblendedCost.Amount]' \
  --output table

Review the top five services by cost. Are there any surprises? A service you don't recognize? Data transfer costs that seem high? This is your cost map — it tells you where your money goes.

Check 10: Elastic IPs Not Attached

aws ec2 describe-addresses --query 'Addresses[?AssociationId==null].[PublicIp,AllocationId]' --output table

AWS charges $0.005/hour ($3.60/month) for Elastic IPs that aren't attached to a running instance. It's small, but it's also the easiest cost to eliminate — release any EIP you're not using.

Your Audit Score

Count how many checks passed without issues. 10/10: your account is in excellent shape. 7-9: you have some cleanup to do but nothing critical. 4-6: block an hour this week to fix the issues you found. Below 4: your account has significant risk — prioritize the IAM findings first.

Making This Automatic

This audit is useful, but it only captures a point-in-time snapshot. By next month, new resources will be running, new IAM users will exist, and configurations will have drifted. Running this manually every month is better than nothing, but it's a commitment that's easy to drop.

Vigilare runs these checks — and hundreds more — automatically, every five minutes. Instead of a monthly CLI session, you get continuous monitoring with a risk score that tells you the moment something needs attention. Start a free 14-day trial.

Related Reading

Protect your AWS accounts before it's too late

Vigilare monitors your AWS accounts for suspension risks — billing anomalies, IAM issues, GuardDuty findings, and more — and alerts you before AWS takes action.

Written by Viktor B.

Co-founder & CEO