Skip to content

Commit 7db93c8

Browse files
committed
Add SQLite database, NLQ engine, query library, examples, and config system
Major new features for v1.5.0: 1. Local SQLite database (db.py) Scan results are now stored locally in ~/.awsmap/inventory.db. Query historical scans, compare across accounts, and track changes over time. Multi-account support: each scan records its account ID. 2. Built-in Natural Language Query engine (nlq.py) Ask questions in plain English with zero dependencies — no LLM, no API keys, no network. Deterministic NL-to-SQL parser tested against 1500 realistic questions with 100% pass rate. awsmap ask show me all EC2 instances without Owner tag awsmap ask how many Lambda functions per region awsmap ask -a production show me S3 buckets 3. Pre-built query library (30 SQL queries) Ready-to-use security, compliance, and operational queries: awsmap query -n admin-users awsmap query -n open-security-groups awsmap query -n resources-by-tag -P tag=Owner awsmap query --list Users can add custom queries in ~/.awsmap/queries/. 4. Examples library (1381 questions across 51 services) Browse and run pre-built questions organized by AWS service: awsmap examples awsmap examples lambda awsmap examples lambda 5 awsmap examples --search "public" 5. Configuration system (config.py) Persistent defaults so users don't repeat CLI flags: awsmap config set profile production awsmap config set regions us-east-1,eu-west-1 Other changes: - Add --version flag - Add --workers minimum validation (must be >= 1) - Fix S3 Tables region filtering - Fix timestream-influxdb type naming (db_instance → db-instance) to match all other services - Remove unused pyyaml dependency - Remove double service validation in collect_all - Align collect_all default max_workers to 40 (matches CLI) - Update requires-python to >=3.9, add Python 3.13 classifier - Update ROADMAP: remove 5 already-implemented services New files: src/aws_inventory/db.py — SQLite database layer src/aws_inventory/nlq.py — NL-to-SQL parser (3000+ lines) src/aws_inventory/config.py — Configuration system src/aws_inventory/examples.py — Examples library (1381 questions) src/aws_inventory/queries_lib.py — Query library loader src/aws_inventory/queries/*.sql — 30 pre-built SQL queries examples/queries/*.sql — 7 sample custom queries
1 parent 61e7ea6 commit 7db93c8

54 files changed

Lines changed: 7907 additions & 35 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ASK_EXAMPLES.md

Lines changed: 1510 additions & 0 deletions
Large diffs are not rendered by default.

Dockerfile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ FROM python:3.11-slim
22

33
LABEL maintainer="Tarek CHEIKH <tarek@tocconsulting.fr>"
44
LABEL description="A fast, comprehensive tool for mapping and inventorying AWS resources across 150+ services"
5-
LABEL version="1.4.0"
5+
LABEL version="1.5.0"
66

77
# Set environment variables
88
ENV PYTHONDONTWRITEBYTECODE=1
@@ -18,8 +18,8 @@ COPY src/ ./src/
1818
# Install the package
1919
RUN pip install --no-cache-dir .
2020

21-
# Create output directory
22-
RUN mkdir -p /app/output
21+
# Create output and data directories
22+
RUN mkdir -p /app/output /root/.awsmap
2323

2424
# Default entrypoint
2525
ENTRYPOINT ["awsmap"]

README.md

Lines changed: 271 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@ A fast, comprehensive tool for mapping and inventorying AWS resources across 150
2020

2121
- **150+ AWS Services**: Covers compute, storage, database, networking, security, and more
2222
- **Multi-Region**: Parallel scanning across all enabled regions
23-
- **Tag Filtering**: Filter resources by tags with OR logic for same key, AND logic across keys
23+
- **Local Database**: Every scan auto-stored in SQLite — query your inventory offline
24+
- **SQL Query Engine**: Run SQL against your inventory history (`awsmap query "SELECT ..."`)
25+
- **Pre-Built Query Library**: 30 ready-to-use security and compliance queries (`awsmap query -n admin-users`)
26+
- **Natural Language Queries**: Ask questions in plain English — zero dependencies, works out of the box (`awsmap ask show me all EC2 without Owner tag`)
27+
- **Examples Library**: 1381 ready-to-run questions organized by service (`awsmap examples lambda`)
28+
- **Multi-Account**: Scan multiple accounts, query across all of them
29+
- **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)
2430
- **Beautiful HTML Reports**: Interactive reports with search, filters, dark mode, and export
2531
- **Multiple Outputs**: JSON, CSV, and HTML formats
2632
- **Fast**: Parallel execution with 40 workers (~2 minutes for typical accounts)
@@ -65,6 +71,7 @@ pip install -e .
6571
docker run --rm \
6672
-v ~/.aws:/root/.aws:ro \
6773
-v $(pwd)/output:/app/output \
74+
-v ~/.awsmap:/root/.awsmap \
6875
awsmap -p myprofile -o /app/output/inventory.html
6976

7077
# Using environment variables
@@ -73,8 +80,14 @@ docker run --rm \
7380
-e AWS_SECRET_ACCESS_KEY \
7481
-e AWS_DEFAULT_REGION=us-east-1 \
7582
-v $(pwd)/output:/app/output \
83+
-v ~/.awsmap:/root/.awsmap \
7684
awsmap -o /app/output/inventory.html
7785

86+
# Query stored inventory
87+
docker run --rm \
88+
-v ~/.awsmap:/root/.awsmap \
89+
awsmap query "SELECT service, COUNT(*) as count FROM resources GROUP BY service ORDER BY count DESC"
90+
7891
# List available services
7992
docker run --rm awsmap --list-services
8093
```
@@ -105,10 +118,180 @@ awsmap -p myprofile --timings
105118

106119
# Exclude default AWS resources (default VPCs, security groups, etc.)
107120
awsmap -p myprofile --exclude-defaults
121+
122+
# Skip local database storage
123+
awsmap -p myprofile --no-db
124+
```
125+
126+
## Multi-Account
127+
128+
Scan multiple AWS accounts. Each scan is stored in the same local database — query across all of them.
129+
130+
```bash
131+
# Scan different accounts (different profiles)
132+
awsmap -p production
133+
awsmap -p staging
134+
awsmap -p dev-account
135+
136+
# Query across all accounts
137+
awsmap query -n resources-by-account
138+
awsmap ask how many resources per account
139+
140+
# Scope to one account
141+
awsmap query -n admin-users -a production
142+
awsmap ask -a staging show me all Lambda functions
143+
```
144+
145+
## Query Your Inventory
146+
147+
Every scan is automatically stored in a local SQLite database (`~/.awsmap/inventory.db`). Query it offline with raw SQL or natural language.
148+
149+
### SQL Queries
150+
151+
```bash
152+
# Count resources per service
153+
awsmap query "SELECT service, COUNT(*) as count FROM resources GROUP BY service ORDER BY count DESC"
154+
155+
# Find all EC2 instances in a specific region
156+
awsmap query "SELECT id, name, region FROM resources WHERE service='ec2' AND type='instance'"
157+
158+
# View scan history
159+
awsmap query "SELECT * FROM scans ORDER BY timestamp DESC"
160+
161+
# JSON or CSV output
162+
awsmap query "SELECT * FROM resources WHERE service='s3'" -f json
163+
awsmap query "SELECT service, id, name FROM resources" -f csv
164+
165+
# Query tags (filter to resources that have the tag)
166+
awsmap query "SELECT id, name, json_extract(tags, '$.Owner') as owner FROM resources WHERE service='ec2' AND json_extract(tags, '$.Owner') IS NOT NULL"
167+
```
168+
169+
**More SQL examples:** See `examples/queries/*.sql` for ready-to-use query templates you can customize.
170+
171+
### Pre-Built Query Library
172+
173+
awsmap ships with 30 pre-built queries for common security, compliance, and operational tasks. No SQL knowledge required.
174+
175+
```bash
176+
# List all available queries
177+
awsmap query --list
178+
179+
# Run a named query
180+
awsmap query -n admin-users
181+
awsmap query -n users-without-mfa
182+
awsmap query -n open-security-groups
183+
awsmap query -n untagged-resources
184+
185+
# Pass parameters (find resources with Owner tag)
186+
awsmap query -n resources-by-tag -P tag=Owner
187+
188+
# Multiple parameters (find EC2 missing Environment tag)
189+
awsmap query -n missing-tag -P tag=Environment -P service=ec2
190+
191+
# Scope to a specific account
192+
awsmap query -n admin-users -a production
193+
194+
# Show query SQL without running it
195+
awsmap query --show admin-users
196+
197+
# Run SQL from a file
198+
awsmap query -F my-query.sql
199+
```
200+
201+
**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`).
202+
203+
**Available queries:**
204+
205+
| Query | Description | Example |
206+
|-------|-------------|---------|
207+
| **IAM / Security** | | |
208+
| `admin-users` | IAM users with admin permissions (direct + via group) | `awsmap query -n admin-users` |
209+
| `admin-roles` | IAM roles with admin permissions | `awsmap query -n admin-roles` |
210+
| `users-without-mfa` | IAM users without MFA enabled | `awsmap query -n users-without-mfa` |
211+
| `iam-inactive-users` | IAM users with no login and no access keys | `awsmap query -n iam-inactive-users` |
212+
| `old-access-keys` | IAM users with access keys | `awsmap query -n old-access-keys` |
213+
| `cross-account-roles` | IAM roles with trust policies allowing external accounts | `awsmap query -n cross-account-roles` |
214+
| `open-security-groups` | Security groups with 0.0.0.0/0 ingress rules | `awsmap query -n open-security-groups` |
215+
| `secrets-no-rotation` | Secrets Manager secrets without auto-rotation | `awsmap query -n secrets-no-rotation` |
216+
| **S3** | | |
217+
| `public-s3-buckets` | S3 buckets with public access enabled | `awsmap query -n public-s3-buckets` |
218+
| `encryption-status` | S3 buckets and their encryption configuration | `awsmap query -n encryption-status` |
219+
| `s3-no-versioning` | S3 buckets without versioning | `awsmap query -n s3-no-versioning` |
220+
| `s3-no-logging` | S3 buckets without access logging | `awsmap query -n s3-no-logging` |
221+
| **EC2 / EBS** | | |
222+
| `stopped-instances` | EC2 instances in stopped state | `awsmap query -n stopped-instances` |
223+
| `unused-volumes` | EBS volumes not attached to any instance | `awsmap query -n unused-volumes` |
224+
| `ebs-unencrypted` | EBS volumes without encryption | `awsmap query -n ebs-unencrypted` |
225+
| `unused-eips` | Elastic IPs not associated with any instance | `awsmap query -n unused-eips` |
226+
| `default-vpcs` | Default VPCs across all regions | `awsmap query -n default-vpcs` |
227+
| **RDS** | | |
228+
| `rds-public` | RDS instances with public access enabled | `awsmap query -n rds-public` |
229+
| `rds-unencrypted` | RDS instances without encryption | `awsmap query -n rds-unencrypted` |
230+
| `rds-no-multi-az` | RDS instances without Multi-AZ | `awsmap query -n rds-no-multi-az` |
231+
| `rds-engines` | RDS instances grouped by engine | `awsmap query -n rds-engines` |
232+
| **Lambda** | | |
233+
| `lambda-runtimes` | Lambda functions grouped by runtime | `awsmap query -n lambda-runtimes` |
234+
| `lambda-high-memory` | Lambda functions with memory > 512 MB | `awsmap query -n lambda-high-memory` |
235+
| **Tags** | | |
236+
| `untagged-resources` | Resources with no tags | `awsmap query -n untagged-resources` |
237+
| `missing-tag` | Resources missing a specific tag | `awsmap query -n missing-tag -P tag=Owner` |
238+
| `resources-by-tag` | Resources that have a specific tag | `awsmap query -n resources-by-tag -P tag=Owner` |
239+
| **Inventory** | | |
240+
| `resources-by-service` | Resource count per service | `awsmap query -n resources-by-service` |
241+
| `resources-by-region` | Resource count per region | `awsmap query -n resources-by-region` |
242+
| `resources-by-account` | Resource count per account | `awsmap query -n resources-by-account` |
243+
| `resources-per-account-service` | Resource count per account per service | `awsmap query -n resources-per-account-service` |
244+
245+
You 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:`).
246+
247+
### Natural Language Queries
248+
249+
Ask 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.
250+
251+
```bash
252+
awsmap ask how many resources per region
253+
awsmap ask show me all EC2 instances without Owner tag
254+
awsmap ask which S3 buckets are in eu-west-1
255+
awsmap ask what services have the most resources
256+
```
257+
258+
awsmap translates your question to SQL using a **built-in parser** (zero dependencies), shows you the generated query, and displays the results.
259+
260+
### Examples Library
261+
262+
Browse and run 1381 pre-built questions organized by AWS service using `awsmap examples`.
263+
264+
```bash
265+
# List all services with question counts
266+
awsmap examples
267+
268+
# Browse questions for a service
269+
awsmap examples lambda
270+
271+
# Run a specific question by number
272+
awsmap examples lambda 5
273+
274+
# Search across all questions
275+
awsmap examples --search "public"
276+
awsmap examples --search "encryption"
277+
```
278+
279+
#### Multi-Account Queries
280+
281+
When multiple accounts have been scanned, `awsmap ask` queries all of them by default. Use `-a` to scope to a single account:
282+
283+
```bash
284+
# Query across all accounts
285+
awsmap ask show me all IAM users
286+
287+
# Scope to one account
288+
awsmap ask -a production show me Lambda functions
108289
```
109290

110291
## CLI Options
111292

293+
### Scan Options
294+
112295
| Option | Description |
113296
|--------|-------------|
114297
| `-p, --profile` | AWS profile name |
@@ -122,8 +305,77 @@ awsmap -p myprofile --exclude-defaults
122305
| `--timings` | Show timing summary per service |
123306
| `--include-global` | Include global services when filtering by non-global regions |
124307
| `--exclude-defaults` | Exclude default AWS resources (default VPCs, security groups, etc.) |
308+
| `--no-db` | Skip local database storage |
125309
| `--list-services` | List available service collectors |
126310

311+
### Query Options (`awsmap query`)
312+
313+
| Option | Description |
314+
|--------|-------------|
315+
| `-n, --name` | Run a pre-built named query |
316+
| `-F, --file` | Run SQL from a file |
317+
| `-l, --list` | List available pre-built queries |
318+
| `-S, --show` | Show SQL of a named query without running it |
319+
| `-P, --param` | Parameter for named query (`key=value`, multiple allowed) |
320+
| `-a, --account` | Scope to an account (account ID, account alias, or AWS profile) |
321+
| `--db` | Database path (default: `~/.awsmap/inventory.db`) |
322+
| `-f, --format` | Output format: `table` (default), `json`, `csv` |
323+
324+
### Ask Options (`awsmap ask`)
325+
326+
| Option | Description |
327+
|--------|-------------|
328+
| `-a, --account` | Scope to an account (account ID, account alias, or AWS profile) |
329+
| `--db` | Database path (default: `~/.awsmap/inventory.db`) |
330+
331+
### Examples Options (`awsmap examples`)
332+
333+
| Argument / Option | Description |
334+
|-------------------|-------------|
335+
| `<service>` | Show questions for a specific service |
336+
| `<service> <number>` | Run question #N against the database |
337+
| `-s, --search` | Search all questions by keyword |
338+
| `--db` | Database path (default: `~/.awsmap/inventory.db`) |
339+
340+
### Config Commands (`awsmap config`)
341+
342+
Set persistent defaults so you don't have to repeat CLI flags. CLI flags always override config values.
343+
344+
Only 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.
345+
346+
| Command | Description |
347+
|---------|-------------|
348+
| `awsmap config set key value` | Set a configuration value (validated) |
349+
| `awsmap config get key` | Get a configuration value |
350+
| `awsmap config list` | List all values (detects and cleans invalid entries) |
351+
| `awsmap config delete key` | Delete a configuration value |
352+
353+
**Available config keys (only these are accepted):**
354+
355+
| Key | Applies to | Description | Example |
356+
|-----|-----------|-------------|---------|
357+
| `profile` | `awsmap` (scan) | Default AWS profile | `awsmap config set profile production` |
358+
| `regions` | `awsmap` (scan) | Default regions (comma-separated) | `awsmap config set regions us-east-1,eu-west-1` |
359+
| `services` | `awsmap` (scan) | Default services (comma-separated) | `awsmap config set services ec2,s3,lambda` |
360+
| `format` | `awsmap` (scan) | Default output format (`html`, `json`, `csv`) | `awsmap config set format json` |
361+
| `workers` | `awsmap` (scan) | Default parallel workers | `awsmap config set workers 20` |
362+
| `exclude_defaults` | `awsmap` (scan) | Exclude default AWS resources (`true`/`false`) | `awsmap config set exclude_defaults true` |
363+
| `db` | `query`, `ask` | Default database path | `awsmap config set db /path/to/inventory.db` |
364+
| `query_format` | `query` | Default query output format (`table`, `json`, `csv`) | `awsmap config set query_format csv` |
365+
366+
```bash
367+
# Set your usual profile and regions
368+
awsmap config set profile production
369+
awsmap config set regions us-east-1,eu-west-1
370+
371+
# Now just run:
372+
awsmap
373+
# Equivalent to: awsmap -p production -r us-east-1,eu-west-1
374+
375+
# CLI flags still override config:
376+
awsmap -p staging # Uses staging profile, but regions from config
377+
```
378+
127379
## Supported Services
128380

129381
| Category | Services |
@@ -316,6 +568,24 @@ This tool only collects **user-owned resources**, excluding:
316568

317569
See [SERVICES.md](SERVICES.md#filtered-resources) for the complete list of filtered resources.
318570

571+
## Why a Built-In NLQ Parser Instead of AI/LLM?
572+
573+
We evaluated three approaches for natural language queries:
574+
575+
| Approach | Accuracy | Cost | Latency | Offline |
576+
|----------|----------|------|---------|---------|
577+
| **Ollama (local LLMs)** | ~80% | Free | Slow (seconds) | Yes |
578+
| **OpenAI / Anthropic APIs** | ~95% | Pay per query | Network dependent | No |
579+
| **Built-in parser (awsmap)** | **100%** | **Free** | **Instant** | **Yes** |
580+
581+
- **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.
582+
- **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.
583+
- **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.
584+
585+
The 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.
586+
587+
> **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.**
588+
319589
## Support
320590

321591
- **Documentation**: Check this README and [SERVICES.md](SERVICES.md)

ROADMAP.md

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# Roadmap
22

3-
We currently cover **146 AWS services**. Below are the **91 services** we still need to add: **67 new collectors** and **24 extensions** to existing collectors.
3+
We currently cover **150 AWS services**. Below are the **86 services** we still need to add: **64 new collectors** and **22 extensions** to existing collectors.
44

55
This list was built by comparing all **417 services available in boto3** against what awsmap already covers, then filtering out deprecated/sunset services and data-plane-only APIs that don't have inventoriable resources.
66

77
To contribute: pick a service, write a collector in `src/aws_inventory/collectors/`, and open a PR.
88

9-
## New collectors (67)
9+
## New collectors (64)
1010

1111
- **aiops** — AI Operations
1212
- **appfabric** — App bundles, ingestions
@@ -21,10 +21,8 @@ To contribute: pick a service, write a collector in `src/aws_inventory/collector
2121
- **controltower** — Landing zones, enabled controls
2222
- **databrew** — Glue DataBrew datasets, projects, recipes, jobs
2323
- **dataexchange** — Data sets, revisions
24-
- **datazone** — Domains, projects, environments
2524
- **deadline** — Deadline Cloud farms, queues, fleets
2625
- **drs** — Elastic Disaster Recovery
27-
- **dsql** — Aurora DSQL clusters
2826
- **entityresolution** — Matching workflows, schema mappings
2927
- **evs** — Elastic VMware Service environments
3028
- **gameliftstreams** — GameLift Streams stream groups
@@ -70,17 +68,15 @@ To contribute: pick a service, write a collector in `src/aws_inventory/collector
7068
- **ssm-quicksetup** — Quick Setup configurations
7169
- **ssm-sap** — SAP applications
7270
- **supplychain** — Supply Chain instances
73-
- **timestream-influxdb** — Timestream for InfluxDB instances
7471
- **tnb** — Telco Network Builder packages, networks
7572
- **verifiedpermissions** — Policy stores, policies
7673
- **wellarchitected** — Workloads, lenses, reviews
7774
- **wickr** — Wickr networks
7875
- **workmail** — Organizations, users, groups
7976

80-
## Extensions to existing collectors (24)
77+
## Extensions to existing collectors (22)
8178

8279
Extend `bedrock.py`:
83-
- **bedrock-agent** — Agents, knowledge bases, data sources
8480
- **bedrock-data-automation** — Data automation projects
8581

8682
Extend `cleanrooms.py`:
@@ -121,7 +117,6 @@ Extend `route53resolver.py`:
121117
Extend `s3.py`:
122118
- **s3control** — Access points, Storage Lens
123119
- **s3outposts** — S3 on Outposts endpoints
124-
- **s3tables** — S3 Tables table buckets
125120

126121
Extend `workspaces.py`:
127122
- **workspaces-instances** — WorkSpaces Instances

0 commit comments

Comments
 (0)