Skip to main content

Infrastructure intelligence engine — Terraform audit CLI

Project description

⚡ Opsight

CI Release Python Type checked: mypy strict Lint: ruff License: MIT

Infrastructure Intelligence Engine — a Terraform audit & scaffolding CLI: aggregate security, cost and quality findings into a single graded health report, and scaffold modules and providers from best-practice templates.

Opsight runs a suite of best-in-class scanners against a Terraform directory, normalizes their heterogeneous output into a canonical model, computes a weighted A→F health score, and produces rich terminal, JSON and HTML reports. It also generates Terraform boilerplate — new modules and provider blocks with sane version constraints — straight from the command line.


Table of contents


Features

  • 🔒 Security auditing via Checkov
  • 🔧 Quality / linting via tflint
  • 💰 Cost estimation via Infracost (optional, requires an API key)
  • 📊 Weighted health score per category + a global grade (A → F)
  • 🎯 Prioritized findings — sorted by severity then category importance
  • 📄 Multi-format reports — terminal (Rich), JSON and a self-contained HTML page
  • 🧩 Pluggable scanners — add a new tool by implementing a single run() method
  • ⚙️ Fully configurable via opsight.yaml (enable/disable scanners, weights, penalties)
  • 🚦 CI-friendly exit codes — non-zero when the global score drops below 60
  • 🏗️ Scaffolding — generate modules (module new) and add providers (provider add) with pessimistic ~> constraints resolved from the Terraform Registry
  • 🪄 op alias — every command is also available under the shorter op

How it works

Terraform dir
     │
     ▼
┌─────────────┐   raw dicts   ┌───────────────┐   Finding[]   ┌────────────────┐
│  Scanners   │ ────────────▶ │ Normalization │ ────────────▶ │ Prioritization │
│ tflint      │               │   service     │               │    service     │
│ checkov     │               └───────────────┘               └────────┬───────┘
│ infracost   │                                                        │
└─────────────┘                                                        ▼
                                                              ┌─────────────────┐
                                                              │  Scoring service │
                                                              └────────┬────────┘
                                                                       ▼
                                                        ScanReport → JSON / HTML / terminal
  1. Scan — every enabled scanner runs against the target path and returns raw, tool-specific dicts.
  2. Normalize — raw results are coerced into a canonical Finding model with a unified Severity and Category.
  3. Prioritize — findings are sorted (security first, critical first).
  4. Score — per-category and weighted global scores are computed and clamped to [0, 100].
  5. Report — results are rendered to the terminal and written to JSON / HTML.

Installation

# Clone, then from the package directory (contains pyproject.toml):
python -m venv venv
source venv/bin/activate          # Windows: venv\Scripts\activate

pip install -e .                  # runtime install
pip install -e ".[dev]"           # with dev tooling (pytest, ruff, mypy)

This exposes the opsight command (see [project.scripts] in pyproject.toml).


Prerequisites

Opsight orchestrates external CLI tools — install the ones you need and make sure they are on your PATH:

Tool Purpose Required?
terraform parse / init infra recommended
tflint quality / linting for the quality scanner
checkov security scanning for the security scanner
infracost cost estimation optional — needs INFRACOST_API_KEY env var

Missing tools are skipped gracefully (a warning is shown); Opsight still runs with whatever scanners are available.


Usage

Every command is also available under the shorter op alias — e.g. op scan ./infra, op module new vpc, op provider add aws.

# Scan a Terraform directory (writes JSON + HTML by default)
opsight scan ./infra

# Verbose — show the full findings table in the terminal
opsight scan ./infra -v

# Pick output formats and directory
opsight scan ./infra --format json,html --output ./reports

# Skip specific scanners
opsight scan ./infra --no-cost --no-security

# Use a custom configuration file
opsight scan ./infra --config my-opsight.yaml

# Print the version
opsight version

scan options

Option Alias Default Description
PATH (required) Path to the Terraform directory
--config -c opsight.yaml Path to a configuration file
--output -o . Output directory for reports
--format -f json,html Report formats: json, html, both
--no-cost false Skip the Infracost scanner
--no-security false Skip the Checkov scanner
--verbose -v false Show detailed findings in the terminal

Exit codes

Code Meaning
0 Global score ≥ 60 (grade C or better)
1 Global score < 60, or invalid path

This makes Opsight easy to drop into a CI pipeline as a quality gate.


Scaffolding

Beyond auditing, Opsight scaffolds Terraform boilerplate from versioned templates. All writes are explicit and safe — existing files are never silently merged.

module new <name>

Scaffold a reusable module under <dir>/modules/<name>/:

opsight module new networking          # creates ./modules/networking/
opsight module new vpc --dir infra     # creates ./infra/modules/vpc/
opsight module new vpc --force         # overwrite an existing module

It generates main.tf, variables.tf, outputs.tf, versions.tf (with a terraform { required_version = … } block) and a README.md. The command aborts if the target already exists, unless --force is given. Names must be lowercase kebab- or snake_case — no path separators or .. traversal.

Option Alias Default Description
NAME (required) Module name (kebab- or snake_case)
--dir -d . Base directory that contains modules/
--force false Overwrite an existing module directory

provider add <name> [--version]

Declare a provider the right way — required_providers in versions.tf and the provider "<name>" {} block in providers.tf (each file created if missing):

opsight provider add scaleway                    # resolve latest -> ~> X.Y
opsight provider add scaleway --version "~> 2.5"  # pin a constraint yourself
opsight provider add hashicorp/aws               # explicit namespace/type
opsight provider add scaleway --dir infra        # target another directory

Version resolution. Without --version, Opsight queries the Terraform Registry for the latest stable release and builds a pessimistic constraint — 2.76.1 becomes ~> 2.76 (i.e. >= 2.76.0, < 3.0.0), allowing minor/patch upgrades while blocking breaking majors. A bare name is resolved by trying hashicorp/<name> then <name>/<name>; pass an explicit namespace/type to be precise. If the registry is unreachable, supply --version to work fully offline.

Idempotency. If the provider is already declared, Opsight warns and changes nothing (exit code 3) — never a duplicate entry. When versions.tf already has a required_providers block, the new entry is inserted safely; if no such block can be located, Opsight refuses rather than attempt a fragile HCL merge.

Option Alias Default Description
NAME (required) Provider name or namespace/type
--version (auto) Version constraint, e.g. ~> 2.5
--dir -d . Target Terraform directory

Exit codes

Code Meaning
0 Success
1 Error (invalid name, module exists, registry down)
3 Provider already declared — skipped, nothing changed

terraform fmt is run on the generated files when terraform is on your PATH; otherwise it is skipped silently.


Configuration

Opsight reads an opsight.yaml file (override with --config). Defaults:

# Opsight default configuration
scanners:
  tflint:
    enabled: true
    extra_args: []
  checkov:
    enabled: true
    extra_args: ["--compact", "--quiet"]
  infracost:
    enabled: false          # requires INFRACOST_API_KEY env var
    extra_args: []

scoring:
  base: 100
  weights:
    security: 1.5
    cost: 1.2
    quality: 1.0
    maintainability: 1.0
  penalties:
    low: 1
    medium: 3
    high: 7
    critical: 15

output:
  formats: ["json", "html"]
  directory: "."
  json_filename: "opsight-report.json"
  html_filename: "opsight-report.html"

codegen:
  terraform_required_version: ">= 1.5.0"   # injected into generated versions.tf
  registry:
    base_url: "https://registry.terraform.io"

logging:
  level: "INFO"

Scoring model

Each category starts at base (100) and loses points for every finding:

penalty(finding) = severity_penalty × category_weight
category_score    = max(0, base − Σ penalties in that category)
global_score      = round( Σ(category_score × weight) / Σ weights ), clamped to [0, 100]

Severity penalties (default): low = 1, medium = 3, high = 7, critical = 15.

Category weights (default): security = 1.5, cost = 1.2, quality = 1.0, maintainability = 1.0.

Letter grades (from the global score):

Grade Score range
A 90 – 100
B 75 – 89
C 60 – 74
D 40 – 59
F 0 – 39

Output reports

  • Terminal — a Rich summary table (per-category scores, grade, finding counts) plus an optional detailed findings table with -v.
  • JSONopsight-report.json: machine-readable, full finding detail (id, title, category, severity, source, resource, file, line, description, impact, recommendation).
  • HTMLopsight-report.html: a self-contained page rendered from a Jinja2 template, suitable for sharing or archiving as a CI artifact.

Architecture

Opsight follows a hexagonal (ports & adapters) layout:

opsight/
├── domain/                     # Pure business core (no I/O)
│   ├── models/                 # Finding, Score, ScanResult
│   ├── value_objects/          # Severity, Category
│   ├── services/               # normalization, prioritization, scoring
│   └── codegen/                # ABC ports, models, services, value objects
├── application/                # Orchestration
│   ├── use_cases/              # RunScanUseCase
│   ├── dto/                    # ScanReportDTO
│   └── codegen/                # NewModuleUseCase, AddProviderUseCase
├── infrastructure/             # Adapters to the outside world
│   ├── scanners/               # tflint, checkov, infracost (+ base, factory)
│   ├── terraform/              # executor (init/plan/fmt), parser (python-hcl2)
│   └── codegen/                # jinja renderer, local FS, HTTP registry, tf runner
├── interfaces/
│   └── cli/                    # Typer entry point (main.py) + codegen sub-apps
├── shared/                     # Cross-cutting concerns
│   ├── config/                 # YAML loading
│   ├── logging/                # logger setup
│   └── reporting/              # JSON + HTML reporters
└── templates/                  # report.html.j2 + terraform/ HCL templates

Adding a new scanner: subclass BaseScanner, implement run(path) -> list[dict], set name, and register it in ScannerFactory. The domain layer needs no changes — normalization maps the raw output onto the canonical Finding model.

Codegen keeps the same discipline: business logic (templating decisions, version resolution, idempotency) lives in domain/codegen, while all I/O — filesystem, HTTP registry, the terraform process — sits behind ABC ports (TemplateRenderer, FileSystemPort, RegistryClient, TerraformRunner) with concrete adapters in infrastructure/codegen. HCL templates are versioned under templates/terraform/.


Development

pip install -e ".[dev]"

pytest                 # run the test suite (unit + integration)
pytest --cov=opsight   # with coverage
ruff check .           # lint
mypy opsight           # type-check (strict mode)

Tooling configuration lives in pyproject.toml:

  • ruff — line length 100, target py311
  • mypystrict = true
  • pytesttestpaths = ["tests"], verbose with short tracebacks

Roadmap

  • Scaffolding (Phase A)module new, provider add
  • Additional scanners (e.g. Trivy, OPA/Conftest)
  • SARIF output for native GitHub code-scanning integration
  • Baseline / diff mode (compare against a previous report)
  • Per-finding severity overrides and suppression rules

License

MIT — see pyproject.toml.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

opsight-0.2.0.tar.gz (40.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

opsight-0.2.0-py3-none-any.whl (51.4 kB view details)

Uploaded Python 3

File details

Details for the file opsight-0.2.0.tar.gz.

File metadata

  • Download URL: opsight-0.2.0.tar.gz
  • Upload date:
  • Size: 40.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for opsight-0.2.0.tar.gz
Algorithm Hash digest
SHA256 484245e47334c30013d9434a6b0505af304db6f590c407994db0376351db0a1b
MD5 5289ea026b3fdd4b880b4e110c344aee
BLAKE2b-256 e53e02020fb0f4ac2752e2bb879f4f75bd508f2562afaa9469df60fd944b440d

See more details on using hashes here.

Provenance

The following attestation bundles were made for opsight-0.2.0.tar.gz:

Publisher: release.yml on Trifel-guy/Opsight

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file opsight-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: opsight-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 51.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for opsight-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ce8d2c8fda480ad9ea0696cfbbb5008b2abd9790aa1482ecf4590652034b8e17
MD5 5f6e590881e45517ec8d76402ead28e2
BLAKE2b-256 8a4d322b4a90031122c92cd1cbe037d3edef218a795f1e374401b9d1650cdc78

See more details on using hashes here.

Provenance

The following attestation bundles were made for opsight-0.2.0-py3-none-any.whl:

Publisher: release.yml on Trifel-guy/Opsight

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page