linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
Intro
Lessons
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Introduction
  • Lessons
  • How it works
  • Knowledge base
  • Cheat sheet
  • Capstone
  • Interview prep
home/terraform/kb/Security/tf-trivy-tfsec

kb/security ── Security ── intermediate

Trivy and tfsec: HCL Security Scanners

tfsec, the original security scanner from Aqua, is written in Go and fast. In 2023 it was merged into Trivy, which now has a `trivy config` module for IaC using the same rule set (AVD-* identifiers). Compared to Checkov: fewer rules, but faster, single binary with no dependencies. In CI you typically use either Trivy/tfsec or Checkov, not both.

view as markdownaka: trivy, tfsec, terraform-trivy, terraform-tfsec

What they are and why there are two

tfsec appeared in 2019 as a Go application from Aqua Security, a security scanner for Terraform. It is fast (tens of milliseconds on a medium-sized project), ships as a single binary, and has no Python dependencies.

In 2023 Aqua merged tfsec into Trivy, the same vendor's multi-tool (container image scanner, SBOM, IaC). IaC scanning is now `trivy config

<dir>`. The rules and engine are the same as in tfsec. tfsec itself is deprecated as a standalone project, but its commands and rule identifiers are still common in legacy pipelines.

The short answer: use Trivy. If you are on a legacy pipeline with tfsec, migration is straightforward. See the migration section in tf-trivy-tfsec.

Running Trivy

bash
trivy config .

Scans all supported formats in the current directory: Terraform, CloudFormation, Kubernetes, Dockerfile.

Output:

Tests: 28 (SUCCESSES: 25, FAILURES: 3, EXCEPTIONS: 0)
Failures: 3 (UNKNOWN: 0, LOW: 0, MEDIUM: 2, HIGH: 1, CRITICAL: 0)
main.tf (terraform)
  AVD-AWS-0086 (HIGH): No public access block so not blocking public acls
    ...
  AVD-AWS-0089 (MEDIUM): S3 Bucket does not have logging enabled.
    ...

Terraform only:

bash
trivy config --skip-dirs node_modules --type terraform .

Specific severity levels:

bash
trivy config --severity HIGH,CRITICAL .

Exit code: 0 when there are no findings by default. --exit-code 1 sets exit 1 on any finding (for CI).

tfsec to trivy migration

tfsectrivy
tfsec .trivy config .
tfsec --exclude aws-s3-encryption-customer-keytrivy config --ignore-policy aws-s3-encryption-customer-key
tfsec --tfvars-file dev.tfvarstrivy config --tf-vars dev.tfvars
.tfsec/config.yamltrivy.yaml
finding-ID: AWS001finding-ID: AVD-AWS-0086 (new)

The rules are the same, but the identifiers were renamed. If you had tfsec:ignore:AWS001 in HCL, replace it with trivy:ignore:AVD-AWS-0086.

Suppression

Inline:

hcl
#trivy:ignore:AVD-AWS-0089
resource "aws_s3_bucket" "logs" {
  bucket = "no-logging-on-purpose"
}

Multiple rules:

hcl
#trivy:ignore:AVD-AWS-0089
#trivy:ignore:AVD-AWS-0086
resource "aws_s3_bucket" "public_assets" {
  # ...
}

Config file trivy.yaml:

yaml
ignored-policies:
  - AVD-AWS-0086

Trivy/tfsec does not require a reason in the comment. This differs from Checkov. Teams often add a convention such as `#trivy:ignore:AVD-AWS-0086

see ADR-014` for traceability.

Trivy/tfsec vs Checkov

AspectTrivy/tfsecCheckov
LanguageGoPython
InstallationSingle binary (curl + chmod)pip install (Python env)
SpeedFaster (milliseconds)Noticeably slower (seconds on a medium project)
Rules~150 for Terraform~600 for Terraform
Cloud provider coverageAWS, GCP, Azure, Oracle, DigitalOceanSame plus Kubernetes policies
Custom rulesYAML or RegoPython class or YAML (since 3.0)
SARIF outputYesYes
MaintainedYes (via Trivy)Yes (Prisma Cloud)

In practice both tools catch similar classes of problems. Checkov has broader coverage; Trivy is faster. Many teams pick one to avoid duplicating suppressions. If you are choosing: Trivy for CI speed, Checkov for policy specifications.

CI integration

yaml
# .github/workflows/security.yml
jobs:
  trivy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Trivy config scan
        uses: aquasecurity/trivy-action@0.24.0
        with:
          scan-type: config
          scan-ref: .
          severity: HIGH,CRITICAL
          exit-code: 1
          format: sarif
          output: trivy-results.sarif
      - name: Upload SARIF
        if: always()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: trivy-results.sarif

You get findings in the Security tab and a mandatory fail on HIGH/CRITICAL.

Custom rules via Rego

Trivy can read custom rules written in Rego (the same language as OPA, see tf-policy-as-code):

rego
# policies/costcenter.rego
package custom.costcenter
__rego_metadata__ := {
    "id": "AVD-CUSTOM-001",
    "title": "CostCenter tag required",
    "severity": "MEDIUM",
}
__rego_input__ := {
    "selector": [{"type": "defsec", "subtypes": [{"service": "s3"}]}],
}
deny[res] {
    bucket := input.aws.s3.buckets[_]
    not bucket.tags.CostCenter
    res := {
        "msg": sprintf("S3 bucket %s missing CostCenter tag", [bucket.name.value]),
    }
}

To load the policy:

bash
trivy config --config-policy ./policies .

Pitfalls

  • AVD identifiers are unstable. They were originally AWS001, then renamed to AVD-AWS-0086. Old tfsec suppress comments will keep "working" as no-ops, but the rule now lives under a different name. Verify your suppressions after migration.

  • Trivy scans everything by default. In a directory that contains both a Dockerfile and .tf files it runs both scans. To limit to Terraform, use --type terraform (or --scanners config for all IaC formats).

  • Severity reflects vendor opinion. Something rated "MEDIUM" by Trivy may be "CRITICAL" in your environment. Do not trust the vendor grading blindly. Set your own --severity filter or use a soft-fail on lower levels.

  • trivy.yaml vs CLI flags: conflicts. When both are present, CLI wins. This trips teams up. Keep configuration in one place.

  • Does not cover state. This is a source scanner, not a runtime tool. What is already deployed and running is outside its scope. For drift, see tf-drift-detection. For scanning existing resources, Trivy has an AWS account scan mode, but that is a separate feature.

  • Suppression without a reason becomes a maintenance problem. Trivy does not require one, unlike Checkov. Have the team adopt a convention: #trivy:ignore:X # reason. Without it, nobody will remember why the ignore is there six months later.

§ команды

bash
trivy config .

Scans all .tf, .yaml, and Dockerfile files in the current directory. Default severity: everything except UNKNOWN.

bash
trivy config --severity HIGH,CRITICAL .

High and critical findings only. The standard CI gate mode.

bash
trivy config --tf-vars dev.tfvars .

Pass a var file to partially resolve interpolations.

bash
trivy config --format sarif --output trivy.sarif .

Output for the GitHub Security tab.

§ см. также

  • tf-secrets-in-stateSecrets and Terraform state: where to store them and how to read themState holds everything that passed through apply: passwords, keys, and tokens in plain text. The options are to store secrets in Secrets Manager, Vault, or KMS; read them through a data source; encrypt the backend (S3 SSE-KMS); and use OIDC instead of access keys for CI. "sensitive=true" affects log output, not encryption.
Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies