open-aws-scanner 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,609 @@
1
+ Metadata-Version: 2.4
2
+ Name: open-aws-scanner
3
+ Version: 0.1.0
4
+ Summary: Find unused AWS resources costing you money. No admin, no auth, just results.
5
+ Author: Jason
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/yourusername/open-aws-scanner
8
+ Project-URL: Issues, https://github.com/yourusername/open-aws-scanner/issues
9
+ Keywords: aws,cost-optimization,cloud,scanner,waste-detection
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: System Administrators
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: System :: Systems Administration
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+ Requires-Dist: fastapi>=0.100.0
24
+ Requires-Dist: uvicorn>=0.20.0
25
+ Requires-Dist: sqlalchemy>=2.0.0
26
+ Requires-Dist: apscheduler>=3.10.0
27
+ Requires-Dist: boto3>=1.28.0
28
+ Requires-Dist: python-dotenv>=1.0.0
29
+ Requires-Dist: slowapi>=0.1.9
30
+ Provides-Extra: postgres
31
+ Requires-Dist: psycopg2-binary>=2.9.0; extra == "postgres"
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest; extra == "dev"
34
+ Requires-Dist: httpx; extra == "dev"
35
+
36
+ # Open AWS Scanner
37
+
38
+ Find unused AWS resources costing you money. No admin panel, no Keycloak, no multi-tenant complexity — just point it at your AWS account and find waste.
39
+
40
+ ---
41
+
42
+ ## Table of Contents
43
+
44
+ - [Install](#install)
45
+ - [How To: Run Your First Scan](#how-to-run-your-first-scan)
46
+ - [How To: Set Up AWS Credentials](#how-to-set-up-aws-credentials)
47
+ - [How To: Scan Multiple Regions](#how-to-scan-multiple-regions)
48
+ - [How To: Run as a Server](#how-to-run-as-a-server)
49
+ - [How To: Use the API](#how-to-use-the-api)
50
+ - [How To: Run with Docker](#how-to-run-with-docker)
51
+ - [How To: Use Stage Mode (No AWS Needed)](#how-to-use-stage-mode-no-aws-needed)
52
+ - [How To: Track Savings](#how-to-track-savings)
53
+ - [How To: Use Postgres Instead of SQLite](#how-to-use-postgres-instead-of-sqlite)
54
+ - [How To: Set Up the IAM Role](#how-to-set-up-the-iam-role)
55
+ - [How To: Verify Package Signatures](#how-to-verify-package-signatures)
56
+ - [CLI Reference](#cli-reference)
57
+ - [API Reference](#api-reference)
58
+ - [What It Scans](#what-it-scans)
59
+ - [Configuration Reference](#configuration-reference)
60
+ - [Relationship to CostOps Platform](#relationship-to-costops-platform)
61
+ - [License](#license)
62
+
63
+ ---
64
+
65
+ ## Install
66
+
67
+ ```bash
68
+ pip install open-aws-scanner
69
+ ```
70
+
71
+ Or from source:
72
+
73
+ ```bash
74
+ git clone https://github.com/yourusername/open-aws-scanner.git
75
+ cd open-aws-scanner
76
+ pip install .
77
+ ```
78
+
79
+ For development (editable install):
80
+
81
+ ```bash
82
+ pip install -e .
83
+ ```
84
+
85
+ ---
86
+
87
+ ## How To: Run Your First Scan
88
+
89
+ **Step 1** — Create a config file:
90
+
91
+ ```bash
92
+ open-aws-scanner init
93
+ ```
94
+
95
+ This creates `config.env` in your current directory.
96
+
97
+ **Step 2** — Edit `config.env` with your AWS setup (see [credentials section](#how-to-set-up-aws-credentials) below).
98
+
99
+ **Step 3** — Run a scan:
100
+
101
+ ```bash
102
+ open-aws-scanner scan
103
+ ```
104
+
105
+ Output looks like:
106
+
107
+ ```
108
+ ================================================================================
109
+ Open AWS Scanner — 10 issues found | $472.90/mo potential savings
110
+ ================================================================================
111
+
112
+ EC2_Instance (2 found, $227.50/mo)
113
+ • test-server-bob: CPU avg 2.1% over last 7 days $85.00/mo [us-east-1]
114
+ • legacy-worker-node: CPU avg 1.3% over last 7 days $142.50/mo [us-east-1]
115
+
116
+ EBS_Volume (2 found, $76.80/mo)
117
+ • backup-vol-old: Volume is not attached to any instance $64.00/mo [us-east-1]
118
+ • dev-data-volume: Volume is not attached to any instance $12.80/mo [us-east-1]
119
+
120
+ ────────────────────────────────────────────────────────────────────────────────
121
+ Total potential savings: $472.90/month
122
+ ```
123
+
124
+ **Step 4** — Get JSON output (for piping to other tools):
125
+
126
+ ```bash
127
+ open-aws-scanner scan --output json > findings.json
128
+ ```
129
+
130
+ ---
131
+
132
+ ## How To: Set Up AWS Credentials
133
+
134
+ The scanner needs read-only AWS access. Pick one of these methods:
135
+
136
+ ### Option A: Use your existing AWS profile (simplest)
137
+
138
+ Leave `AWS_ROLE_ARN` blank in `config.env`. The scanner uses whatever credentials are available:
139
+
140
+ - `~/.aws/credentials` (AWS CLI profile)
141
+ - `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` environment variables
142
+ - EC2 instance profile or ECS task role
143
+
144
+ ```env
145
+ AWS_ROLE_ARN=
146
+ AWS_REGIONS=us-east-1
147
+ ```
148
+
149
+ ### Option B: Assume an IAM role (cross-account)
150
+
151
+ Set up a read-only role in the target account (see [IAM setup](#how-to-set-up-the-iam-role)), then:
152
+
153
+ ```env
154
+ AWS_ROLE_ARN=arn:aws:iam::123456789012:role/OpenScannerRole
155
+ AWS_EXTERNAL_ID=my-scanner
156
+ AWS_REGIONS=us-east-1
157
+ ```
158
+
159
+ ### Option C: Explicit credentials
160
+
161
+ ```env
162
+ AWS_ACCESS_KEY_ID=AKIA...
163
+ AWS_SECRET_ACCESS_KEY=wJal...
164
+ AWS_REGIONS=us-east-1
165
+ ```
166
+
167
+ Not recommended for production — use roles or profiles instead.
168
+
169
+ ### Verify credentials work
170
+
171
+ ```bash
172
+ # Quick check that your credentials are valid
173
+ aws sts get-caller-identity
174
+ ```
175
+
176
+ ---
177
+
178
+ ## How To: Scan Multiple Regions
179
+
180
+ Comma-separate regions in `config.env`:
181
+
182
+ ```env
183
+ AWS_REGIONS=us-east-1,us-west-2,eu-west-1,ap-southeast-1
184
+ ```
185
+
186
+ Or override from CLI:
187
+
188
+ ```bash
189
+ open-aws-scanner scan --regions us-east-1,eu-west-1
190
+ ```
191
+
192
+ Each region is scanned independently. Findings are tagged with their region.
193
+
194
+ ---
195
+
196
+ ## How To: Run as a Server
197
+
198
+ Start the API server with automatic scheduled scans:
199
+
200
+ ```bash
201
+ open-aws-scanner serve
202
+ ```
203
+
204
+ This gives you:
205
+
206
+ - Automatic scans every N hours (default: 6, configurable via `SCAN_INTERVAL_HOURS`)
207
+ - REST API for querying findings
208
+ - JSON index page at `/` with status summary
209
+ - Swagger docs at `/docs`
210
+
211
+ Custom host/port:
212
+
213
+ ```bash
214
+ open-aws-scanner serve --host 127.0.0.1 --port 9000
215
+ ```
216
+
217
+ Keep it running in the background:
218
+
219
+ ```bash
220
+ nohup open-aws-scanner serve > scanner.log 2>&1 &
221
+ ```
222
+
223
+ Or with systemd (Linux):
224
+
225
+ ```ini
226
+ # /etc/systemd/system/open-aws-scanner.service
227
+ [Unit]
228
+ Description=Open AWS Scanner
229
+ After=network.target
230
+
231
+ [Service]
232
+ Type=simple
233
+ WorkingDirectory=/opt/scanner
234
+ ExecStart=/usr/local/bin/open-aws-scanner serve
235
+ Restart=always
236
+ Environment=AWS_REGIONS=us-east-1
237
+
238
+ [Install]
239
+ WantedBy=multi-user.target
240
+ ```
241
+
242
+ ---
243
+
244
+ ## How To: Use the API
245
+
246
+ Once the server is running:
247
+
248
+ ### Trigger a scan
249
+
250
+ ```bash
251
+ curl -X POST http://localhost:8000/scan
252
+ ```
253
+
254
+ ### Get all findings
255
+
256
+ ```bash
257
+ curl http://localhost:8000/findings
258
+ ```
259
+
260
+ ### Filter findings
261
+
262
+ ```bash
263
+ # Only open findings
264
+ curl "http://localhost:8000/findings?status=open"
265
+
266
+ # Only EC2 findings
267
+ curl "http://localhost:8000/findings?resource_type=EC2_Instance"
268
+
269
+ # Combine filters
270
+ curl "http://localhost:8000/findings?status=open&resource_type=EBS_Volume"
271
+ ```
272
+
273
+ ### Mark a finding as fixed
274
+
275
+ ```bash
276
+ curl -X PUT "http://localhost:8000/findings/42/status?status=fixed"
277
+ ```
278
+
279
+ Valid statuses: `open`, `fixed`, `dismissed`, `in_progress`
280
+
281
+ ### Get savings summary
282
+
283
+ ```bash
284
+ curl http://localhost:8000/summary
285
+ ```
286
+
287
+ Response:
288
+
289
+ ```json
290
+ {
291
+ "total_findings": 10,
292
+ "total_potential_savings": 472.90,
293
+ "open": { "count": 7, "savings": 380.50 },
294
+ "fixed": { "count": 2, "savings_realized": 67.65 },
295
+ "dismissed": { "count": 0, "savings": 0 },
296
+ "in_progress": { "count": 1, "savings": 24.75 }
297
+ }
298
+ ```
299
+
300
+ ### Get scan history
301
+
302
+ ```bash
303
+ curl http://localhost:8000/scans
304
+ ```
305
+
306
+ ---
307
+
308
+ ## How To: Run with Docker
309
+
310
+ ### Build and run
311
+
312
+ ```bash
313
+ docker build -t open-aws-scanner .
314
+ docker run -p 8000:8000 --env-file config.env open-aws-scanner
315
+ ```
316
+
317
+ ### With AWS credentials from host
318
+
319
+ ```bash
320
+ docker run -p 8000:8000 \
321
+ -e AWS_REGIONS=us-east-1 \
322
+ -e STAGE_MODE=true \
323
+ -v ~/.aws:/root/.aws:ro \
324
+ open-aws-scanner
325
+ ```
326
+
327
+ ### Persist the database
328
+
329
+ ```bash
330
+ docker run -p 8000:8000 \
331
+ --env-file config.env \
332
+ -v $(pwd)/data:/app/scanner.db \
333
+ open-aws-scanner
334
+ ```
335
+
336
+ ---
337
+
338
+ ## How To: Use Stage Mode (No AWS Needed)
339
+
340
+ Stage mode uses mock data — no real AWS credentials required. Perfect for:
341
+
342
+ - Testing the API
343
+ - Developing integrations
344
+ - Demoing the tool
345
+
346
+ Set in `config.env`:
347
+
348
+ ```env
349
+ STAGE_MODE=true
350
+ ```
351
+
352
+ Or as an environment variable:
353
+
354
+ ```bash
355
+ STAGE_MODE=true open-aws-scanner scan
356
+ STAGE_MODE=true open-aws-scanner serve
357
+ ```
358
+
359
+ ---
360
+
361
+ ## How To: Track Savings
362
+
363
+ The scanner tracks finding status over time. A typical workflow:
364
+
365
+ 1. Run a scan → findings are `open`
366
+ 2. Fix a resource (e.g., delete the unused EBS volume in AWS console)
367
+ 3. Mark it fixed:
368
+
369
+ ```bash
370
+ curl -X PUT "http://localhost:8000/findings/42/status?status=fixed"
371
+ ```
372
+
373
+ 4. Check realized savings:
374
+
375
+ ```bash
376
+ curl http://localhost:8000/summary
377
+ # → "fixed": { "count": 3, "savings_realized": 145.25 }
378
+ ```
379
+
380
+ Statuses:
381
+
382
+ | Status | Meaning |
383
+ |--------|---------|
384
+ | `open` | Identified waste, not yet addressed |
385
+ | `in_progress` | Being worked on |
386
+ | `fixed` | Resource cleaned up — savings realized |
387
+ | `dismissed` | Intentionally kept (not waste) |
388
+
389
+ ---
390
+
391
+ ## How To: Use Postgres Instead of SQLite
392
+
393
+ By default the scanner uses SQLite (zero config, file-based). For production or team use, switch to Postgres:
394
+
395
+ **Step 1** — Install the postgres extra:
396
+
397
+ ```bash
398
+ pip install open-aws-scanner[postgres]
399
+ ```
400
+
401
+ **Step 2** — Set `DATABASE_URL` in `config.env`:
402
+
403
+ ```env
404
+ DATABASE_URL=postgresql://user:pass@localhost:5432/scanner
405
+ ```
406
+
407
+ **Step 3** — Create the database:
408
+
409
+ ```bash
410
+ createdb scanner
411
+ ```
412
+
413
+ The tables are created automatically on first run.
414
+
415
+ ---
416
+
417
+ ## How To: Set Up the IAM Role
418
+
419
+ If scanning a different AWS account, create a read-only role:
420
+
421
+ **Step 1** — In the target account, create a role with this trust policy:
422
+
423
+ ```json
424
+ {
425
+ "Version": "2012-10-17",
426
+ "Statement": [
427
+ {
428
+ "Effect": "Allow",
429
+ "Principal": {
430
+ "AWS": "arn:aws:iam::YOUR_SCANNER_ACCOUNT:root"
431
+ },
432
+ "Action": "sts:AssumeRole",
433
+ "Condition": {
434
+ "StringEquals": {
435
+ "sts:ExternalId": "my-scanner"
436
+ }
437
+ }
438
+ }
439
+ ]
440
+ }
441
+ ```
442
+
443
+ **Step 2** — Attach this permissions policy to the role:
444
+
445
+ ```json
446
+ {
447
+ "Version": "2012-10-17",
448
+ "Statement": [
449
+ {
450
+ "Effect": "Allow",
451
+ "Action": [
452
+ "ec2:Describe*",
453
+ "rds:DescribeDBInstances",
454
+ "lambda:ListFunctions",
455
+ "s3:ListAllMyBuckets",
456
+ "s3:ListBucket",
457
+ "sqs:ListQueues",
458
+ "sns:ListTopics",
459
+ "elasticloadbalancing:DescribeLoadBalancers",
460
+ "dynamodb:ListTables",
461
+ "elasticache:DescribeCacheClusters",
462
+ "logs:DescribeLogGroups",
463
+ "logs:DescribeLogStreams",
464
+ "secretsmanager:ListSecrets",
465
+ "cloudwatch:GetMetricStatistics"
466
+ ],
467
+ "Resource": "*"
468
+ }
469
+ ]
470
+ }
471
+ ```
472
+
473
+ **Step 3** — Configure `config.env`:
474
+
475
+ ```env
476
+ AWS_ROLE_ARN=arn:aws:iam::123456789012:role/OpenScannerRole
477
+ AWS_EXTERNAL_ID=my-scanner
478
+ ```
479
+
480
+ **Step 4** — Make sure the machine running the scanner has `sts:AssumeRole` permission for that role ARN.
481
+
482
+ ---
483
+
484
+ ## How To: Verify Package Signatures
485
+
486
+ All releases are signed with [Sigstore](https://www.sigstore.dev/).
487
+
488
+ ### Verify a release
489
+
490
+ ```bash
491
+ pip install sigstore
492
+
493
+ sigstore verify identity open_aws_scanner-0.1.0.tar.gz \
494
+ --cert-identity "luge-sud-0q@icloud.com" \
495
+ --cert-oidc-issuer "https://appleid.apple.com"
496
+ ```
497
+
498
+ ### Verify git commits
499
+
500
+ ```bash
501
+ git log --show-signature
502
+ ```
503
+
504
+ ---
505
+
506
+ ## CLI Reference
507
+
508
+ ```
509
+ open-aws-scanner init
510
+ Create a config.env template in the current directory.
511
+
512
+ open-aws-scanner scan [OPTIONS]
513
+ Run a one-shot scan and print results.
514
+
515
+ --regions REGIONS Comma-separated AWS regions (overrides config)
516
+ --role-arn ARN AWS role ARN to assume (overrides config)
517
+ --output {json,table} Output format (default: table)
518
+ --config PATH Path to config.env (default: ./config.env)
519
+
520
+ open-aws-scanner serve [OPTIONS]
521
+ Start the API server with scheduled scans.
522
+
523
+ --host HOST Host to bind (default: 0.0.0.0)
524
+ --port PORT Port to bind (default: 8000)
525
+ --config PATH Path to config.env (default: ./config.env)
526
+ ```
527
+
528
+ ---
529
+
530
+ ## API Reference
531
+
532
+ | Method | Endpoint | Description |
533
+ |--------|----------|-------------|
534
+ | GET | `/` | Status summary + endpoint list (JSON) |
535
+ | GET | `/health` | Health check |
536
+ | POST | `/scan` | Trigger a scan now |
537
+ | GET | `/findings` | List all findings |
538
+ | GET | `/findings?status=open` | Filter by status |
539
+ | GET | `/findings?resource_type=EC2_Instance` | Filter by resource type |
540
+ | PUT | `/findings/{id}/status?status=fixed` | Update finding status |
541
+ | GET | `/summary` | Savings summary by status |
542
+ | GET | `/scans` | Scan run history |
543
+ | GET | `/docs` | Swagger UI (interactive) |
544
+ | GET | `/redoc` | ReDoc (readable docs) |
545
+
546
+ ---
547
+
548
+ ## What It Scans
549
+
550
+ | Resource | Detection | Est. Savings |
551
+ |----------|-----------|-------------|
552
+ | EBS Volumes | Unattached | $0.08/GB/mo |
553
+ | Elastic IPs | Unassociated | $3.65/mo |
554
+ | EBS Snapshots | Orphaned (source volume deleted, 30+ days) | $0.05/GB/mo |
555
+ | ENIs | Detached | $0 |
556
+ | Security Groups | Not attached to any ENI | $0 |
557
+ | EC2 Instances | CPU avg < 5% over 7 days | — |
558
+ | RDS Instances | < 1 avg connection over 7 days | — |
559
+ | Lambda Functions | Zero invocations in 30 days | $0 |
560
+ | S3 Buckets | Empty (zero objects) | $0 |
561
+ | SQS Queues | Zero messages sent in 14 days | $0 |
562
+ | SNS Topics | Zero publishes in 14 days | $0 |
563
+ | Load Balancers | Zero requests in 7 days | $16.20/mo |
564
+ | NAT Gateways | Zero bytes processed in 7 days | $32.40/mo |
565
+ | DynamoDB Tables | Zero read capacity in 14 days | — |
566
+ | ElastiCache | < 1 avg connection in 7 days | — |
567
+ | CloudWatch Logs | No ingestion in 30 days | $0.03/GB |
568
+ | Secrets Manager | Not accessed in 90 days | $0.40/mo |
569
+
570
+ ---
571
+
572
+ ## Configuration Reference
573
+
574
+ All settings go in `config.env` (or as environment variables):
575
+
576
+ | Variable | Description | Default |
577
+ |----------|-------------|---------|
578
+ | `AWS_ROLE_ARN` | IAM role to assume (blank = use local creds) | *(empty)* |
579
+ | `AWS_EXTERNAL_ID` | External ID for role assumption | *(empty)* |
580
+ | `AWS_REGIONS` | Comma-separated regions to scan | `us-east-1` |
581
+ | `AWS_ACCESS_KEY_ID` | Explicit access key (optional) | *(from env/profile)* |
582
+ | `AWS_SECRET_ACCESS_KEY` | Explicit secret key (optional) | *(from env/profile)* |
583
+ | `SCAN_INTERVAL_HOURS` | Hours between scheduled scans (server mode) | `6` |
584
+ | `STAGE_MODE` | Use mock data (no real AWS calls) | `false` |
585
+ | `DATABASE_URL` | Database connection string | `sqlite:///./scanner.db` |
586
+ | `HOST` | Server bind host | `0.0.0.0` |
587
+ | `PORT` | Server bind port | `8000` |
588
+
589
+ ---
590
+
591
+ ## Relationship to CostOps Platform
592
+
593
+ This is the open-source core of the [CostOps AWS Scanner](../costops-AWS-scanner/) platform. The full platform adds:
594
+
595
+ - Multi-tenant support with per-tenant IAM role assumption
596
+ - Keycloak SSO with JWT zero-trust auth on every endpoint
597
+ - Admin API for tenant/user/billing management
598
+ - React dashboard with drag-and-drop tiles
599
+ - AWS Cost Explorer integration (rightsizing, RI/Savings Plans utilization)
600
+ - Email reports, activity logging, billing model
601
+ - 55+ resource scanners (this package has the core 17)
602
+
603
+ The full scanner imports scanning functions from this package — one codebase, shared logic.
604
+
605
+ ---
606
+
607
+ ## License
608
+
609
+ MIT