How it Works
This document describes the full execution flow of cloudrift analyze, from CLI invocation to rendering results.
Execution flow
user: cloudrift analyze -r us-east-1 eu-west-1 [--format json] [--pdf] [--live-pricing]
|
v
main.ts (Commander.js parses arguments)
|
v
analyze-waste.command.ts (orchestrates options/config/output)
1. loadConfig() — cloudrift.config.json / .cloudriftrc / --config
2. AwsRegion.parse() for each region
3. accountId: --account-id or STS GetCallerIdentity
|
v
analyze-waste.composition.ts (composition root)
4. Pricing: static table <- live API (--live-pricing) <- config.prices (wins)
5. Builds scanners from declarative registries
6. Filters by --scanners / --all-services / interactive wizard
|
v
AnalyzeCloudWasteUseCase.execute({ regions })
Worker pool: max 12 scans in-flight (scanner x region)
|
v
Each scanner: AWS API -> domain entity -> waste policy -> findings
|
v
WastedResourcesSummary -> chosen format (table/json/markdown/PDF)
totalWasteMonthlyUsd > costAlertThresholdUsd -> exit code 2 (CI gate)
How scanners work
Every scanner follows the same pattern:
- Creates an AWS client for the region (with retry/backoff and timeouts)
- Lists candidates with pagination, pre-filtering server-side where possible
- Fetches CloudWatch metrics (for idle/underutilized scanners) with limited concurrency (max 5)
- Maps to domain entities calculating cost via
PricingPort - Applies the waste policy — grace period, exclusion tags, type-specific criteria
- Destroys the client in a
finallyblock
23 of 44 scanners extend a shared template method (CloudWatchIdleScanner) that owns this lifecycle. The other 21 implement the same pattern directly.
Price resolution
Costs are resolved per (region, key) from three layers; the most specific wins:
- Config
pricesoverrides — negotiated/enterprise rates (highest priority) - AWS Pricing API (
--live-pricing) — current public list prices - Built-in static table (
prices.json) — always present as fallback
Every report shows prices as of to indicate which layer was used.
Even with
--live-pricing, AWS returns list prices, not your bill. Configpricesoverrides are the only way to match the report to what you actually pay.
Concurrency
- Global worker pool: max 12 scans in-flight (configurable via
CLOUDRIFT_SCAN_CONCURRENCY), any scanner/region mix - Internal fan-out: CloudWatch calls limited to 5 concurrent per scanner (prevents throttling)
- Scanner-major queue order: the first batch spreads across regions instead of concentrating on the first one
Error handling
Error granularity is per (scanner, region): a missing permission in one region produces a warning for that pair without affecting anything else. The report is always returned with partial data and a “Scan Warnings” section.
The exit code is driven only by the cost threshold (costAlertThresholdUsd), never by scan errors.
AWS credentials
AWS SDK v3 uses the default credential chain:
- Environment variables (
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY) ~/.aws/credentialsfile (defaultprofile orAWS_PROFILE)- IAM Instance Profile (on EC2)
- ECS Task Role / EKS Service Account
For cross-account scanning, --assume-role-arn assumes the target role via STS before any AWS call.
Output formats
--format table(default) — colored terminal tables--format json— machine-readable report on stdout (composable withjq)--format markdown— ready for PR comments / GitHub step summary--pdf [filename]— additional PDF artifact on disk--json [filename]— additional JSON artifact on disk--silent— suppresses all stdout (for file-only runs)