Architecture Overview¶
Cloudrift follows a pipeline architecture where each scan step feeds into the next. The codebase uses Go's modular package system with clear separation between CLI, parsing, AWS integration, detection, policy evaluation, and output formatting.
Pipeline Diagram¶
graph LR
A["CLI Entry<br/>cmd/scan.go"] --> B["Config<br/>Viper + AWS SDK"]
B --> C["Parse Plan<br/>internal/parser/"]
B --> D["Fetch Live State<br/>internal/aws/"]
C --> E["Detect Drift<br/>internal/detector/"]
D --> E
E --> F["Evaluate Policies<br/>internal/policy/"]
F --> G["Format Output<br/>internal/output/"] Tech Stack¶
| Component | Technology | Version | Purpose |
|---|---|---|---|
| Language | Go | 1.24 | Core language |
| CLI Framework | Cobra | 1.10 | Command-line interface |
| Config | Viper | 1.21 | YAML configuration loading |
| AWS SDK | AWS SDK for Go v2 | 1.41 | S3, EC2, IAM, STS API calls |
| Policy Engine | Open Policy Agent | 1.13 | OPA/Rego policy evaluation |
| Testing | Testify | 1.11 | Assertion library |
| Console | fatih/color | 1.16 | Colorized terminal output |
| Progress | briandowns/spinner | 1.23 | CLI progress indicators |
| Concurrency | golang.org/x/sync | 0.19 | errgroup for parallel API calls |
Key Design Decisions¶
Pre-Apply Detection¶
Unlike post-apply tools (driftctl, Terraform Cloud drift detection), Cloudrift operates on the Terraform plan file — before any changes are applied. This enables:
- CI/CD gating before deployment
- Code review with drift context
- Zero risk of modifying infrastructure
Parallel AWS API Calls¶
S3 bucket attributes are fetched concurrently using errgroup.WithContext. Each bucket triggers 7 parallel API calls (ACL, tags, versioning, encryption, logging, public access block, lifecycle), reducing latency.
graph TD
B["Bucket: my-bucket"] --> A1["GetBucketAcl"]
B --> A2["GetBucketTagging"]
B --> A3["GetBucketVersioning"]
B --> A4["GetBucketEncryption"]
B --> A5["GetBucketLogging"]
B --> A6["GetPublicAccessBlock"]
B --> A7["GetLifecycleConfig"] Embedded Policies¶
All 49 .rego policy files are embedded in the binary via Go's //go:embed directive. This means:
- No external files required at runtime
- Policies versioned with the binary
- Custom policies can be loaded alongside built-ins via
--policy-dir
Dynamic Policy Registry¶
Policy metadata (IDs, categories, frameworks) is extracted from .rego files at runtime using regex — never hardcoded. This ensures counts stay accurate as policies are added.
Service-Based Modularity¶
Each AWS service follows the same pattern with dedicated files:
| Component | S3 File | EC2 File | IAM File |
|---|---|---|---|
| Parser | internal/parser/s3.go | internal/parser/ec2.go | internal/parser/iam.go |
| AWS Client | internal/aws/s3.go | internal/aws/ec2.go | internal/aws/iam.go |
| Detector | internal/detector/s3.go | internal/detector/ec2.go | internal/detector/iam.go |
| Printer | internal/detector/s3_printer.go | internal/detector/ec2_printer.go | internal/detector/iam_printer.go |
| Model | internal/models/s3.go | internal/models/ec2.go | internal/models/iam.go |
Adding a new service means creating one file per component and registering it in cmd/scan.go.
Graceful Error Handling¶
AWS API calls for optional attributes (tags, lifecycle, etc.) gracefully handle "not found" errors. For example, NoSuchTagSet or NoSuchLifecycleConfiguration are expected for buckets without those features configured — these are silently ignored rather than causing scan failures.