Skip to content

CloudArsenal/aws-account-scanner

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AWS Account Scanner

Full inventory scan of an AWS account — discovers every resource across all regions and services, outputs a Keysight-branded HTML report and multi-sheet XLSX workbook. Reports include a live API call count showing exactly how many AWS API calls were made to produce the scan.

This is the Services-integrated version of the scanner. It lives under AWS/Services/Account/Scanner/ and shares auth + S3 upload infrastructure with the rest of the Services/ family.


Project Structure

AWS/Services/Account/Scanner/
├── account_scanner.py          # Entry point — CLI, orchestration, multi-account loop
├── config.json                 # Auth, scan scope, output settings
├── requirements.txt
└── modules/
    ├── auth.py                 # Session helpers (SSO, IAM, region discovery, org accounts)
    ├── service_scanners.py     # All dedicated service scanners + SCANNERS registry
    ├── ce_discovery.py         # Cost Explorer–driven service/region discovery
    ├── generic_scanner.py      # Fallback scanner for services with no dedicated scanner
    └── report_generator.py     # HTML + XLSX report generation (Keysight branded)

What it does

  • Auto-discovers all enabled regions for the account
  • CE-driven discovery (default): queries Cost Explorer to find only active services and their regions — no wasted API calls on empty services
  • Scans 24+ services in parallel: EC2, EBS, EIP, AMI, RDS, S3, Lambda, EKS, ECS, ELB/ALB/NLB, VPC, IAM, CloudFormation, DynamoDB, SQS, SNS, ElastiCache, CloudWatch, Route53, ACM, Secrets Manager, KMS, CloudTrail, CloudFront
  • Generic scanner fallback: CE-discovered services with no dedicated scanner are still inventoried (ARN + tags)
  • Optional deep scans per service (OS names, S3 content classification, ALB target health, CloudWatch log group sizes)
  • Optional Cost Explorer analysis (last N days, by service + region, plus usage-type breakdown)
  • Optional EOL report — at-risk OS instances only, accounts with no risk are silently skipped
  • Outputs: HTML (searchable, per-service sections, API call count KPI) + XLSX (Summary sheet + one sheet per service)
  • Auto-uploads reports + logs to S3 (YOUR_S3_BUCKET/services/account/scanner/)

Setup

cd AWS/Services/Account/Scanner
pip install -r requirements.txt

Before You Run — Mandatory Pre-flight

Step 1 — check your auth.mode in config.json

"auth": {
  "mode": "sso"   ← this controls everything
}
auth.mode Pre-flight required
sso Must run aws sso login --sso-session YOUR_SSO_SESSION before every session
iam_mfa_switch_role No pre-flight — script prompts for MFA token at runtime
iam_direct No pre-flight — credentials are in config.json

Step 2 — if using SSO, login first (mandatory, session expires)

aws sso login --sso-session YOUR_SSO_SESSION

Step 3 — then run the script

python account_scanner.py

Authentication

Set auth.mode in config.json. Three modes are supported:

1. sso — AWS SSO (single account, specific list, or full org)

Best for: teams using AWS SSO. Supports scanning one account, a list, or every account in the org.

"auth": {
  "mode": "sso",
  "sso": {
    "profile": "YOUR_SSO_PROFILE",
    "session_name": "AccountScanSession"
  }
},
"organization": {
  "management_account_id": "YOUR_MANAGEMENT_ACCOUNT_ID",
  "role_name": "YOUR_AUTOMATION_ROLE_NAME",
  "external_id": "YOUR_EXTERNAL_ID",
  "mode": "specific",
  "accounts": ["YOUR_ACCOUNT_ID"],
  "exclude_accounts": ["YOUR_MANAGEMENT_ACCOUNT_ID"]
}

Prerequisites:

aws sso login --sso-session YOUR_SSO_SESSION

Flow: YOUR_SSO_PROFILE SSO hub → sts:AssumeRole (YOUR_AUTOMATION_ROLE_NAME) per target account

Multi-account options (SSO only):

# Scan all accounts in the org
python account_scanner.py --all-accounts

# Scan specific accounts (overrides config)
python account_scanner.py --accounts 123456789012 987654321098

# Scan accounts listed in config.json organization.accounts (default)
python account_scanner.py

Each account gets its own report file. Rows include Account ID and Account Name columns when scanning multiple accounts.


2. iam_direct — IAM access key + secret (no MFA)

Best for: service accounts or roles that don't require MFA.

"auth": {
  "mode": "iam_direct",
  "iam_direct": {
    "access_key_id": "YOUR_ACCESS_KEY_ID",
    "secret_access_key": "YOUR_SECRET_ACCESS_KEY",
    "region": "us-east-1"
  }
}

3. iam_mfa_switch_role — IAM key + MFA + assume role into target account

Best for: human IAM users with MFA enabled who need to switch into another account.

"auth": {
  "mode": "iam_mfa_switch_role",
  "iam_mfa_switch_role": {
    "access_key_id": "YOUR_ACCESS_KEY_ID",
    "secret_access_key": "YOUR_SECRET_ACCESS_KEY",
    "mfa_serial": "arn:aws:iam::SOURCE_ACCOUNT_ID:mfa/MFA_DEVICE_NAME",
    "mfa_token_duration": 3600,
    "target_role_arn": "arn:aws:iam::TARGET_ACCOUNT_ID:role/ROLE_NAME",
    "session_name": "AccountScanSession"
  }
}

At runtime the script prompts:

Enter MFA token for [arn:aws:iam::YOUR_ACCOUNT_ID:mfa/YOUR_MFA_DEVICE]: 123456

Flow: IAM keys → sts:GetSessionToken (with MFA) → sts:AssumeRole (into target account)


Running — All Arguments

(no args) — Full scan, CE-driven discovery

python account_scanner.py

Queries Cost Explorer first to find active services and their regions, then scans only those. Output: account_scan_<id>_full_<ts>.html/xlsx


--services — Limit to specific services

python account_scanner.py --services ec2 rds s3
python account_scanner.py --services lambda dynamodb

Valid values: ec2 ebs eip ami rds s3 lambda eks ecs elb vpc iam cloudformation dynamodb sqs sns elasticache cloudwatch route53 acm secretsmanager kms cloudtrail cloudfront


--regions — Limit to specific regions

python account_scanner.py --regions us-east-1 us-east-2 us-west-2
python account_scanner.py --services ec2 rds --regions us-east-1 eu-west-1

--deep — Richer detail for specific services

python account_scanner.py --deep s3          # content classification, size, encryption
python account_scanner.py --deep ec2         # resolved OS name, EOL columns, SG rules
python account_scanner.py --deep alb         # listeners, target health, WAF, certs
python account_scanner.py --deep cloudwatch  # log groups, stored size, retention, last ingestion
python account_scanner.py --deep all         # all four deep scans
python account_scanner.py --services ec2 rds --deep s3   # standard ec2+rds + deep s3

What each deep scan adds:

--deep Extra columns vs standard scan
ec2 OS name (resolved from AMI), OS Family, EOL Status, EOL Date, Days Until EOL, security group rules, key pair, IAM profile, CPU detail, attached EBS
s3 Content Classification, Top Prefixes (sampled), Last Object Written, Last Written Age, Last Object Key, Est. Total Size, Est. Objects, Encryption, Lifecycle Rules, Replication, Website Hosting, Access Logging
alb Listeners (port/protocol/SSL policy), Target Groups (healthy/unhealthy/unused counts), WAF WebACL, Access Logs bucket, Deletion Protection, Idle Timeout, Certificates
cloudwatch Stored Size, Retention Policy, Last Ingestion, Last Ingestion Age

--eol — End-of-Life OS report

Finds EC2 instances running outdated or soon-expiring operating systems. Uses a built-in database of OS versions (no external API calls beyond EC2). Accounts with zero at-risk instances are silently skipped.

python account_scanner.py --eol                              # all OS families
python account_scanner.py --eol --eol-os windows             # Windows only
python account_scanner.py --eol --eol-os windows ubuntu rhel
python account_scanner.py --eol --all-accounts               # entire org
python account_scanner.py --deep ec2 --eol --accounts YOUR_ACCOUNT_ID

Risk levels:

Risk Meaning
CRITICAL Past EOL — no extended support available
HIGH Past EOL — extended support exists (costs extra)
MEDIUM Expiring within 90 days
LOW Expiring within 1 year

--eol-os families: windows ubuntu amazon rhel / redhat centos debian suse


--cost — Cost Explorer analysis

Generates two sections: service-level totals (by service + region) and usage-type breakdown (e.g. USE2-TimedStorage-ByteHrs = CloudWatch logs storage in us-east-2).

python account_scanner.py --cost                  # last 30 days
python account_scanner.py --cost --cost-days 90   # last 90 days
python account_scanner.py --deep cloudwatch --cost

--all-accounts / --accounts — Multi-account (SSO only)

aws sso login --sso-session YOUR_SSO_SESSION

python account_scanner.py --all-accounts
python account_scanner.py --accounts YOUR_ACCOUNT_ID YOUR_ACCOUNT_ID
python account_scanner.py --eol --eol-os windows --all-accounts

Each account produces its own report. Account ID and Account Name columns are added to every row.


--tag-filter KEY=VALUE ... — Filter by resource tags

Only include resources that match all specified tags. Works for: EC2, EBS, EIP, AMI, VPC/Subnet/IGW/NAT, Lambda, EKS.

python account_scanner.py --tag-filter Environment=prod
python account_scanner.py --tag-filter Owner=network-team
python account_scanner.py --tag-filter Environment=prod Owner=infra-team   # AND
python account_scanner.py --tag-filter CostCenter                          # key-only match
python account_scanner.py --deep ec2 --tag-filter Environment=prod --all-accounts

Output filename gets a _tagged suffix.

Note: Services without tag data in scan results (RDS, ECS, CloudFormation) return no rows when --tag-filter is active. Use --services to limit to tag-capable services.


Other flags

python account_scanner.py --no-html                    # XLSX only
python account_scanner.py --no-xlsx                    # HTML only
python account_scanner.py --output-dir /tmp/reports    # change output directory
python account_scanner.py --config prod_config.json    # alternate config file

Common combinations

# Full inventory + cost — standard weekly run
python account_scanner.py --cost

# Investigate a surprise CloudWatch bill
python account_scanner.py --deep cloudwatch --cost --regions us-east-2

# S3 audit — size, content, last write
python account_scanner.py --deep s3

# Org-wide EOL sweep — Windows + RHEL (most common risk)
python account_scanner.py --eol --eol-os windows rhel --all-accounts

# Full deep scan + cost for a specific account
python account_scanner.py --deep all --cost --accounts 111122223333

# Quick targeted check
python account_scanner.py --services ec2 rds --regions us-east-1 eu-west-1

# Deep EC2 scoped to a team's instances
python account_scanner.py --deep ec2 --tag-filter Owner=network-team --all-accounts

Output

Reports are saved to output/ with descriptive names:

Command Output filename
(full scan) account_scan_123456789012_full_20260312_143022.html
--deep s3 account_scan_123456789012_s3-deep_20260312_143022.html
--deep all account_scan_123456789012_ec2-deep_s3-deep_alb-deep_cloudwatch-deep_20260312.html
--cost account_scan_123456789012_cost_20260312_143022.html
--eol account_scan_123456789012_eol_20260312_143022.html
--services ec2 rds account_scan_123456789012_ec2_rds_20260312_143022.html

Both .html and .xlsx are generated with the same base name. A .log file is also written alongside. Reports and logs are automatically uploaded to S3.


API Call Summary

Every report includes a live API call count — the total number of AWS API calls made during the scan. This appears as:

  • A KPI tile in the HTML report header
  • A API Calls Made row in the XLSX Summary sheet
  • A line in the console summary table

Typical call counts for a full scan across 20 regions with 24 services:

  • ~200–500 calls (CE-driven, sparse account)
  • ~1,000–3,000 calls (full explicit scan, dense account)
  • ~5,000–10,000 calls (--deep all across a large account)

The count covers every AWS SDK call: describe_*, list_*, get_*, paginator pages, and auth calls (STS, IAM).


CE-Driven Discovery

By default (no --services flag), the scanner queries Cost Explorer once to find which services have been billed in the last 30 days, then scans only those services in their active regions. This avoids making hundreds of describe_* calls to services that aren't in use.

  • If CE is unavailable (no ce:GetCostAndUsage permission), falls back to scanning all known services
  • CE billing names without a dedicated scanner use the generic scanner (ARN + tags via botocore)
  • IAM is always included regardless of CE (it's free-tier, never billed)

Notes

  • config.json and output/ are gitignored — credentials never leave your machine
  • S3 size/object counts come from CloudWatch metrics (1–2 day lag; new buckets show n/a)
  • Cost Explorer requires ce:GetCostAndUsage permission on the target role
  • IAM scan (users, roles, groups) is global and runs once regardless of region count
  • Reports upload to s3://YOUR_S3_BUCKET/services/account/scanner/{logs|reports}/YYYY/MM/DD/

About

AWS Account Scanner — full inventory scan across all regions and services

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages