Architecture Overview¶
Purpose¶
tf-peek is a command-line tool that parses Terraform plan JSON files and generates
human-readable Markdown reports. It is designed to make reviewing terraform plan
output easier by producing a structured summary with per-resource diffs.
Core Principles¶
- Single responsibility: a single command that performs one task — convert a plan to a report.
- Offline / local-only: no network calls, no databases. All processing happens on local files.
- Declarative configuration: a TOML file (
peek_config.toml) assigns per-repository resource tiers (silent/normal/critical) and, fornormal, a detail level (full/summary). Seeresource-tier-configfor the rule format. - Structured data ingestion: the Terraform plan JSON is validated at parse time using Pydantic models.
- Separation of concerns: parsing, business logic, configuration, and rendering are each in their own module.
Code Organization¶
src/tf_peek/
├── __init__.py # Package marker
├── __main__.py # Module entrypoint (python -m tf_peek)
├── cli.py # CLI definition and top-level orchestration
├── actions.py # Terraform action vocabulary shared by the report and the CLI
├── diff.py # Semantic diffing of before/after values and their markers
├── formatting.py # Rendering of semantic diff values into Markdown table cells
├── report.py # Aggregation of a plan into report data, and template rendering
├── models.py # Pydantic models for the Terraform plan JSON
├── config.py # Configuration loading (TOML → PeekConfig)
└── templates/
└── report.md.j2 # Jinja2 template that renders the Markdown report
Processing Pipeline¶
tf-peek follows a linear pipeline:
- Load configuration — reads an optional
peek_config.toml(or a path supplied via--config). - Parse plan — deserializes the Terraform plan JSON into typed Pydantic models. The plan is
read from either a file (
JSON_PATH) or stdin (JSON_PATHgiven as-), decoded as UTF-8 explicitly in both cases so parsing doesn't depend on the ambient process locale. A missing or unreadable file, undecodable bytes, malformed JSON, or JSON that doesn't match the expected structure produces a one-line diagnostic and exit1, rather than an uncaught traceback. - Classify actions — each
ResourceChangeis mapped to a simplified action:create,update,delete,replace, orno-op.no-opandreadresources are excluded. - Classify tier — each remaining resource is matched against the configured
[[resources]]rules (by exactmatch_typeor regexmatch_pattern) and assigned a tier (silent/normal/critical) and, fornormal, adetaillevel (full/summary). Acriticalresource whose action is in that rule'scritical_onlist is routed into the report's 🚨 Critical Changes section instead of the normal resource list. - Compute / emit diffs —
silent-tier resources are only counted: they never reach the diff pipeline and never appear in any rendered entry.summary-detail resources are rendered as a title-only collapsible entry (no attribute table, no(known after apply)markers); only thecalculate_diffstep is skipped. For everything else, before/after attribute values are compared; Terraform'safter_unknownmarkers are resolved recursively into theaftervalue, so a nested unknown leaf becomes(known after apply)inside its containing structure instead of being lost. - Mask sensitive values — an attribute whose
before_sensitive/after_sensitivesubtree has any truthy leaf is replaced with(sensitive value)on both sides, unless--show-sensitiveis passed. Masking runs before any formatting so no serialization path can bypass it. - Format cells — every remaining value passes through one canonical formatter that emits compact JSON and escapes the Markdown table delimiter and the code-span backtick. The template receives display strings only, so no branch of it can re-derive a value differently.
- Render template — data is passed to a Jinja2 template that produces the final Markdown.
- Output — the completed Markdown is written to a file (if
--outputis specified) or emitted literally to stdout viatyper.echo. Both destinations pin UTF-8 with LF endings, so their bytes are identical regardless of the ambient locale.
Key Dependencies¶
| Library | Role |
|---|---|
typer |
CLI argument parsing, command definition, and literal stdout emission |
pydantic |
Runtime validation and typing of Terraform plan JSON |
jinja2 |
Markdown report templating |
tomllib |
Standard library TOML parser for reading peek_config.toml |