RSS

Terraform Security Scanning in CI: Using tfsec and Checkov with GitHub Actions

Terraform Security CI/CD Github Actions Infrastructure As Code

What you'll learn: Checkov vs. tfsec at a practical level, including when to use one or both. How to set up GitHub Actions to run both tools on every push and pull request, and interpret output and fail builds on critical issues so you can customize rules for your environment.

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.

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

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

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:

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.

    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

    1. Catch issues before they become outages: Blocking merges on CRITICAL/HIGH findings prevents you from having to work late on Fridays.
    2. 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.
    3. Consistent security bar across repos: A shared workflow enforces the same checks for all Terraform modules and services.
    4. Plan-aware accuracy: Scanning plan output reduces false positives related to variable resolution and merges, a capability Checkov documents and Terrateam integrates natively.
    5. Policy as code alignment: Pair scanners with OPA/Conftest to encode non-negotiables, approvals, and environment-specific rules.
    6. 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

    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.