Architecture

Technical architecture based on arc42 documentation standard. Clean separation, extensible design, security-first.

System Overview

tfplan2md converts Terraform plan JSON into Markdown reports optimized for pull request reviews.

📄

Input

Terraform Plan JSON

terraform show -json
⚙️

tfplan2md

Parse · Transform · Render

📝

Output

Markdown Report

GitHub / Azure DevOps PR

Quality Goals

Priority 1

Security

Mask sensitive values by default · FROM scratch container · AOT-compiled static binary · Non-root user (UID 1654) · No shell · Minimal attack surface

Priority 2

Reliability

Handle malformed JSON gracefully · Validate all markdown output · Comprehensive test coverage

Priority 3

Usability

Simple CLI · Sensible defaults · Clear error messages · Zero configuration needed

Priority 4

Maintainability

Clean architecture · Immutable models · Pure functions · Modern C# patterns

Priority 5

Extensibility

Custom templates · Resource-specific renderers · Provider-specific handling

Priority 6

Performance

Fast startup for CI/CD · Handle large plans efficiently · 14.7MB Docker image (89.6% reduction) · AOT compilation with aggressive trimming

Core Components

Clean separation of concerns with immutable data models and pure transformation functions.

🎯

CLI

Command-line parsing and orchestration. Handles user input, loads configuration, coordinates workflow.

CliParser.cs HelpTextProvider.cs
📦

Parsing

Terraform plan JSON parsing into immutable domain models using System.Text.Json.

TerraformPlan.cs TerraformPlanParser.cs
🔄

Model Building

Transform domain models into report models. Build resource summaries, group by module, apply inline diffing.

ReportModel.cs Summaries/*.cs
✍️

Rendering

Apply Scriban templates to generate markdown. Supports default templates, summary template, and custom user templates.

MarkdownRenderer.cs ScribanHelpers.cs Templates/*.sbn
☁️

Azure Utilities

Azure-specific functionality: principal ID mapping, resource ID parsing, role assignment formatting.

PrincipalMapper.cs azurerm/* templates
🔒

Security

Sensitive value detection and masking. AOT-compiled static binary in FROM scratch container. Non-root user, no shell, minimal dependencies.

--show-sensitive flag FROM scratch + 3 musl libs

Technology Stack

Compilation
NativeAOT (linux-musl-x64)
Ahead-of-time compilation to native executable with aggressive trimming
Language
C# 13
Modern language features: records, pattern matching, file-scoped namespaces
JSON Parser
System.Text.Json
Parse Terraform plan JSON with built-in .NET library
Template Engine
Scriban 6.5.2
Render markdown from customizable templates
Container Base
FROM scratch
Empty base with 3 musl libraries (14.7MB) - minimal attack surface, no shell, non-root user
Test Framework
TUnit 1.9.26
Unit and integration tests with comprehensive coverage, async-first design, and real-time progress reporting
Markdown Linter
markdownlint-cli2 0.20.0
Validate markdown output for GitHub/Azure DevOps compatibility

Architectural Patterns

Immutability

All data models are immutable records. No mutable shared state. Pure functions for transformations.

✅ Thread-safe, predictable, easier to reason about

Template-Driven

Default templates embedded as resources. Custom templates from filesystem. Resource-specific overrides.

✅ Flexible, customizable, user-controlled formatting

Separation of Concerns

Clear boundaries: Parsing → Model Building → Rendering. Each component has single responsibility.

✅ Testable, maintainable, modular

Security by Default

Sensitive values masked unless explicitly shown. FROM scratch containers with AOT-compiled static binaries. Non-root user, no shell, minimal dependencies.

✅ Safe for CI/CD, minimal attack surface, sub-second startup

Architecture Decision Records

Key technical decisions and architectural patterns that shape tfplan2md. These guide consistent implementation across features.

Scriban for Templating

Lightweight, text-focused template engine with familiar syntax. Better fit than Razor or Liquid for markdown generation. Embeddable, supports custom functions, and works seamlessly with AOT compilation.

Decision: Use Scriban for all markdown generation. Enable user customization through filesystem templates. Provide composable partials for clean template organization.

NativeAOT with FROM scratch

Ahead-of-time compilation produces static native executable (linux-musl-x64). Deployed in FROM scratch container with only 3 musl libraries. Achieves 14.7MB image size (89.6% reduction from baseline) with fastest possible startup time.

Benefits: Minimal attack surface, no shell, non-root user (UID 1654), sub-second startup in CI/CD. Build-time resource embedding with custom ResourceManager avoids runtime reflection.

Modern C# 13 Patterns

Records for immutable data models, file-scoped namespaces, nullable reference types, pattern matching. Comprehensive XML documentation (including private members) for AI-assisted development.

Benefits: Thread-safe, predictable, easier to reason about. Supports 100% AI-assisted development workflow with GitHub Copilot multi-model approach.

CSS Layers for Website Examples

CSS @layer directive isolates rendered example styles from website page styles. Examples use @layer examples with lower cascade priority than website styles.

Benefits: Prevents specificity conflicts. Examples remain readable while website styles always win conflicts. Clean separation between content and presentation layers.

Resource-Specific Templates with ViewModels

Complex resources (firewall rules, NSG rules, role assignments, variable groups) use specialized templates with ViewModel pattern. C# Factory precomputes semantic diffs, matches items by key (e.g., rule name), formats before/after comparisons.

Benefits: Testable C# logic for complex merging/matching. Clean templates iterate preformatted rows. Powerful semantic diffing without Scriban limitations.

Single-Pass Template Rendering

Direct template dispatch replaces render-then-replace pattern. Main template orchestrates with composable partials (_header.sbn, _resource.sbn, _footer.sbn). Resource-specific templates override default partial.

Benefits: No anchors or regex replacement. Explicit, debuggable template selection. No wasted computation. Scriban include with custom template loader.

Markdig for Platform-Specific HTML

Standalone HtmlRenderer tool converts markdown to HTML with GitHub and Azure DevOps flavors. Uses Markdig with platform-specific pipelines. GitHub strips style attributes; Azure DevOps preserves for theme support.

Use Case: Visual testing, website examples, screenshot generation. Wrapper templates with {{content}} placeholder. Not pixel-perfect but "good enough approximation" for development workflows.

Playwright for Visual Regression

ScreenshotGenerator tool uses Playwright + Chromium for automated screenshot capture. Supports full-page capture, element targeting by Terraform resource address or CSS selector, multiple formats (PNG/JPEG).

CI Integration: Cross-platform, headless-only, actionable error messages. Union bounding box for multiple element matches. Enables visual regression testing pipelines.

DiagnosticContext for Debug Output

Optional diagnostics collected throughout pipeline via DiagnosticContext passed to components. Single --debug flag appends section showing principal mapping status, template resolution, failed ID lookups.

Benefits: Non-intrusive (zero impact when disabled). Clean separation (diagnostics separate from business logic). Flexible (easy to add new categories). Matches immutable architecture patterns.

Report Metadata for Build Traceability

Every report includes metadata line: tfplan2md version, git commit hash (7 chars), generation timestamp UTC, Terraform version. Format: Generated by tfplan2md X.Y.Z (abc1234) on YYYY-MM-DD HH:MM:SS UTC | Terraform 1.x.x

Benefits: Audit trail for debugging. Mockable MetadataProvider for deterministic tests. Suppressible with --hide-metadata flag.

TUnit for Async-First Testing

Modern test framework with true async support, real-time progress reporting, parallel execution. Comprehensive test coverage (unit, integration, snapshot) with fast feedback loops.

Benefits: Faster than MSTest/xUnit. Native async/await support. Better diagnostics. Aligns with modern C# patterns and AI-assisted development workflow.

Terraform Show Approximation

Development script generates plan JSON from config + state without terraform plan execution. Constructs synthetic plan structure for rapid iteration on resource-specific templates.

Use Case: Rapid development workflow. Test templates without full plan cycle. Note: Approximation only; not suitable for production use.

Complete Architecture Documentation

This page provides a high-level overview. For complete details including building block views, runtime scenarios, deployment architecture, and design decisions, see the full arc42 documentation.