Terraform Security Scanning in CI | tfsec vs Checkov for GitHub Actions
The fastest way to prevent a bad Terraform change from reaching production is to block it in Continuous Integration (CI) before it's merged. Static analysis tools like tfsec and Checkov can spot high-risk misconfigurations – open security groups, public S3 buckets, disabled encryption – long before the apply command.
Many teams also scan the Terraform plan for deeper, resolved context, which you can automate directly in Terrateam.
In this post, you'll learn about practical uses for Checkov and tfsec and how you can use Terrateam to orchestrate GitOps workflows, including scanning Terraform plans with Checkov.
If you're also using OpenTofu, we've got you covered – Checkov and tfsec fit into the same pattern.
Checkov vs. tfsec: What they're used for (and why you should use both)
Both tools enable you to analyze infrastructure-as-code (IaC) for insecure defaults and policy violations. They're optimized for speed in CI and work well as "shift-left" checks that run before provisioning.
At a glance:
Capability | tfsec | Checkov |
---|---|---|
Primary input | Terraform/HCL directory | Terraform/HCL directory and Terraform plan files |
Typical CI usage | Fast static checks on changed code | Broader policy coverage; can scan plans generated by your CI |
Output formats | Text, JSON, SARIF (GitHub code scanning) | Text, JSON, SARIF (GitHub code scanning) |
Rule customization | Allow/ignore by code comment or config; custom checks possible | Rich policy set; can extend and enforce org standards; strong plan-level context |
Best for | Quick, developer-friendly feedback | Comprehensive policy guardrails; plan-aware checks |
Why use both? A typical pattern is to run tfsec first for near-instant feedback on code diffs, then run Checkov after terraform plan to evaluate the complete change set with resource values resolved. Checkov's Terraform Plan Scanning has official documentation, and Terrateam can run that step automatically against the generated plan.
Checkov supports SARIF output and GitHub code scanning workflows, while tfsec supports SARIF output for GitHub code scanning.
A GitHub Actions configuration to run tfsec and Checkov on every PR
Below is a pragmatic setup, with two jobs in one workflow.
- tfsec runs quickly on raw code.
- checkov generates a Terraform plan and scans it for deeper context.
Create .github/workflows/iac-security.yml:
name: IaC Security (tfsec + Checkov)
on:
pull_request:
push:
branches: [ main ]
permissions:
contents: read
security-events: write # for SARIF uploads
jobs:
tfsec:
name: tfsec (code scan)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.9.5
- name: Run tfsec
uses: aquasecurity/tfsec-action@v1
with:
working_directory: .
additional_args: >
--concise-output
--format sarif
--out tfsec.sarif
--soft-fail-severities LOW, MEDIUM
- name: Upload SARIF (tfsec)
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: tfsec.sarif
checkov:
name: Checkov (plan scan)
runs-on: ubuntu-latest
needs: tfsec
env:
TF_IN_AUTOMATION: "true"
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.9.5
- name: Terraform Init
run: terraform init -input=false
- name: Terraform Plan (json)
run: terraform plan -out=tfplan.bin && terraform show -json tfplan.bin > tfplan.json
- name: Run Checkov on plan
uses: bridgecrewio/checkov-action@v12
with:
directory: .
file: tfplan.json
framework: terraform_plan
soft_fail: false
output_format: sarif
output_file_path: checkov.sarif
- name: Upload SARIF (Checkov)
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: checkov.sarif
Why this layout works
- Fail fast on code issues with tfsec: developers get immediate, actionable feedback. Aqua Security maintains the tfsec Action and supports generating SARIF for GitHub's code scanning UI.
- Plan-aware checks catch issues that only appear once variables and modules resolve during plan. Checkov documents Terraform Plan Scanning explicitly, and you can feed it the terraform show -json output (as in the job above)
- SARIF is the standard format GitHub consumes for third-party scanners; the upload-sarif Action publishes results to the Security tab.
If you manage Terraform via Terrateam, you can move the heavy lifting out of bespoke CI and let Terrateam's workflow engine run Checkov against the plan automatically via a dedicated step. Read our guide to "Scanning Terraform Plans with Checkov" and the workflows reference.
Interpreting results and customizing rules
Security scanners produce a lot of signal; the trick is turning that into a clean, enforceable bar that your team can actually meet.
1. Start with severity gates
- In tfsec, use --format sarif + --out to produce a SARIF file and consider soft-failing lower severities while you roll out. You'll still block HIGH/CRITICAL.
- In Checkov, you can choose outputs (CLI, JSON, JUnit, SARIF) and control failure behavior; for example, it documents soft-fail options.
Starting with severity gates helps you "land" scanners without blocking every PR on day one.
2. Triage findings by class
Focus on classes with the highest blast radius:
- Network exposure – unrestricted ingress/egress, public endpoints without controls.
- Data at rest – unencrypted storage (EBS, S3, RDS).
- Identity – wildcard IAM policies, admin-level roles bound to services.
- Secrets – inline credentials or missing secret managers.
You'll see these patterns repeatedly, so create playbooks to standardize fixes.
3. Use inline, documented exceptions
Sometimes the scanner is right, but the business requirement is stronger. In those rare cases, annotate with a targeted skip and a reason in code comments (both tools support this pattern), e.g.,
- #tfsec:ignore: AWS002 – allow public read on this demo bucket
- #checkov:skip=CKV_AWS_20 – legacy ingress for partner IP
Checkov's docs cover suppression and custom policy approaches.
4. Implement plan-level validation for derived values
A lot of "is this actually public?" questions depend on variables, data sources, or module defaults. Running Checkov on the plan answers these with real resolved values. That's why many teams run both: quick code-only checks plus plan-aware checks. Terrateam's workflow step makes this natural to adopt in your GitOps process.
5. Centralize org standards as policy
If you already enforce policy with OPA/Conftest, keep using it alongside scanners. Terrateam integrates with OPA, allowing you to encode "musts" (e.g., "S3 buckets must use KMS keys") as code, and the system applies or requires approvals based on policy outcomes.
Extending and tuning rules
Both tools provide substantial built-in checks; still, you'll want lightweight customization.
- Directory or module scoping: Point the scanner only at changed paths or critical modules to reduce noise and runtime.
- Rule selection: Enforce HIGH/CRITICAL globally, then ratchet to MEDIUM as debt burns down. Checkov lets you tune failure behavior (e.g., soft-fail by severity).
- Baselining: Use a baseline to block regressions while you remediate over time if your repo has existing debt.
- Output formats: Emit SARIF to surface results natively in GitHub's "Security" tab; JSON outputs help you build dashboards. (Both tools support SARIF).
For org-wide policy, consider "policy after plan": run a plan, evaluate with Checkov and/or OPA, require approvals, and only then apply.
The benefits of integrating Checkov and tfsec into your Terraform Workflow
- Catch issues before they become outages: Blocking merges on CRITICAL/HIGH findings prevents you from having to work late on Fridays.
- Developer-centric feedback: Inline SARIF comments and concise console output keep the loop short – developers don't need to leave the PR to see what failed. GitHub treats uploaded SARIF as first-class code scanning results.
- Consistent security bar across repos: A shared workflow enforces the same checks for all Terraform modules and services.
- Plan-aware accuracy: Scanning plan output reduces false positives related to variable resolution and merges, a capability Checkov documents and Terrateam integrates natively.
- Policy as code alignment: Pair scanners with OPA/Conftest to encode non-negotiables, approvals, and environment-specific rules.
- Fits GitOps orchestration: Terrateam treats planning, policy, and scanning as first-class workflow steps, ensuring that reviews and approvals happen with full context in the pull request. See the workflows reference and gate/approval capabilities.
Best Practices Checklist
- Run on every PR and push to main. Use the tfsec and Checkov Actions.
- Fail on HIGH/CRITICAL to start, then tighten the settings. Use soft-fail options while you roll out.
- Scan the Terraform plan for deeper context.
- Use SARIF uploads for rich PR annotations.
- Document exceptions with reasons and reviews. Checkov supports suppression; tfsec supports ignore comments.
- Add OPA/Conftest for org-specific policies.
- Automate with Terrateam where possible to simplify CI and centralize workflows.
Example: Moving scanning into Terrateam workflows
If you're already using Terrateam, you can simplify your CI by declaring checks in Terrateam's config and letting it orchestrate plan → scan → policy → approval:
# .terrateam/config.yml (excerpt)
workflows:
- tag_query: "dir:production"
plan:
- type: init
- type: plan
- type: checkov
- type: conftest
This approach centralizes security while keeping the pull request as the source of truth.
Checkov vs. tfsec vs. Terrascan
You'll often see comparisons of Checkov vs. tfsec vs terrascan. In practice, most teams don't pick one forever, they compose tools. However, each does have its pros and cons:
Tool | Pros | Cons | When to use it |
---|---|---|---|
Checkov | Broad IaC support with extensive built-in policies and strong integrations | Slightly slower and more complex to configure | Best for multi-framework environments needing wide compliance coverage |
tfsec | Fast, lightweight, and easy to integrate for Terraform use cases | Limited to Terraform with minimal customization | Ideal for Terraform-only projects that require quick security checks |
Terrascan | Multi-IaC support with powerful OPA-based custom policy control | Slightly steeper learning curve and fewer default policies | Suited for enterprises needing strict, customizable compliance across IaC types |
You should also consider additional tools (e.g., OPA/Conftest) for organization-specific guardrails and approvals.
Conclusion
Security scans shouldn't be a once-a-quarter audit, they should be a daily guardrail that runs every time someone proposes a change. By combining tfsec and Checkov in GitHub Actions, you block risky Terraform changes before they land. By scanning plans, you catch issues that only show up once everything is resolved.
Suppose you'd like less DIY CI and more orchestrated GitOps, with plans, scans, policies, and approvals baked into the pull request. If that's the case,give Terrateam a try. It integrates tightly with Checkov for plan scanning and with OPA for policy enforcement, allowing you to ship safer infrastructure with confidence. Sign up here: https://terrateam.io/signup.