Documentation
Complete reference for using tfplan2md in your Terraform workflow.
CLI Reference
Basic Usage
tfplan2md [options] [input-file]
terraform show -json plan.tfplan | tfplan2md
Use tfplan2md with an input file or pipe Terraform JSON to stdin.
Arguments:
input-fileis the path to the Terraform plan JSON file. If omitted, tfplan2md reads from stdin.
Options
Usage Examples
terraform show -json plan.tfplan | tfplan2md
tfplan2md plan.json
tfplan2md --principal-mapping principals.json plan.json
tfplan2md plan.json --report-title "Drift Detection - Production"
tfplan2md plan.json --render-target github
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
--template default
summary
--template summary
Rendering Engine
The report renderer is now implemented in pure C#. Custom Scriban template files are no longer supported.
--template defaultgives you the full report with summary, grouped resource changes, and outputs.--template summarygives you the compact overview for notifications and quick triage.- The built-in engine keeps rendering behavior compile-time checked and consistent across providers.
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:
{
"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.
tfplan2md plan.json --principal-mapping principals.json
docker run -v $(pwd):/data oocx/tfplan2md \
--principal-mapping /data/principals.json \
/data/plan.json --output /data/plan.md
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 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.
# 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
Usage Examples
tfplan2md plan.json --code-analysis-results checkov.sarif
tfplan2md plan.json --code-analysis-results "*.sarif"
tfplan2md plan.json --code-analysis-results "**/*.sarif"
tfplan2md plan.json \
--code-analysis-results "*.sarif" \
--code-analysis-minimum-level warning \
--fail-on-static-code-analysis-errors
Docker Usage
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
- 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
**/*.sariffor recursive search across subdirectories - Set
--code-analysis-minimum-level warningto reduce noise - Combine
--fail-on-static-code-analysis-errorswith 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.
- 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)
<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 azdogithub
```diff
#!/bin/bash
- echo "v1.0"
+ echo "v2.0"
apt-get update
```
--render-target githubMigration 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, notterraform plan - Verify the plan file exists and is a valid Terraform plan
- Check that stdin is not empty when piping from Terraform
# 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 defaultfor the full report - Use
--template summaryfor 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-sensitivefrom your command - Mark attributes as
sensitive = truein 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:
- Open an issue on GitHub with your error message and command
- Check GitHub Discussions for community help
- Review examples for working configurations