{"id":51688741,"url":"https://github.com/TocConsulting/awsmap","last_synced_at":"2026-08-04T05:00:35.125Z","repository":{"id":335418232,"uuid":"1145545667","full_name":"TocConsulting/awsmap","owner":"TocConsulting","description":"A fast, comprehensive tool for mapping and inventorying AWS resources across 150+ services and all regions.","archived":false,"fork":false,"pushed_at":"2026-06-12T20:31:55.000Z","size":2589,"stargazers_count":93,"open_issues_count":0,"forks_count":12,"subscribers_count":5,"default_branch":"main","last_synced_at":"2026-06-12T22:20:21.281Z","etag":null,"topics":["asset-inventory","asset-management","aws","aws-security","cli","cloud","cloud-security","cmdb","cspm","devops","drift-detection","inventory","multi-account","multi-region","python","reconnaissance","resources","security"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/awsmap/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TocConsulting.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-29T23:03:33.000Z","updated_at":"2026-06-12T20:31:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/TocConsulting/awsmap","commit_stats":null,"previous_names":["tocconsulting/awsmap"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/TocConsulting/awsmap","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TocConsulting%2Fawsmap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TocConsulting%2Fawsmap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TocConsulting%2Fawsmap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TocConsulting%2Fawsmap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TocConsulting","download_url":"https://codeload.github.com/TocConsulting/awsmap/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TocConsulting%2Fawsmap/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":36261000,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"online","status_checked_at":"2026-08-04T02:00:06.901Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asset-inventory","asset-management","aws","aws-security","cli","cloud","cloud-security","cmdb","cspm","devops","drift-detection","inventory","multi-account","multi-region","python","reconnaissance","resources","security"],"created_at":"2026-07-16T00:00:31.283Z","updated_at":"2026-08-04T05:00:35.108Z","avatar_url":"https://github.com/TocConsulting.png","language":"Python","funding_links":[],"categories":["Open Source Repos"],"sub_categories":["Miscellaneous Repos"],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/logo.png\" alt=\"awsmap\" width=\"160\" style=\"height: auto;\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://pypi.org/project/awsmap/\"\u003e\u003cimg src=\"https://img.shields.io/pypi/v/awsmap.svg\" alt=\"PyPI version\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://pepy.tech/project/awsmap\"\u003e\u003cimg src=\"https://static.pepy.tech/badge/awsmap\" alt=\"Downloads\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://hub.docker.com/r/tarekcheikh/awsmap\"\u003e\u003cimg src=\"https://img.shields.io/docker/v/tarekcheikh/awsmap?label=docker\" alt=\"Docker\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://hub.docker.com/r/tarekcheikh/awsmap\"\u003e\u003cimg src=\"https://img.shields.io/docker/pulls/tarekcheikh/awsmap\" alt=\"Docker Pulls\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://opensource.org/licenses/MIT\"\u003e\u003cimg src=\"https://img.shields.io/badge/License-MIT-brightgreen.svg\" alt=\"License: MIT\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.python.org/downloads/\"\u003e\u003cimg src=\"https://img.shields.io/badge/python-3.9+-blue.svg\" alt=\"Python 3.9+\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://aws.amazon.com/\"\u003e\u003cimg src=\"https://img.shields.io/badge/AWS-150%2B_Services-orange.svg\" alt=\"AWS Services\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n# awsmap\n\nA fast, comprehensive tool for mapping and inventorying AWS resources across 150+ services and all regions.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/demo.gif\" alt=\"awsmap demo: scan, SQL query, security queries, and natural-language ask\" width=\"100%\"\u003e\n\u003c/p\u003e\n\n## Features\n\n- **150+ AWS Services**: Covers compute, storage, database, networking, security, and more\n- **Multi-Region**: Parallel scanning across all enabled regions\n- **Local Database**: Every scan auto-stored in SQLite - query your inventory offline\n- **SQL Query Engine**: Run SQL against your inventory history (`awsmap query \"SELECT ...\"`)\n- **Pre-Built Query Library**: 30 ready-to-use security and compliance queries (`awsmap query -n admin-users`)\n- **Natural Language Queries**: Ask questions in plain English - zero dependencies, works out of the box (`awsmap ask show me all EC2 without Owner tag`)\n- **Examples Library**: 1381 ready-to-run questions organized by service (`awsmap examples lambda`)\n- **Multi-Account**: Scan multiple accounts, query across all of them\n- **Tag Filtering**: Filter by tags - multiple values for same tag match ANY (Owner=John OR Jane), different tags match ALL (Owner=John AND Environment=Production)\n- **Beautiful HTML Reports**: Interactive reports with search, filters, dark mode, and export\n- **Multiple Outputs**: JSON, CSV, and HTML formats\n- **Fast**: Parallel execution with 40 workers (~2 minutes for typical accounts)\n- **Drift Detection**: Compare snapshots over time - detect added, removed, and modified resources (`awsmap diff`)\n- **Waste Detection**: Find idle or wasteful resources from collected data, no extra API calls (`awsmap waste`)\n- **Tag Compliance**: Audit tagging coverage and score against required tags (`awsmap tags`)\n- **Scan-Scoped Queries**: Query any point in your scan history, not just the current state (`awsmap query --scan`, `awsmap ask --scan`)\n- **Console Login Support**: Works with `aws login` credential provider\n\n## Installation\n\n### PyPI\n\n```bash\npip install awsmap\n```\n\n**Requirements:** Python 3.9+, AWS credentials configured\n\n### Docker\n\n```bash\ndocker pull tarekcheikh/awsmap\n```\n\nOr build locally:\n\n```bash\ngit clone https://github.com/TocConsulting/awsmap.git\ncd awsmap\ndocker build -t awsmap .\n```\n\n### Development Installation\n\n```bash\ngit clone https://github.com/TocConsulting/awsmap.git\ncd awsmap\npip install -e .\n```\n\n## Docker Usage\n\n```bash\n# Using AWS credentials file\ndocker run --rm \\\n  -v ~/.aws:/root/.aws:ro \\\n  -v $(pwd)/output:/app/output \\\n  -v ~/.awsmap:/root/.awsmap \\\n  awsmap -p myprofile -o /app/output/inventory.html\n\n# Using environment variables\ndocker run --rm \\\n  -e AWS_ACCESS_KEY_ID \\\n  -e AWS_SECRET_ACCESS_KEY \\\n  -e AWS_DEFAULT_REGION=us-east-1 \\\n  -v $(pwd)/output:/app/output \\\n  -v ~/.awsmap:/root/.awsmap \\\n  awsmap -o /app/output/inventory.html\n\n# Query stored inventory\ndocker run --rm \\\n  -v ~/.awsmap:/root/.awsmap \\\n  awsmap query \"SELECT service, COUNT(*) as count FROM resources GROUP BY service ORDER BY count DESC\"\n\n# List available services\ndocker run --rm awsmap --list-services\n```\n\n## Usage\n\n```bash\n# Full account inventory (all services, all regions, HTML output)\nawsmap -p myprofile\n\n# Specific services (comma-separated or multiple -s flags)\nawsmap -p myprofile -s ec2,s3,rds,lambda,iam\n\n# Specific regions\nawsmap -p myprofile -r us-east-1,eu-west-1\n\n# Filter by tags (OR logic for same key)\nawsmap -p myprofile -t Owner=John -t Owner=Jane -t Environment=Production\n\n# JSON output\nawsmap -p myprofile -f json -o inventory.json\n\n# List available collectors\nawsmap --list-services\n\n# Show timing per service (useful for debugging)\nawsmap -p myprofile --timings\n\n# Exclude default AWS resources (default VPCs, security groups, etc.)\nawsmap -p myprofile --exclude-defaults\n\n# Skip local database storage\nawsmap -p myprofile --no-db\n```\n\n## Multi-Account\n\nScan multiple AWS accounts. Each scan is stored in the same local database - query across all of them.\n\n```bash\n# Scan different accounts (different profiles)\nawsmap -p production\nawsmap -p staging\nawsmap -p dev-account\n\n# Query across all accounts\nawsmap query -n resources-by-account\nawsmap ask how many resources per account\n\n# Scope to one account\nawsmap query -n admin-users -a production\nawsmap ask -a staging show me all Lambda functions\n```\n\n## Query Your Inventory\n\nEvery scan is automatically stored in a local SQLite database (`~/.awsmap/inventory.db`). Query it offline with raw SQL or natural language.\n\n### SQL Queries\n\n```bash\n# Count resources per service\nawsmap query \"SELECT service, COUNT(*) as count FROM resources GROUP BY service ORDER BY count DESC\"\n\n# Find all EC2 instances in a specific region\nawsmap query \"SELECT id, name, region FROM resources WHERE service='ec2' AND type='instance'\"\n\n# View scan history\nawsmap query \"SELECT * FROM scans ORDER BY timestamp DESC\"\n\n# JSON or CSV output\nawsmap query \"SELECT * FROM resources WHERE service='s3'\" -f json\nawsmap query \"SELECT service, id, name FROM resources\" -f csv\n\n# Query tags (filter to resources that have the tag)\nawsmap query \"SELECT id, name, json_extract(tags, '$.Owner') as owner FROM resources WHERE service='ec2' AND json_extract(tags, '$.Owner') IS NOT NULL\"\n```\n\n**More SQL examples:** See `examples/queries/*.sql` for ready-to-use query templates you can customize.\n\n### Pre-Built Query Library\n\nawsmap ships with 30 pre-built queries for common security, compliance, and operational tasks. No SQL knowledge required.\n\n```bash\n# List all available queries\nawsmap query --list\n\n# Run a named query\nawsmap query -n admin-users\nawsmap query -n users-without-mfa\nawsmap query -n open-security-groups\nawsmap query -n untagged-resources\n\n# Pass parameters (find resources with Owner tag)\nawsmap query -n resources-by-tag -P tag=Owner\n\n# Multiple parameters (find EC2 missing Environment tag)\nawsmap query -n missing-tag -P tag=Environment -P service=ec2\n\n# Scope to a specific account\nawsmap query -n admin-users -a production\n\n# Show query SQL without running it\nawsmap query --show admin-users\n\n# Run SQL from a file\nawsmap query -F my-query.sql\n```\n\n**Parameter format:** Use `-P parameter=value` where `parameter` is the query parameter name (e.g., `tag`, `service`) and `value` is what you're searching for. Example: `-P tag=Owner` means \"filter by the Owner tag\" (NOT `-P Owner=SomeValue`).\n\n**Available queries:**\n\n| Query | Description | Example |\n|-------|-------------|---------|\n| **IAM / Security** | | |\n| `admin-users` | IAM users with admin permissions (direct + via group) | `awsmap query -n admin-users` |\n| `admin-roles` | IAM roles with admin permissions | `awsmap query -n admin-roles` |\n| `users-without-mfa` | IAM users without MFA enabled | `awsmap query -n users-without-mfa` |\n| `iam-inactive-users` | IAM users with no login and no access keys | `awsmap query -n iam-inactive-users` |\n| `old-access-keys` | IAM users with access keys | `awsmap query -n old-access-keys` |\n| `cross-account-roles` | IAM roles with trust policies allowing external accounts | `awsmap query -n cross-account-roles` |\n| `open-security-groups` | Security groups with 0.0.0.0/0 ingress rules | `awsmap query -n open-security-groups` |\n| `secrets-no-rotation` | Secrets Manager secrets without auto-rotation | `awsmap query -n secrets-no-rotation` |\n| **S3** | | |\n| `public-s3-buckets` | S3 buckets with public access enabled | `awsmap query -n public-s3-buckets` |\n| `encryption-status` | S3 buckets and their encryption configuration | `awsmap query -n encryption-status` |\n| `s3-no-versioning` | S3 buckets without versioning | `awsmap query -n s3-no-versioning` |\n| `s3-no-logging` | S3 buckets without access logging | `awsmap query -n s3-no-logging` |\n| **EC2 / EBS** | | |\n| `stopped-instances` | EC2 instances in stopped state | `awsmap query -n stopped-instances` |\n| `unused-volumes` | EBS volumes not attached to any instance | `awsmap query -n unused-volumes` |\n| `ebs-unencrypted` | EBS volumes without encryption | `awsmap query -n ebs-unencrypted` |\n| `unused-eips` | Elastic IPs not associated with any instance | `awsmap query -n unused-eips` |\n| `default-vpcs` | Default VPCs across all regions | `awsmap query -n default-vpcs` |\n| **RDS** | | |\n| `rds-public` | RDS instances with public access enabled | `awsmap query -n rds-public` |\n| `rds-unencrypted` | RDS instances without encryption | `awsmap query -n rds-unencrypted` |\n| `rds-no-multi-az` | RDS instances without Multi-AZ | `awsmap query -n rds-no-multi-az` |\n| `rds-engines` | RDS instances grouped by engine | `awsmap query -n rds-engines` |\n| **Lambda** | | |\n| `lambda-runtimes` | Lambda functions grouped by runtime | `awsmap query -n lambda-runtimes` |\n| `lambda-high-memory` | Lambda functions with memory \u003e 512 MB | `awsmap query -n lambda-high-memory` |\n| **Tags** | | |\n| `untagged-resources` | Resources with no tags | `awsmap query -n untagged-resources` |\n| `missing-tag` | Resources missing a specific tag | `awsmap query -n missing-tag -P tag=Owner` |\n| `resources-by-tag` | Resources that have a specific tag | `awsmap query -n resources-by-tag -P tag=Owner` |\n| **Inventory** | | |\n| `resources-by-service` | Resource count per service | `awsmap query -n resources-by-service` |\n| `resources-by-region` | Resource count per region | `awsmap query -n resources-by-region` |\n| `resources-by-account` | Resource count per account | `awsmap query -n resources-by-account` |\n| `resources-per-account-service` | Resource count per account per service | `awsmap query -n resources-per-account-service` |\n\nYou can also add your own queries by placing `.sql` files in `~/.awsmap/queries/`. Use the same header format as the built-in queries (`-- name:`, `-- description:`, `-- params:`).\n\n### Natural Language Queries\n\nAsk questions about your inventory in plain English using `awsmap ask`. **No setup required** - works out of the box with a built-in zero-dependency parser.\n\n```bash\nawsmap ask how many resources per region\nawsmap ask show me all EC2 instances without Owner tag\nawsmap ask which S3 buckets are in eu-west-1\nawsmap ask what services have the most resources\n```\n\nawsmap translates your question to SQL using a **built-in parser** (zero dependencies), shows you the generated query, and displays the results.\n\n### Examples Library\n\nBrowse and run 1381 pre-built questions organized by AWS service using `awsmap examples`.\n\n```bash\n# List all services with question counts\nawsmap examples\n\n# Browse questions for a service\nawsmap examples lambda\n\n# Run a specific question by number\nawsmap examples lambda 5\n\n# Search across all questions\nawsmap examples --search \"public\"\nawsmap examples --search \"encryption\"\n```\n\n#### Multi-Account Queries\n\nWhen multiple accounts have been scanned, `awsmap ask` queries all of them by default. Use `-a` to scope to a single account:\n\n```bash\n# Query across all accounts\nawsmap ask show me all IAM users\n\n# Scope to one account\nawsmap ask -a production show me Lambda functions\n```\n\n## Drift Detection\n\nCompare snapshots of your AWS inventory over time to detect what changed - resources added, removed, or modified.\n\n```bash\n# What did the most recent scan change? (no arguments: previous scan vs current)\nawsmap diff\n\n# What changed in the last 7 days?\nawsmap diff --from 7d\n\n# Compare two specific dates\nawsmap diff --from 2026-01-15 --to 2026-02-09\n\n# Scope to specific services\nawsmap diff --from 30d -s ec2,s3\n\n# Scope to a specific account (by profile name, alias, or account ID)\nawsmap diff --from 7d -a production -r us-east-1\n\n# Show only added or removed resources\nawsmap diff --from 7d --type added\nawsmap diff --from 7d --type removed\n\n# Summary only (no resource details)\nawsmap diff --from 7d --summary\n\n# Ignore tag-only changes\nawsmap diff --from 30d --ignore-tags\n\n# JSON output\nawsmap diff --from 7d -f json -o drift-report.json\n\n# HTML report (interactive, with filters and dark mode)\nawsmap diff --from 7d -f html -o drift-report.html\n```\n\n**How it works:** awsmap reconstructs point-in-time snapshots from your scan history. For each `(account, service)`, it finds the latest scan at or before the given date, then compares the two snapshots field by field. This correctly handles partial scans - if you scanned EC2 on Monday and S3 on Tuesday, each service uses its own latest scan.\n\nWith no `--from`, `awsmap diff` compares the state before the most recent scan against the current state, so you can see exactly what your latest scan changed. `--to` without `--from` defaults `--from` to the scan immediately before `--to`.\n\n**Relative dates:** `7d`, `30d`, `90d`, `yesterday`, `today`, or exact dates like `2026-01-15`.\n\n**Change types:**\n- **Added** - resource exists in the newer snapshot but not the older one\n- **Removed** - resource exists in the older snapshot but not the newer one\n- **Modified** - resource exists in both but details, tags, or name changed (with field-level diffs)\n\n## Waste Detection\n\nFind idle or potentially wasteful resources from the data awsmap already collected. No new AWS API calls - the rules run over your latest stored snapshot.\n\n```bash\n# Run all rules against the current snapshot\nawsmap waste\n\n# Counts per rule only\nawsmap waste --summary\n\n# Scope to one account (by profile name, alias, or account ID)\nawsmap waste -a production\n\n# Run only specific rules\nawsmap waste -t unattached-ebs -t available-eni\n\n# Change the age threshold for snapshots and AMIs (default 90 days)\nawsmap waste --min-age-days 180\n\n# Include default AWS resources (excluded by default)\nawsmap waste --include-defaults\n\n# HTML report (interactive, with filters and dark mode)\nawsmap waste -f html -o waste.html\n```\n\n**Rules:**\n\n| Rule key | What it flags |\n|----------|---------------|\n| `unattached-ebs` | EBS volumes in the `available` state |\n| `unassociated-eip` | Elastic IPs not attached to an instance or network interface |\n| `available-eni` | Network interfaces in the `available` (detached) state |\n| `idle-target-group` | Target groups with no registered targets |\n| `empty-classic-elb` | Classic load balancers with no instances |\n| `old-snapshot` | EBS snapshots older than `--min-age-days` (default 90) |\n| `old-ami` | AMIs older than `--min-age-days` (default 90) |\n| `stopped-instance` | EC2 instances in the `stopped` state |\n\nawsmap reports counts and the resources to act on. It does not estimate dollar costs. Output is `table` (default), `json`, or `html`; `is_default` resources are excluded unless you pass `--include-defaults`.\n\n## Tag Compliance\n\nAudit tagging coverage across your inventory and score it against a set of required tags. Operates on already-collected data.\n\n```bash\n# Coverage of \"has at least one tag\"\nawsmap tags\n\n# Compliance against required tags\nawsmap tags -R Owner,Environment,CostCenter\n\n# Scope to an account and service, list only non-compliant resources\nawsmap tags -a production -s ec2 --noncompliant-only\n\n# List only resources with zero tags\nawsmap tags --untagged-only\n\n# Score only, no resource listing\nawsmap tags -R Owner --summary\n\n# HTML report\nawsmap tags -R Owner,Environment -f html -o tag-compliance.html\n```\n\nThe report shows an overall compliance score, per-required-tag coverage (so you can see which tag is the gap), a per-service breakdown, and the list of non-compliant resources with their missing tags.\n\n- Required tags come from `-R/--required` (comma-separated or repeatable) or the `required_tags` config key. With neither set, compliance falls back to \"has at least one tag\".\n- A blank tag value (for example `Owner=`) counts as missing.\n- `is_default` resources are excluded by default; pass `--include-defaults` to keep them.\n- Set a default required set once: `awsmap config set required_tags Owner,Environment,CostCenter`.\n- Output is `table` (default), `json`, or `html`.\n\n## Querying a Specific Scan\n\nBy default `query` and `ask` run against the current snapshot (`is_current`). Use `--scan` to target any scan in your history.\n\n```bash\n# List stored scans\nawsmap query --list-scans\n\n# Run a named query against the previous scan\nawsmap query --scan previous -n admin-users\n\n# Raw SQL against the latest scan (use the {scan_filter} placeholder)\nawsmap query --scan latest \"SELECT service, COUNT(*) FROM resources WHERE {scan_filter} GROUP BY service\"\n\n# Natural language against the first (oldest) scan\nawsmap ask --scan first show me ec2 instances\n```\n\nSelectors: `latest`, `previous`, `first`, or an explicit `\u003cscan_id\u003e` (see `--list-scans`). Named queries, files, and `ask` apply the scope automatically. For raw inline SQL, include the `{scan_filter}` placeholder where the scope should go; using `--scan` on raw SQL without the placeholder reports an error instead of running an unscoped query.\n\n## Demo Database\n\nGenerate a realistic synthetic database to try awsmap without needing an AWS account. Covers all 150+ services, multiple accounts, and multiple scans with drift.\n\n```bash\n# Generate with defaults (3 accounts, 3 scans, ~12,000 resources)\nawsmap demo\n\n# Custom options\nawsmap demo --accounts 2 --scans 5 --db ./demo.db\n\n# Overwrite existing\nawsmap demo --force\n```\n\nAfter generating, use `--db` to point any command at the demo database:\n\n```bash\nawsmap query --db ~/.awsmap/demo.db -n admin-users\nawsmap ask --db ~/.awsmap/demo.db show me all EC2 instances\nawsmap diff --db ~/.awsmap/demo.db --from 30d\nawsmap examples lambda 5 --db ~/.awsmap/demo.db\n```\n\nOr set it as the default database:\n\n```bash\nawsmap config set db ~/.awsmap/demo.db\n```\n\n### Demo Options (`awsmap demo`)\n\n| Option | Description |\n|--------|-------------|\n| `--db` | Database path (default: `~/.awsmap/demo.db`) |\n| `--accounts` | Number of accounts to generate (1-5, default: 3) |\n| `--scans` | Number of scans per account for drift (1-5, default: 3) |\n| `--seed` | Random seed for reproducibility (default: 42) |\n| `--force` | Overwrite existing demo database |\n\n## CLI Options\n\n### Scan Options\n\n| Option | Description |\n|--------|-------------|\n| `-p, --profile` | AWS profile name |\n| `-r, --region` | Region(s) to scan (comma-separated or multiple flags) |\n| `-s, --services` | Service(s) to scan (comma-separated or multiple flags) |\n| `-t, --tag` | Filter by tag Key=Value (multiple allowed) |\n| `-f, --format` | Output format: `html` (default), `json`, `csv` |\n| `-o, --output` | Output file path |\n| `-w, --workers` | Parallel workers (default: 40) |\n| `-q, --quiet` | Suppress progress output |\n| `--timings` | Show timing summary per service |\n| `--include-global` | Include global services when filtering by non-global regions |\n| `--exclude-defaults` | Exclude default AWS resources (default VPCs, security groups, etc.) |\n| `--no-db` | Skip local database storage |\n| `--list-services` | List available service collectors |\n\n### Query Options (`awsmap query`)\n\n| Option | Description |\n|--------|-------------|\n| `-n, --name` | Run a pre-built named query |\n| `-F, --file` | Run SQL from a file |\n| `-l, --list` | List available pre-built queries |\n| `-S, --show` | Show SQL of a named query without running it |\n| `-P, --param` | Parameter for named query (`key=value`, multiple allowed) |\n| `-a, --account` | Scope to an account (account ID, account alias, or AWS profile) |\n| `--scan` | Scope to a scan: `latest`, `previous`, `first`, or a `\u003cscan_id\u003e` (raw SQL needs the `{scan_filter}` placeholder) |\n| `--list-scans` | List stored scans and exit |\n| `--db` | Database path (default: `~/.awsmap/inventory.db`) |\n| `-f, --format` | Output format: `table` (default), `json`, `csv` |\n\n### Ask Options (`awsmap ask`)\n\n| Option | Description |\n|--------|-------------|\n| `-a, --account` | Scope to an account (account ID, account alias, or AWS profile) |\n| `--scan` | Scope to a scan: `latest`, `previous`, `first`, or a `\u003cscan_id\u003e` |\n| `--db` | Database path (default: `~/.awsmap/inventory.db`) |\n\n### Diff Options (`awsmap diff`)\n\n| Option | Description |\n|--------|-------------|\n| `--from` | Start date for comparison. Supports: `YYYY-MM-DD`, `7d`, `30d`, `yesterday`, `today`. If omitted, compares the previous scan against the current state |\n| `--to` | End date (default: current state). Same date formats as `--from` |\n| `-a, --account` | Scope to an account (account ID, alias, or profile) |\n| `-s, --service` | Service(s) to compare (comma-separated or multiple flags) |\n| `-r, --region` | Region(s) to compare (comma-separated or multiple flags) |\n| `--type` | Show only one change type: `all` (default), `added`, `removed`, or `modified` |\n| `--summary` | Show summary counts only, no resource details |\n| `--ignore-tags` | Ignore tag-only changes |\n| `-f, --format` | Output format: `table` (default), `json`, `html` |\n| `-o, --output` | Output file path |\n| `--db` | Database path (default: `~/.awsmap/inventory.db`) |\n\n### Waste Options (`awsmap waste`)\n\n| Option | Description |\n|--------|-------------|\n| `-a, --account` | Scope to an account (account ID, alias, or profile) |\n| `-t, --type` | Run only specific rule key(s) (comma-separated or multiple flags) |\n| `--min-age-days` | Age threshold for `old-snapshot` and `old-ami` (default: 90) |\n| `--include-defaults` | Include default AWS resources (excluded by default) |\n| `--summary` | Show counts per rule only, no resource listing |\n| `-f, --format` | Output format: `table` (default), `json`, `html` |\n| `-o, --output` | Output file path |\n| `--db` | Database path (default: `~/.awsmap/inventory.db`) |\n\n### Tags Options (`awsmap tags`)\n\n| Option | Description |\n|--------|-------------|\n| `-R, --required` | Required tag key(s) (comma-separated or repeatable). Falls back to the `required_tags` config key |\n| `-a, --account` | Scope to an account (account ID, alias, or profile) |\n| `-s, --service` | Scope to service(s) (comma-separated or multiple flags) |\n| `--untagged-only` | List only resources with zero tags |\n| `--noncompliant-only` | List only resources missing a required tag |\n| `--include-defaults` | Include default AWS resources (excluded by default) |\n| `--summary` | Show scores only, no resource listing |\n| `-f, --format` | Output format: `table` (default), `json`, `html` |\n| `-o, --output` | Output file path |\n| `--db` | Database path (default: `~/.awsmap/inventory.db`) |\n\n### Examples Options (`awsmap examples`)\n\n| Argument / Option | Description |\n|-------------------|-------------|\n| `\u003cservice\u003e` | Show questions for a specific service |\n| `\u003cservice\u003e \u003cnumber\u003e` | Run question #N against the database |\n| `-s, --search` | Search all questions by keyword |\n| `--db` | Database path (default: `~/.awsmap/inventory.db`) |\n\n### Config Commands (`awsmap config`)\n\nSet persistent defaults so you don't have to repeat CLI flags. CLI flags always override config values.\n\nOnly the keys listed below are accepted - unknown keys and invalid values are rejected. If the config file is manually edited and contains invalid entries, `awsmap config list` detects them, warns you, and auto-cleans the file.\n\n| Command | Description |\n|---------|-------------|\n| `awsmap config set key value` | Set a configuration value (validated) |\n| `awsmap config get key` | Get a configuration value |\n| `awsmap config list` | List all values (detects and cleans invalid entries) |\n| `awsmap config delete key` | Delete a configuration value |\n\n**Available config keys (only these are accepted):**\n\n| Key | Applies to | Description | Example |\n|-----|-----------|-------------|---------|\n| `profile` | `awsmap` (scan) | Default AWS profile | `awsmap config set profile production` |\n| `regions` | `awsmap` (scan) | Default regions (comma-separated) | `awsmap config set regions us-east-1,eu-west-1` |\n| `services` | `awsmap` (scan) | Default services (comma-separated) | `awsmap config set services ec2,s3,lambda` |\n| `format` | `awsmap` (scan) | Default output format (`html`, `json`, `csv`) | `awsmap config set format json` |\n| `workers` | `awsmap` (scan) | Default parallel workers | `awsmap config set workers 20` |\n| `exclude_defaults` | `awsmap` (scan) | Exclude default AWS resources (`true`/`false`) | `awsmap config set exclude_defaults true` |\n| `db` | `query`, `ask` | Default database path | `awsmap config set db /path/to/inventory.db` |\n| `query_format` | `query` | Default query output format (`table`, `json`, `csv`) | `awsmap config set query_format csv` |\n| `required_tags` | `tags` | Default required tag keys (comma-separated) | `awsmap config set required_tags Owner,Environment,CostCenter` |\n\n```bash\n# Set your usual profile and regions\nawsmap config set profile production\nawsmap config set regions us-east-1,eu-west-1\n\n# Now just run:\nawsmap\n# Equivalent to: awsmap -p production -r us-east-1,eu-west-1\n\n# CLI flags still override config:\nawsmap -p staging    # Uses staging profile, but regions from config\n```\n\n## Shell Completion\n\nawsmap supports tab completion for bash, zsh, and fish. Complete subcommands, service names, regions, AWS profiles, query names, account names, and config keys.\n\n```bash\n# Bash\neval \"$(awsmap completion bash)\"     # add to ~/.bashrc\n\n# Zsh\neval \"$(awsmap completion zsh)\"      # add to ~/.zshrc\n\n# Fish\nawsmap completion fish \u003e ~/.config/fish/completions/awsmap.fish\n```\n\n**What gets completed:**\n\n| Context | Completions |\n|---------|-------------|\n| `awsmap \u003cTAB\u003e` | Subcommands: ask, config, completion, demo, diff, examples, query |\n| `awsmap -s \u003cTAB\u003e` | Service names (ec2, s3, lambda, ...) |\n| `awsmap -r \u003cTAB\u003e` | AWS region names |\n| `awsmap -p \u003cTAB\u003e` | AWS profile names from ~/.aws/credentials and ~/.aws/config |\n| `awsmap query -n \u003cTAB\u003e` | Pre-built query names |\n| `awsmap query -a \u003cTAB\u003e` | Account aliases, profiles, and IDs from the database |\n| `awsmap config set \u003cTAB\u003e` | Valid configuration keys |\n| `awsmap examples \u003cTAB\u003e` | Service names from the examples library |\n\n\u003e **Important: Bash version requirement.** Shell completion requires **Bash 4.4 or newer**. macOS ships with Bash 3.2 (from 2007, frozen due to GPLv3 licensing) which is **not supported**. To fix this on macOS:\n\u003e\n\u003e ```bash\n\u003e # Install modern Bash via Homebrew\n\u003e brew install bash\n\u003e\n\u003e # Add it to allowed shells\n\u003e sudo sh -c 'echo /opt/homebrew/bin/bash \u003e\u003e /etc/shells'\n\u003e\n\u003e # Set it as your default shell\n\u003e chsh -s /opt/homebrew/bin/bash\n\u003e ```\n\u003e\n\u003e Alternatively, macOS users can use **zsh** (the default shell since macOS Catalina) which works out of the box.\n\n## Supported Services\n\n| Category | Services |\n|----------|----------|\n| **Compute** | ec2, lambda, ecs, eks, ecr, ecr-public, lightsail, autoscaling, application-autoscaling, elasticbeanstalk, batch, apprunner, imagebuilder |\n| **Storage** | s3, efs, fsx, backup, datasync, dlm, storagegateway |\n| **Database** | rds, dynamodb, elasticache, memorydb, docdb, neptune, redshift, redshift-serverless, keyspaces, opensearch, opensearch-serverless, dax, dsql, timestream-influxdb |\n| **Networking** | vpc, elbv2, elb, route53, route53resolver, route53domains, cloudfront, globalaccelerator, apigateway, apigatewayv2, appsync, directconnect, network-firewall, servicediscovery, vpc-lattice, networkmanager |\n| **Security** | iam, sso, kms, secretsmanager, acm, acm-pca, wafv2, guardduty, inspector2, securityhub, ds, cognito, accessanalyzer, macie2, detective, shield, fms, cloudhsmv2, auditmanager, securitylake |\n| **Management \u0026 Monitoring** | cloudwatch, logs, cloudtrail, ssm, config, sns, sqs, events, xray, grafana, amp, ce, budgets, compute-optimizer, service-quotas, resource-groups, health, synthetics, appconfig, organizations, servicecatalog, resiliencehub |\n| **Serverless** | stepfunctions, kinesis, firehose, kafka, serverlessrepo, eventbridge-scheduler, eventbridge-pipes, schemas |\n| **Developer Tools** | cloudformation, codeartifact, codebuild, codepipeline, codedeploy, devicefarm |\n| **Analytics** | athena, glue, mwaa, lakeformation, emr, emr-serverless, cleanrooms, quicksight, datazone |\n| **AI/ML** | sagemaker, bedrock, lexv2, rekognition, textract, transcribe, translate, comprehend, polly, personalize, kendra, frauddetector |\n| **Media** | mediaconvert, mediaconnect, mediapackage, medialive, mediastore, mediatailor, ivs |\n| **Migration \u0026 Transfer** | transfer, dms |\n| **End User Computing** | workspaces, amplify, connect |\n| **IoT** | iot, iotsitewise |\n| **Other** | ram, resource-explorer-2, mq, sesv2, appflow, gamelift, outposts, fis, location |\n\nFor detailed resource types per service, see [SERVICES.md](SERVICES.md).\n\n## Output Formats\n\n### HTML (Default)\nInteractive report with:\n- Dashboard with resource counts and charts\n- Global search across all resources\n- Filter by service and region\n- Collapsible service sections\n- Click to copy ARN/ID\n- Clickable tag badges (shows all tags)\n- Dark/light mode toggle\n- Export filtered view to CSV\n- Print-friendly\n\n### JSON\n```json\n{\n  \"metadata\": {\n    \"account_id\": \"123456789012\",\n    \"timestamp\": \"2024-12-24 15:30:00 UTC\",\n    \"resource_count\": 1590\n  },\n  \"resources\": [\n    {\n      \"service\": \"ec2\",\n      \"type\": \"instance\",\n      \"id\": \"i-1234567890abcdef0\",\n      \"arn\": \"arn:aws:ec2:us-east-1:123456789012:instance/i-1234567890abcdef0\",\n      \"name\": \"my-instance\",\n      \"region\": \"us-east-1\",\n      \"is_default\": false,\n      \"details\": {...},\n      \"tags\": {\"Owner\": \"John\", \"Environment\": \"Production\"}\n    }\n  ]\n}\n```\n\n### CSV\nFlat format with columns: service, type, id, name, region, arn, is_default, tags\n\n## Tag Filtering\n\n```bash\n# Single tag\nawsmap -t Environment=Production\n\n# Multiple values for same key (OR logic)\nawsmap -t Owner=John -t Owner=Jane\n# Returns resources where Owner is \"John\" OR \"Jane\"\n\n# Multiple keys (AND logic)\nawsmap -t Owner=John -t Environment=Production\n# Returns resources where Owner is \"John\" AND Environment is \"Production\"\n\n# Combined\nawsmap -t Owner=John -t Owner=Jane -t Environment=Production\n# Returns resources where (Owner is \"John\" OR \"Jane\") AND Environment is \"Production\"\n```\n\n## Global vs Regional Services\n\nAWS has two types of services:\n- **Regional services** (EC2, RDS, Lambda, etc.) - Resources exist in specific regions\n- **Global services** (IAM, Route53, CloudFront, etc.) - Resources are account-wide, not region-specific\n\n### How awsmap handles global services\n\nWhen you filter by region, awsmap intelligently includes global services based on their **control plane location**:\n\n| Command | Behavior |\n|---------|----------|\n| `awsmap` (no region) | All services (regional + global) |\n| `awsmap -r us-east-1` | Regional in us-east-1 + global services with us-east-1 control plane |\n| `awsmap -r us-west-2` | Regional in us-west-2 + global services with us-west-2 control plane |\n| `awsmap -r eu-west-1` | Regional in eu-west-1 only (no global services) |\n| `awsmap -r eu-west-1 --include-global` | Regional in eu-west-1 + all global services |\n\n### Global services by control plane\n\nBased on [AWS Global Services documentation](https://docs.aws.amazon.com/whitepapers/latest/aws-fault-isolation-boundaries/global-services.html):\n\n| Control Plane | Global Services |\n|---------------|-----------------|\n| **us-east-1** | IAM, Organizations, Route53, Route53 Domains, CloudFront, Shield, Budgets, Cost Explorer, Health |\n| **us-west-2** | Network Manager, Global Accelerator |\n\n### S3 buckets\n\nS3 bucket names are globally unique, but **each bucket has a specific region**. awsmap treats S3 as a regional service:\n\n```bash\n# Only S3 buckets in eu-west-1\nawsmap -r eu-west-1 -s s3\n\n# All S3 buckets\nawsmap -s s3\n```\n\n## Performance\n\nScans **150+ services** across all regions in parallel.\n\n| Account Size | Resources | Estimated Time |\n|--------------|-----------|----------------|\n| Small | \u003c500 | ~1.5 minutes |\n| Medium | 500-5,000 | ~2 minutes |\n| Large | 5,000-20,000 | ~3-5 minutes |\n| Enterprise | 20,000+ | ~5-10 minutes |\n\n**Tuning Options:**\n```bash\n# Increase parallelism for faster scans\nawsmap -p myprofile -w 50\n\n# Reduce parallelism for rate-limited accounts\nawsmap -p myprofile -w 20\n\n# Scan specific services only (much faster)\nawsmap -p myprofile -s ec2,s3,lambda,iam\n\n# Scan specific regions only\nawsmap -p myprofile -r us-east-1,eu-west-1\n```\n\n**Why is the scan fast?**\n- Parallel execution with configurable workers (default: 40)\n- Region-aware collectors skip unsupported regions automatically\n- Global services (IAM, Route53, etc.) collected once, not per-region\n- Smart region filtering excludes global services when not relevant\n- Optimized API calls (batch operations where available)\n\n## IAM Permissions\n\nOnly scanning (`awsmap`) calls AWS, and it needs read-only access to the services you want to inventory. The analysis commands (`query`, `ask`, `diff`, `waste`, `tags`) run entirely against your local database and require no AWS permissions.\n\nBeyond the per-service read actions, a scan calls `sts:GetCallerIdentity`, `account:ListRegions` (to discover enabled regions; falls back to a built-in region list if denied), and `iam:ListAccountAliases` (for the account alias).\n\n### Recommended: ReadOnlyAccess plus a small supplement\n\nAttach the AWS managed [`ReadOnlyAccess`](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/ReadOnlyAccess.html) policy. It is maintained by AWS and covers the large majority of awsmap's read calls.\n\n`ReadOnlyAccess` does not cover everything, though: it lags on some newer services (Amazon Location, MediaTailor, Timestream for InfluxDB, Textract adapters) and deliberately omits a few read actions (for example `glue:GetConnections`). awsmap calls 26 read actions that `ReadOnlyAccess` does not grant. They were computed by diffing awsmap's exact API calls against the live `ReadOnlyAccess` document, so the list is the precise difference, not a guess.\n\nAttach this supplemental policy alongside `ReadOnlyAccess`:\n\n```json\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"awsmapSupplementalReadOnly\",\n      \"Effect\": \"Allow\",\n      \"Action\": [\n        \"airflow:GetEnvironment\",\n        \"bedrock:ListTagsForResource\",\n        \"codeartifact:ListPackageGroups\",\n        \"fms:GetResourceSet\",\n        \"fms:ListResourceSets\",\n        \"geo:ListGeofenceCollections\",\n        \"geo:ListMaps\",\n        \"geo:ListPlaceIndexes\",\n        \"geo:ListRouteCalculators\",\n        \"geo:ListTrackers\",\n        \"glue:GetConnections\",\n        \"mediatailor:ListChannels\",\n        \"mediatailor:ListPlaybackConfigurations\",\n        \"mediatailor:ListSourceLocations\",\n        \"quicksight:ListAnalyses\",\n        \"quicksight:ListDashboards\",\n        \"quicksight:ListDataSets\",\n        \"quicksight:ListDataSources\",\n        \"quicksight:ListTagsForResource\",\n        \"rekognition:DescribeCollection\",\n        \"textract:GetAdapter\",\n        \"textract:ListAdapters\",\n        \"timestream-influxdb:GetDbInstance\",\n        \"timestream-influxdb:ListDbInstances\",\n        \"timestream-influxdb:ListDbParameterGroups\",\n        \"timestream-influxdb:ListTagsForResource\"\n      ],\n      \"Resource\": \"*\"\n    }\n  ]\n}\n```\n\nAttach both to a role (or use `attach-user-policy` for a user):\n\n```bash\n# 1. Attach the AWS managed ReadOnlyAccess policy\naws iam attach-role-policy \\\n  --role-name YourRoleName \\\n  --policy-arn arn:aws:iam::aws:policy/ReadOnlyAccess\n\n# 2. Create the supplemental policy from the JSON above and attach it\naws iam create-policy \\\n  --policy-name awsmap-supplemental-readonly \\\n  --policy-document file://awsmap-supplemental-readonly.json\n\naws iam attach-role-policy \\\n  --role-name YourRoleName \\\n  --policy-arn arn:aws:iam::\u003caccount-id\u003e:policy/awsmap-supplemental-readonly\n```\n\nEvery collector call is wrapped so a denied permission never stops a scan: the affected resources are simply skipped. The supplement only removes those blind spots so the inventory is complete. All 26 actions are read-only.\n\n### Alternative: no managed policy\n\nIf you cannot use `ReadOnlyAccess`, grant read actions (`Describe*`, `List*`, `Get*`, plus `BatchGet*`/`Search*` where applicable) for the services awsmap scans, together with the supplemental actions above. The full standalone list covers about 150 service prefixes; see the [IAM Service Authorization Reference](https://docs.aws.amazon.com/service-authorization/latest/reference/reference_policies_actions-resources-contextkeys.html) for per-service read actions.\n\n## What's NOT Collected\n\nThis tool only collects **user-owned resources**, excluding:\n- AWS-managed policies (only customer-managed)\n- AWS-managed KMS keys (only customer-managed)\n- Default parameter groups and option groups\n- AWS service-linked roles\n- Reserved instance offerings (pricing catalog)\n- Foundation models (Bedrock catalog)\n- Automated backups (only manual snapshots)\n- AWS system keyspaces (Keyspaces: `system_*`)\n- AWS default queues/groups (MediaConvert, X-Ray)\n- AWS managed domain lists (Route53 Resolver: `AWSManagedDomains*`)\n- Default data lake settings (Lake Formation)\n\n**Default VPC resources** (default VPCs, subnets, security groups, route tables, internet gateways, NACLs, DHCP options) are collected by default and marked with a \"DEFAULT\" badge in HTML reports. Use `--exclude-defaults` to filter them out.\n\nSee [SERVICES.md](SERVICES.md#filtered-resources) for the complete list of filtered resources.\n\n## Why a Built-In NLQ Parser Instead of AI/LLM?\n\nWe evaluated three approaches for natural language queries:\n\n| Approach | Accuracy | Cost | Latency | Offline |\n|----------|----------|------|---------|---------|\n| **Ollama (local LLMs)** | ~80% | Free | Slow (seconds) | Yes |\n| **OpenAI / Anthropic APIs** | ~95% | Pay per query | Network dependent | No |\n| **Built-in parser (awsmap)** | **100%** | **Free** | **Instant** | **Yes** |\n\n- **Ollama** models are free and run locally, but when tested against real AWS inventory queries, accuracy was around 80% - one in five queries would generate wrong SQL or fail silently. Not acceptable for a CLI tool where users trust the output.\n- **OpenAI / Anthropic APIs** produce better results, but require API keys, cost money per query, and depend on network connectivity. Not ideal for an infrastructure tool that should just work.\n- **Built-in parser** is a zero-dependency, deterministic NL-to-SQL engine. It's tested against **1500 realistic test questions with a 100% pass rate** (separate from the 1381 examples library). It covers listing, counting, aggregation, region filters, negation, tags, multi-service queries, synonyms, typo tolerance, relative time, numeric fields, keyword-value patterns, and 150+ AWS services. No API keys, no network, no cost, instant results.\n\nThe 1500 test questions (used during development to validate the parser) are designed to cover the vast majority of real-world use cases. The parser also includes typo tolerance, synonym support, and fuzzy matching to handle natural variations in how people phrase questions.\n\n\u003e **Found a bug or an inaccurate query?** Please [open an issue](https://github.com/TocConsulting/awsmap/issues) and report it! Every report helps improve the parser for everyone. **If you have ideas for a better approach than the built-in NLQ, we're always open to suggestions.**\n\n## Support\n\n- **Documentation**: Check this README and [SERVICES.md](SERVICES.md)\n- **Issues**: Report bugs via [GitHub Issues](https://github.com/TocConsulting/awsmap/issues)\n- **Discussions**: Join conversations in [GitHub Discussions](https://github.com/TocConsulting/awsmap/discussions)\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTocConsulting%2Fawsmap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FTocConsulting%2Fawsmap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTocConsulting%2Fawsmap/lists"}