Your platform team is replacing tfsec with Trivy 0.74 for Terraform checks — what carries over unchanged, and what must you rework?
answer
- same engine, new home
- checks shipped as a bundle
- two comment prefixes still parsed
- IDs: short, AVD, long, alias
- custom checks need loading flags
basics
~20 sTrivy carries tfsec's Terraform engine and still parses tfsec:ignore comments with their :exp: expiries. Rework the command, the exit code, ignore IDs that no longer match, repo-wide exclusions, and custom checks, which Trivy loads as Rego.
solid answer
~40 stfsec's README says its scanning is being consolidated into Trivy; tfsec laid the foundation of Trivy's IaC scanning and stays available, but development goes into Trivy. So the Terraform evaluation and most checks carry over: you swap `tfsec ./infra` for `trivy config ./infra`. Trivy 0.74 still parses `tfsec:ignore:` comments alongside `trivy:ignore:`, including the `:exp:yyyy-mm-dd` expiry, and matches an ignore against a check's ID, AVD ID, long ID or alias, case-insensitively. What you rework: the checks now arrive as the trivy-checks bundle pulled over the network, so egress or a mirror matters; Trivy exits 0 on findings unless you set `--exit-code`; some long IDs differ, so every carried-over comment needs a test run; repo-wide exclusions move to Trivy's ignore files; and custom checks are loaded as Rego with `--config-check` and `--check-namespaces`.
code
hcl · 9 lines#tfsec:ignore:aws-s3-enable-bucket-logging:exp:2026-12-31
resource "aws_s3_bucket" "audit" {
bucket = "audit-logs-example"
}
#trivy:ignore:aws-s3-enable-logging:exp:2026-12-31
resource "aws_s3_bucket" "reports" {
bucket = "reports-example"
}go deeper
Know that tfsec's scanning now lives in Trivy and that trivy config is the replacement command for a Terraform directory.
Explain which identifiers an ignore can name, that both comment prefixes are parsed, and what --config-check and --check-namespaces do.
Run the migration without a coverage gap: side-by-side diff, ignore verification, exit code, bundle reachability, then remove tfsec.
Decide when to cut over a fleet of repositories: a shared invocation, a tolerance window for ID drift, and who signs off the diff.
## Where tfsec stands **tfsec** was Aqua Security's Terraform static-analysis tool. Its repository now carries the banner "Tfsec is now part of Trivy": scanning work is being consolidated into Trivy, tfsec "laid the foundations" of Trivy's IaC and misconfiguration scanning, and tfsec remains available for the time being while engineering attention goes to Trivy. The repository is not archived, but it is no longer where checks evolve, so a migration is about moving to the maintained path, not escaping a dead tool overnight. ## What carries over - **The Terraform evaluation.** Trivy's Terraform scanner descends from tfsec's: HCL parsing, variable and module evaluation, `for_each` and `count` expansion. - **The check catalogue, in lineage.** tfsec's Terraform checks became part of the **trivy-checks** library, now mostly Rego with some Go. - **Inline ignore comments.** Trivy 0.74's ignore parser accepts both `tfsec:ignore:<id>` and `trivy:ignore:<id>` as prefixes, so existing comments are still read. The `:exp:yyyy-mm-dd` expiry suffix works the same way, and Trivy also supports a `:ws:` workspace section and wildcards. - **Variable files.** Values reach the scan through `--tf-vars` files and `TF_VAR_` variables. ## What you must rework | Area | tfsec habit | Trivy 0.74 | |---|---|---| | Command | `tfsec ./infra` | `trivy config ./infra`, or `--scanners misconfig` on `fs` and `repo` | | Check delivery | shipped with the tool | trivy-checks bundle pulled from `mirror.gcr.io/aquasec/trivy-checks:2`, cached, refreshed every 24 hours | | Failing the job | your existing wrapper | add `--exit-code 1`; the default exit code is 0 even with findings | | Excluding checks | `-e check1,check2` | Trivy's ignore files (`.trivyignore`, `.trivyignore.yaml`), part of the CI wiring | | Custom checks | whatever the old job loaded | Rego checks, loaded with `--config-check` and `--check-namespaces` | ### Identifiers A Trivy check has several identifiers, and an ignore rule may name any of them: - the **ID** shown in the report (`ID` in JSON; the older `AVDID` field is deprecated in 0.74), with AVD-style forms such as `AVD-AWS-0089`; - a **long ID** built as `<provider>-<service>-<short-code>`, for example `aws-s3-enable-logging`; - any **aliases** the check declares. Since 0.71.0 these matches are case-insensitive. tfsec comments used long IDs, and not every long ID survived unchanged: tfsec's README shows `aws-vpc-no-public-ingress-sgr`, while Trivy's documentation uses `aws-ec2-no-public-ingress-sgr` for its security-group-rule example. An ignore comment whose ID matches nothing in Trivy suppresses nothing, so the finding simply reappears. ### Custom checks Trivy evaluates the built-in namespaces by default. A custom check in another Rego package, such as `user.terraform.tags`, is loaded with `--config-check <path>` and only runs once its namespace prefix is passed with `--check-namespaces user`. Writing those checks is a policy-as-code skill of its own; the migration task is to load them and confirm they fire. ## A migration sequence that does not lose coverage 1. Run tfsec and Trivy side by side on the same commit and save both reports as JSON. 2. Diff by resource and rule. A finding only tfsec reports usually means an ID that changed or a check that moved; one only Trivy reports is usually a newer check. 3. Grep for `tfsec:ignore:` and confirm each one still suppresses its finding under Trivy; rename IDs that no longer match to the Trivy long ID. 4. Add `--exit-code 1` (and the severities the team agreed) before you delete the tfsec job, so the gate never silently stops failing. 5. Make sure CI can reach the checks registry, or mirror the bundle and point `--checks-bundle-repository` at it. 6. Remove the tfsec step only when the diff is explained. ## Where interviews probe - "Is tfsec dead?" Not archived, still installable, but no longer where checks evolve. - "Will our ignores break?" The prefix is still parsed; the IDs are what may not match. - "Why did our gate stop failing?" Usually the exit code default, not missing checks. ## Choosing the subcommand for the new job - `trivy config` runs the misconfiguration scanner only, which is the closest one-to-one replacement for a tfsec step. - `trivy fs --scanners vuln,secret,misconfig` covers the same Terraform plus lockfile vulnerabilities and secret rules in one pass, at the cost of one report mixing three kinds of finding. - Whichever you pick, pass the same `--tf-vars` files the old job used for variable values, or variables that tfsec resolved become unknown and their checks go quiet. The second option is tempting, but teams that own Terraform and teams that own dependencies often triage differently, so many keep separate jobs.
- After the switch, a finding you had suppressed with a tfsec comment is back. Why?The `tfsec:` prefix is still parsed, so the ID is the suspect: the comment names a tfsec-era long ID that matches none of the Trivy check's ID, AVD ID, long ID or aliases. Copy the ID from Trivy's report into the comment, keep the `:exp:` date, and re-run to confirm the finding is suppressed.
- Your runners have no internet egress. What changes for the checks?Trivy cannot pull the trivy-checks bundle and falls back to the checks embedded in the binary at build time, which age with your Trivy version. Mirror the bundle into an internal registry and set `--checks-bundle-repository` to it.
saying these in an interview costs you the question
- tfsec:ignore comments are ignored by Trivy and must all be rewritten
- tfsec's repository is archived and its binary no longer installs
- Trivy fails the build on findings by default, just like the old job
- Every tfsec long ID maps unchanged to the same Trivy long ID
- A custom check in a user package runs once --config-check points at it