Documentation

Complete reference for using tfplan2md in your Terraform workflow.

CLI Reference

Basic Usage

Syntax
tfplan2md [options] [input-file]
terraform show -json plan.tfplan | tfplan2md

Use tfplan2md with an input file or pipe Terraform JSON to stdin.

Arguments:

  • input-file is the path to the Terraform plan JSON file. If omitted, tfplan2md reads from stdin.

Options

-o, --output <file>
Write output to a file instead of stdout.
-t, --template <name>
Use a built-in template by name: default or summary. Custom template files are no longer supported.
--report-title <title>
Override the report title (level-1 heading) with a custom value.
-p, --principal-mapping <file>
Map Azure principals, subscriptions, tenants, management groups, and Azure DevOps users, groups, projects, and repositories to readable names.
--render-target <platform>
Target platform for rendering: azuredevops (default, alias: azdo) for styled HTML with line-by-line and character-level diff highlighting, or github for traditional diff format with +/- markers. Note: Replaces deprecated --large-value-format flag.
--details <auto|open|closed>
Control whether resource details start expanded. auto (default) opens resources with findings, open expands all, and closed collapses all.
--show-unchanged-values
Include unchanged attribute values in tables. By default, only changed attributes are shown.
--code-analysis-results <pattern>
Path or wildcard pattern for SARIF 2.1.0 files containing static analysis results, for example *.sarif or **/*.sarif. Security findings are mapped to resources and included in the report.
--code-analysis-minimum-level <level>
Minimum severity level to include in the report. Options: critical, high, medium, low, informational.
--fail-on-static-code-analysis-errors <level>
Exit with code 10 when findings at or above the requested severity exist.
--ignore-azure-id-case-changes
Suppress casing-only Azure resource ID changes. Enabled by default and also applies to AzAPI body properties.
--show-sensitive
Show sensitive values unmasked. By default, sensitive values are masked as 🔒 (sensitive value).
--hide-metadata
Suppress tfplan2md version and generation timestamp from the report header.
--debug
Append diagnostic information to the report for troubleshooting. Includes principal mapping diagnostics and renderer selection details.
-h, --help
Display help message.
-v, --version
Display version information.

Usage Examples

From stdin
terraform show -json plan.tfplan | tfplan2md
From file
tfplan2md plan.json
With principal mapping
tfplan2md --principal-mapping principals.json plan.json
With custom report title
tfplan2md plan.json --report-title "Drift Detection - Production"
GitHub-friendly rendering
tfplan2md plan.json --render-target github
With output file and summary template
tfplan2md plan.json --output plan.md --template summary

Templates and Mapping

tfplan2md ships with built-in report modes and a built-in rendering engine. Use --template to select the layout and --principal-mapping to enrich the rendered values.

Built-in Templates

default

Full report with summary, resource changes grouped by module, output tables, and attribute details. Shows exactly what will change.
--template default

summary

Compact overview showing only action counts and resource type breakdown. Perfect for PR titles or large plans.
--template summary

Rendering Engine

The report renderer is now implemented in pure C#. Custom Scriban template files are no longer supported.

  • --template default gives you the full report with summary, grouped resource changes, and outputs.
  • --template summary gives you the compact overview for notifications and quick triage.
  • The built-in engine keeps rendering behavior compile-time checked and consistent across providers.
Usage
tfplan2md plan.json --template default
tfplan2md plan.json --template summary

Feature 107 removed the old Scriban-based customization path. The main CLI now has zero third-party runtime dependencies, and rendering changes are implemented as built-in C# renderers rather than user-provided .sbn files.

Principal Mapping File

Map Azure principals, subscriptions, management groups, tenants, roles, and Azure DevOps entities to human-readable names for better summaries, tables, and output values. Create a JSON file with this structure:

principals.json
{
  "users": [
    { "id": "12345678-1234-1234-1234-123456789012", "displayName": "jane.doe@contoso.com" }
  ],
  "groups": [
    { "id": "abcdef12-3456-7890-abcd-ef1234567890", "displayName": "Platform Team" }
  ],
  "servicePrincipals": [
    { "id": "11111111-2222-3333-4444-555555555555", "displayName": "terraform-spn" }
  ],
  "subscriptions": [
    { "id": "d1828a48-fced-4ea2-b2ec-4b9623f327fd", "displayName": "Production" }
  ],
  "managementGroups": [
    { "id": "mg-production", "displayName": "Production Workloads" }
  ],
  "tenants": [
    { "id": "tenant-guid", "displayName": "Contoso Corp" }
  ],
  "azdoUsers": {
    "4a2c5e2b-3b4f-4e6f-8a9b-1c2d3e4f5a6b": "John Smith"
  },
  "azdoGroups": {
    "vssgp.Uy0xLTktMTU1MTM...": "Platform Team"
  },
  "azdoProjects": {
    "8f7e6d5c-4b3a-2c1d-0e9f-8a7b6c5d4e3f": "Infrastructure Project"
  },
  "azdoRepositories": {
    "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d": "Infrastructure Repo"
  }
}
  • Azure and Azure AD sections can be exported with Azure CLI.
  • Azure DevOps sections are added manually and support users, groups, projects, and repositories.
  • Repository IDs render with the 🗃️ icon and branch/ref attributes render with ⎇ when mappings are present.
  • Output tables also benefit from these mappings, so repository IDs, resource IDs, and principals remain readable outside resource sections.

Use the --principal-mapping option to replace GUIDs and descriptors with friendly names in role assignment reports, Azure DevOps build definitions, group memberships, and output tables.

Usage
tfplan2md plan.json --principal-mapping principals.json
Docker - Mount from current directory
docker run -v $(pwd):/data oocx/tfplan2md \
  --principal-mapping /data/principals.json \
  /data/plan.json --output /data/plan.md
Docker - Mount as read-only
docker run \
  -v $(pwd)/plan.json:/data/plan.json:ro \
  -v $(pwd)/principals.json:/app/principals.json:ro \
  oocx/tfplan2md --principal-mapping /app/principals.json /data/plan.json
Docker - With debug output
docker run -v $(pwd):/data oocx/tfplan2md --debug \
  --principal-mapping /data/principals.json \
  /data/plan.json --output /data/plan.md

Static Code Analysis

Integrate security findings from Checkov, TfLint, Trivy, and other SARIF-compatible tools directly into your Terraform plan reports.

Overview

tfplan2md natively supports SARIF 2.1.0 format, enabling you to combine infrastructure changes with security findings in a single unified report.

Basic Usage
# Generate Terraform plan
terraform show -json plan.tfplan > plan.json

# Run security tools
checkov -d terraform --framework terraform --output sarif -o checkov.sarif
tflint --format sarif > tflint.sarif
trivy config terraform --format sarif --output trivy.sarif

# Generate unified report
tfplan2md plan.json \
  --code-analysis-results "*.sarif" \
  --output report.md

What You Get

  • Summary Table: High-level overview of findings by severity.
  • Resource Mapping: Findings are automatically linked to affected resources and attributes.
  • Multi-Tool Support: Combine results from multiple security tools in one report.
  • Severity Badges: Visual indicators on resources show security status.

CLI Options

--code-analysis-results <pattern>
Path or wildcard pattern for SARIF files. Supports both simple wildcards (*.sarif) and recursive patterns (**/*.sarif).
--code-analysis-minimum-level <level>
Minimum severity level to include. Options: critical, high, medium, low, informational.
--fail-on-static-code-analysis-errors <level>
Exit with code 10 if findings at or above the requested severity are present.

Usage Examples

Single tool
tfplan2md plan.json --code-analysis-results checkov.sarif
Multiple files with wildcard
tfplan2md plan.json --code-analysis-results "*.sarif"
Recursive search
tfplan2md plan.json --code-analysis-results "**/*.sarif"
Filter severity and fail on errors
tfplan2md plan.json \
  --code-analysis-results "*.sarif" \
  --code-analysis-minimum-level warning \
  --fail-on-static-code-analysis-errors

Docker Usage

Mount directory with SARIF files
docker run -v $(pwd):/data oocx/tfplan2md \
  /data/plan.json \
  --code-analysis-results "/data/**/*.sarif" \
  --output /data/report.md

Supported Tools

tfplan2md works with any tool that outputs SARIF 2.1.0 format. Here are some popular options:

Checkov

Infrastructure security scanning with 1000+ built-in policies.

checkov -d terraform \
  --framework terraform \
  --output sarif \
  -o checkov.sarif

TfLint

Terraform linter with pluggable rules for AWS, Azure, and GCP.

tflint \
  --format sarif \
  > tflint.sarif

Trivy

Comprehensive security scanner for IaC misconfigurations, vulnerabilities, and secrets.

trivy config terraform \
  --format sarif \
  --output trivy.sarif

CI/CD Integration Example

GitHub Actions Workflow
- name: Run security scans
  run: |
    checkov -d terraform --framework terraform \
      --output sarif -o checkov.sarif --compact

    cd terraform && tflint --format sarif > ../tflint.sarif && cd ..

    trivy config terraform --format sarif --output trivy.sarif

- name: Generate unified report
  run: |
    terraform show -json plan.tfplan | \
      docker run -i -v $(pwd):/data oocx/tfplan2md \
        --code-analysis-results "/data/**/*.sarif" \
        --fail-on-static-code-analysis-errors \
        > plan.md

💡 Tips

  • Use **/*.sarif for recursive search across subdirectories
  • Set --code-analysis-minimum-level warning to reduce noise
  • Combine --fail-on-static-code-analysis-errors with CI/CD to block deployments
  • Security findings persist in the report even if resources haven't changed

Azure DevOps Integration

When tfplan2md writes to a file in Azure Pipelines, it can also emit a pipeline variable that tells later steps whether the plan contains meaningful changes.

Pipeline Variable

When you use -o or --output in Azure DevOps, tfplan2md emits:

  • ##vso[task.setvariable variable=tfplan2md_haschanges]true
  • ##vso[task.setvariable variable=tfplan2md_haschanges]false

This makes it easy to conditionally post PR comments, upload artifacts, or skip follow-up stages when the rendered report says there are no changes.

Azure Pipelines
- script: |
    terraform show -json plan.tfplan | tfplan2md --output plan.md
  displayName: Generate tfplan2md report

- script: echo "Only run when the plan changed"
  condition: eq(variables['tfplan2md_haschanges'], 'true')
  displayName: Conditional follow-up step

Render Targets

The --render-target flag controls platform-specific rendering behavior. Attributes with newlines or over 100 characters are automatically moved to collapsible sections below the main table.

azuredevops (default)

Styled HTML with line-by-line and character-level diff highlighting. Optimized for Azure DevOps PR comments.
Output Example
<pre style="font-family: monospace; line-height: 1.5;"><code>#!/bin/bash
<span style="background-color: #fff5f5; color: #24292e;">echo "v1.0"</span>
<span style="background-color: #f0fff4; color: #24292e;">echo "v2.0"</span>
apt-get update
</code></pre>
--render-target azuredevops or --render-target azdo

github

Traditional diff format with +/- markers. Fully portable and works on both GitHub and Azure DevOps.
Output Example
```diff
  #!/bin/bash
- echo "v1.0"
+ echo "v2.0"
  apt-get update
```
--render-target github

Migration note: The --large-value-format flag has been deprecated and replaced by --render-target. Use --render-target azuredevops for inline-diff behavior or --render-target github for standard-diff behavior.

Troubleshooting

❌ "No valid JSON detected in input"

This error occurs when tfplan2md receives input that is not valid Terraform JSON.

Solution:

  • Ensure you're using terraform show -json plan.tfplan, not terraform plan
  • Verify the plan file exists and is a valid Terraform plan
  • Check that stdin is not empty when piping from Terraform
Correct usage
# Generate plan file
terraform plan -out=plan.tfplan

# Convert to JSON and pipe to tfplan2md
terraform show -json plan.tfplan | tfplan2md

❌ Unknown template name

This error occurs when `--template` is set to a name other than the built-in `default` or `summary` modes.

Solution:

  • Use --template default for the full report
  • Use --template summary for the compact overview
  • Custom template files are no longer supported

⚠️ Sensitive values still visible

Sensitive values are masked by default unless `--show-sensitive` is used.

Solution:

  • Remove --show-sensitive from your command
  • Mark attributes as sensitive = true in Terraform
  • Review CI/CD configuration to ensure sensitive flags aren't enabled accidentally

⚠️ Docker permission denied

Docker commands fail with permission errors.

Solution:

  • Add your user to the docker group: sudo usermod -aG docker $USER
  • Log out and back in for group changes to take effect
  • Or run with sudo (not recommended for CI/CD)

💡 Need more help?

Solution: