Docs navigation

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:

  1. Creates an AWS client for the region (with retry/backoff and timeouts)
  2. Lists candidates with pagination, pre-filtering server-side where possible
  3. Fetches CloudWatch metrics (for idle/underutilized scanners) with limited concurrency (max 5)
  4. Maps to domain entities calculating cost via PricingPort
  5. Applies the waste policy — grace period, exclusion tags, type-specific criteria
  6. Destroys the client in a finally block

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:

  1. Config prices overrides — negotiated/enterprise rates (highest priority)
  2. AWS Pricing API (--live-pricing) — current public list prices
  3. 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. Config prices overrides 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:

  1. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
  2. ~/.aws/credentials file (default profile or AWS_PROFILE)
  3. IAM Instance Profile (on EC2)
  4. 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 with jq)
  • --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)