IaC Config Scanning and SBOM
.mdVerified against Trivy 0.68.2, flags verified via `trivy config --help` / `trivy sbom --help` run · official docs
What it is and where it fits#
Trivy's config command covers Terraform, CloudFormation, Kubernetes manifests, Helm charts, Dockerfiles and
Azure ARM in one scanner — this is what absorbed tfsec's entire check library after Aqua put tfsec into
maintenance mode (see the tfsec cheat sheet). Trivy's sbom command closes the loop with Syft/Grype's
generate-then-scan pattern, but from a single tool: Trivy can both generate an SBOM from an image and later
re-scan that saved SBOM for newly published vulnerabilities without ever touching the original image again.
See the companion page for image/filesystem vulnerability scanning.
Config / IaC misconfiguration scanning#
trivy config . # auto-detects Terraform/K8s/CloudFormation/Dockerfile/Helm in the directory
trivy config --severity CRITICAL,HIGH .
trivy config --exit-code 1 --severity CRITICAL,HIGH . # gate CI — exit code defaults to 0 here too
trivy config main.tf # a single fileSample output shape (illustrative — exact check IDs/messages evolve with the checks-bundle version Trivy downloads):
main.tf (terraform)
====================
Tests: 24 (SUCCESSES: 20, FAILURES: 4, EXCEPTIONS: 0)
Failures: 4 (LOW: 0, MEDIUM: 1, HIGH: 2, CRITICAL: 1)
CRITICAL: Bucket does not have logging enabled.
════════════════════════════════════════
S3 bucket does not have logging enabled, exposing it to audit-trail gaps.
────────────────────────────────────────
main.tf:3-8
────────────────────────────────────────
3 ┌ resource "aws_s3_bucket" "example" {
4 │ bucket = "my-bucket"
5 └ }
────────────────────────────────────────
Scoping to a specific IaC type#
trivy config --misconfig-scanners terraform .
trivy config --misconfig-scanners kubernetes .
trivy config --misconfig-scanners dockerfile,cloudformation .Helm chart scanning#
trivy config ./mychart \
--helm-set replicaCount=3 \
--helm-values ./mychart/values-prod.yamlTrivy renders the chart with the given values (the same way helm template would) before scanning the
resulting manifests — a chart with security-sensitive defaults that get overridden safely in your actual
values file won't produce false-positive findings, as long as you point Trivy at the real values file.
Compliance report mode#
trivy image --compliance docker-cis-1.6.0 myapp:latest # map findings to a CIS Docker Benchmark reportSBOM: generating one from an image#
trivy image --format cyclonedx --output sbom.cdx.json myapp:latest
trivy image --format spdx-json --output sbom.spdx.json myapp:latestSBOM: scanning an existing SBOM for vulnerabilities#
trivy sbom sbom.cdx.json
trivy sbom /path/to/report.cdx.intoto.jsonl # a signed/attested CycloneDX documentReal-world scenario: same-day response to a newly published CVE, no re-scan needed#
This is the "instant search across every artifact we've ever built" use case: if you've been saving a CycloneDX SBOM alongside every release, a newly published CVE against a library you've shipped in the past can be checked instantly against every historical SBOM, without re-pulling or re-scanning the original images:
for sbom in releases/*/sbom.cdx.json; do
echo "=== $sbom ==="
trivy sbom "$sbom" --severity CRITICAL
doneAt real scale this is exactly the job a tool like Dependency-Track automates continuously (see the DevSecOps tutorial series' supply-chain chapter) — the loop above is the same idea done by hand for a small number of saved SBOMs.
Ignoring specific findings#
# .trivyignore in the scan root
CVE-2023-12345
AVD-AWS-0001 # a specific misconfig check ID, with an optional expiry: exp:2026-12-31trivy config --ignorefile .trivyignore.yaml . # override the default .trivyignore filenameThe plain .trivyignore format is a flat ID-per-line list; a .trivyignore.yaml variant supports structured
per-finding metadata (expiry dates, statements) if you need an auditable reason attached to each suppression
— worth the switch the moment a security review asks "why is this ignored."
Real-world scenario: GitHub Actions IaC + SBOM recipe#
# .github/workflows/trivy-iac-sbom.yml
name: Trivy IaC + SBOM
on: [pull_request]
jobs:
iac:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aquasecurity/trivy-action@0.28.0
with:
scan-type: config
scan-ref: .
exit-code: '1'
severity: 'CRITICAL,HIGH'
sbom:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- uses: aquasecurity/trivy-action@0.28.0
with:
image-ref: myapp:${{ github.sha }}
format: cyclonedx
output: sbom.cdx.json
- uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.cdx.jsonFull config-scan flag reference#
| Flag | What it does |
|---|---|
--misconfig-scanners | Comma-separated list of which IaC types to scan (default: azure-arm, cloudformation, dockerfile, helm, kubernetes, terraform, terraformplan-json, terraformplan-snapshot) |
--tf-vars | Path(s) to .tfvars files to load when evaluating Terraform — needed for variables that affect security-relevant attributes |
--tf-exclude-downloaded-modules | Skip findings inside anything Terraform downloaded into .terraform/modules |
--k8s-version | Kubernetes version to validate deprecated/removed API usage against |
--helm-set / --helm-set-string / --helm-set-file | Same semantics as helm template's equivalent flags — override chart values before scanning the rendered output |
--helm-values | Path(s) to override values.yaml files |
--compliance | Map findings to a named compliance report (e.g. docker-cis-1.6.0) |
--report | all or summary — how much detail the compliance report includes |
--config-check | Path to custom Rego check files/directories to load alongside the built-ins |
--config-data | Path(s) providing data available to custom Rego checks |
--include-deprecated-checks | Include checks Aqua has since deprecated — useful when comparing historical scan results |
--file-patterns | Override which file patterns are treated as config files worth scanning |
Troubleshooting#
"Terraform scan shows false positives for a resource whose real config comes from a variable" — pass
--tf-vars path/to/terraform.tfvars explicitly; Trivy doesn't automatically discover every possible
.tfvars file the way terraform plan does with its precedence rules.
"Helm chart scan doesn't reflect my production values" — same root cause as the Terraform case: Trivy
renders the chart with whatever values you give it via --helm-values/--helm-set; without them, it scans
the chart's default values, which are frequently more permissive than what actually deploys.
"SBOM scan says 0 vulnerabilities but I know the image has some" — confirm the SBOM was generated
before the vulnerability in question was published to the DB, or that trivy sbom isn't using a stale
cached DB (trivy image --download-db-only refreshes it).
Real-world scenario: compliance report for an audit#
A team preparing for a CIS Docker Benchmark review needs a structured report mapping technical findings to specific benchmark controls, not just a raw vulnerability list:
trivy image --compliance docker-cis-1.6.0 --report summary myapp:latestThis reframes the same underlying scan data around the benchmark's own control numbering — the shape an auditor actually expects to see, rather than a generic CVE table they'd need to manually cross-reference against the benchmark themselves.
Common pitfalls#
- Scanning raw Terraform without expected variables — misconfig checks can produce false positives/negatives
if a resource's security-relevant attribute is only set via a
.tfvarsfile Trivy didn't load; pass--tf-varsexplicitly when a project relies on external variable files. - Confusing
.trivyignorescope — it applies per invocation directory; a monorepo with several IaC roots needs either one ignore file per root or an explicit--ignorefilepath per scan. - Forgetting
--exit-code 1here too — the config scanner shares Trivy's global "reports, doesn't gate by default" behavior.
When to reach for something else#
For Terraform specifically, tfsec's check library is now literally sourced from Trivy, so there's no functional gap to fill by adding tfsec alongside Trivy — pick one. Checkov remains meaningfully different: it covers a few IaC ecosystems Trivy doesn't (notably Ansible and Bicep as of this writing) and has its own distinct check catalog worth cross-checking on a security-critical project rather than assuming full overlap.