CloudMapper: Mapping AWS Resources and Finding Security Gaps
Auditing an AWS account manually is time-consuming — navigating the console, cross-referencing regions, checking which resources talk to which. CloudMapper automates this: it collects all resource data via the AWS API, renders an interactive relationship map, and generates a security report highlighting exposed services, unused resources, and policy issues.
The Audit Workflow
%%{ init: { 'look': 'handDrawn' } }%%
graph TD
A[AWS Account] -->|1. Collect<br/>AWS API calls| B[CloudMapper<br/>JSON data]
B -->|2. Prepare<br/>enumeration data| C[CloudMapper<br/>enumeration files]
B -->|3. Generate<br/>security report| D[HTML report<br/>vulnerabilities]
B -->|4. Start web<br/>server| E[Interactive<br/>resource map]
D --> F[S3 misconfigs<br/>Public buckets]
D --> G[IAM issues<br/>Overpermissive roles]
D --> H[Unused resources<br/>Idle instances]
D --> I[Security groups<br/>Open ports]
E --> J[S3 bucket<br/>relationships]
E --> K[Cross-account<br/>access paths]
E --> L[VPC peering<br/>connections]
CloudMapper reads from AWS APIs but doesn’t modify anything — it’s purely read-only reconnaissance.
Setup
# Clone the repo
git clone https://github.com/duo-labs/cloudmapper.git
cd cloudmapper
# Build the Docker image (avoids Python dependency issues)
docker build -t cloudmapper .
# Configure AWS credentials
export AWS_ACCESS_KEY_ID=AKIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_DEFAULT_REGION=eu-central-1
The Audit Process
# 1. Add your account
docker run --rm -it \
-v $(pwd):/data \
-e AWS_ACCESS_KEY_ID \
-e AWS_SECRET_ACCESS_KEY \
-e AWS_DEFAULT_REGION \
cloudmapper \
python cloudmapper.py configure add-account \
--account my-account \
--id 123456789012
# 2. Collect all resources
docker run --rm -it \
-v $(pwd):/data \
-e AWS_ACCESS_KEY_ID \
-e AWS_SECRET_ACCESS_KEY \
-e AWS_DEFAULT_REGION \
cloudmapper \
python cloudmapper.py collect \
--account my-account
# 3. Generate the security report
docker run --rm -it \
-v $(pwd):/data \
cloudmapper \
python cloudmapper.py report \
--account my-account
# 4. Start the web UI
docker run --rm -it \
-v $(pwd):/data \
-p 8000:8000 \
cloudmapper \
python cloudmapper.py webserver \
--account my-account
What the Report Covers
%%{ init: { 'look': 'handDrawn' } }%%
graph TD
A[CloudMapper Report] --> B[Public S3 Buckets<br/>ACL + policy analysis]
A --> C[IAM Role Issues<br/>Wildcard actions, no conditions]
A --> D[Security Groups<br/>0.0.0.0/0 on sensitive ports]
A --> E[Unencrypted S3<br/>No KMS, no SSE]
A --> F[Unused Resources<br/>Idle EBS, unused EIPs]
A --> G[Cross-Account Access<br/>ExternalPrincipals in IAM]
A --> H[Overpermissive Policies<br/>Actions on * resources]
B --> I[Bucket policies allowing<br/>public access]
C --> J[arn:aws:iam::*:root<br/>No MFA, no conditions]
D --> K[Port 22 from 0.0.0.0/0<br/>RDP from 0.0.0.0/0]
E --> L[S3 buckets without<br/>encryption enabled]
The report is a static HTML file at account-data/report.html — open it in a browser to see the findings.
The Resource Map
%%{ init: { 'look': 'handDrawn' } }%%
graph LR
A[S3 Bucket<br/>prod-data] -->|via| B[IAM Role<br/>lambda-processor]
B -->|executes| C[Lambda<br/>data-processor]
C -->|writes to| D[RDS PostgreSQL<br/>prod-db]
A -->|public access<br/>via policy| E[Internet<br/>downloads]
F[EC2 Instance<br/>bastion] -->|SSH| G[Private Subnet<br/>app servers]
G -->|SQL| D
The web UI visualizes how resources connect. You can see which Lambda functions access which S3 buckets. You can see which EC2 instances can reach which RDS clusters. You can see which IAM roles have cross-account access.
This is useful for identifying unintended blast radius.
Running It Daily with Slack Notifications
#!/bin/bash
# daily-audit.sh
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_DEFAULT_REGION=eu-central-1
docker run --rm -v $(pwd):/data \
-e AWS_ACCESS_KEY_ID \
-e AWS_SECRET_ACCESS_KEY \
-e AWS_DEFAULT_REGION \
cloudmapper \
python cloudmapper.py collect --account my-account
docker run --rm -v $(pwd):/data \
cloudmapper \
python cloudmapper.py report --account my-account
# Check for critical findings
CRITICAL=$(grep -c "Critical" account-data/report.html || echo 0)
if [ "$CRITICAL" -gt 0 ]; then
curl -X POST https://hooks.slack.com/services/... \
-d "{\"text\": \"CloudMapper found $CRITICAL critical issues in AWS account\"}"
fi
Run this daily via cron or a GitHub Actions workflow. The collect step is incremental — it only re-fetches changed resources.
Quick Verification
# Verify credentials are working
aws sts get-caller-identity
# Should return your account ID, user/role ARN
# Then verify CloudMapper can see your account
docker run --rm -it \
-v $(pwd):/data \
-e AWS_ACCESS_KEY_ID \
-e AWS_SECRET_ACCESS_KEY \
-e AWS_DEFAULT_REGION \
cloudmapper \
python cloudmapper.py configure listAccounts
For teams with multiple AWS accounts, CloudMapper can run against each one and generate a unified view — useful for security audits across production, staging, and development environments.