Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

tflint and tfsec in Your Pipeline

By Kokil Thapa | Last reviewed: September 2026

Your Terraform plan looks clean, then production opens an S3 bucket to the world or provisions the wrong instance type in the wrong region. Running tflint and tfsec in your pipeline catches those mistakes before terraform apply ever runs. TFLint enforces provider rules and catches invalid resource arguments. tfsec scans for security misconfigurations aligned with CIS and cloud best practices. On real client projects where a single bad module can affect staging and production, I treat both scanners as non-negotiable gates in the same way I treat shift-left security in CI/CD pipelines.

What are TFLint and tfsec, and why run them in CI?

TFLint is a pluggable linter for Terraform and OpenTofu. It checks syntax, naming, and provider-specific rules that terraform validate does not cover. tfsec is a static analysis tool that reads your .tf files and flags insecure defaults: public buckets, open security groups, missing encryption, weak IAM policies.

Neither tool replaces a human review or a full cloud audit. They do stop the obvious failures that slip through when teams move fast. I've seen a developer copy a module from Stack Overflow, change the name, and push. TFLint caught an invalid instance_type for the chosen AWS region. tfsec flagged a security group rule allowing 0.0.0.0/0 on port 22.

Running them only on a laptop is not enough. Developers forget. Pipelines do not. Wire both into the same stage where you already run terraform fmt -check and terraform validate. That pattern fits naturally alongside the broader guidance in our IaC security scan with tfsec, Checkov, and Terrascan article.

tflint and tfsec in Your PipelineGit PushPR openedfmt + validateHashiCorp checksTFLintStyle + providertfsecSecurity scanFail PRBlock mergePass PRPlan + applyError or CRITICALAll clearterraform plan (approved envs)Manual or automated apply after review
Pipeline placement for tflint and tfsec: run both after fmt/validate and before plan or apply on protected branches.

What each tool catches that validate misses

  • TFLint: Deprecated arguments, wrong attribute names, resources that do not exist in a given provider version, and regional constraints like unavailable EC2 types.
  • tfsec: Public exposure, missing encryption at rest, overly permissive IAM, logging disabled, and metadata service issues on compute resources.
  • Together: Correctness plus security posture, with minimal overlap and fast execution on typical modules.

How do you install and run TFLint locally?

Start local so developers see the same errors CI will surface. Install TFLint from the official release binary or your package manager, then initialise plugins once per repo.

Install TFLint on Linux or macOS

# Download latest release from GitHub (adjust version as needed)
curl -s https://raw.githubusercontent.com/terraform-linters/tflint/master/install_linux.sh | bash

# Verify
tflint --version

# In your Terraform repo root
tflint --init

Official install scripts and release notes live on the TFLint GitHub repository. Pin the version in CI so local and pipeline runs match.

Configure .tflint.hcl

Place a .tflint.hcl file at the repo root or module root. Enable the Terraform language plugin and your cloud provider plugin.

plugin "terraform" {
  enabled = true
  preset  = "recommended"
}

plugin "aws" {
  enabled = true
  version = "0.32.0"
  source  = "github.com/terraform-linters/tflint-ruleset-aws"
}

config {
  module = true
  force  = false
}

rule "terraform_naming_convention" {
  enabled = true
  format  = "snake_case"
}

Run the linter recursively when you use nested modules:

tflint --recursive --format compact

Fix what you can with tflint --fix where rules support auto-fix. Commit the config file so every environment shares the same rule set. Store outputs as JUnit or SARIF if your CI dashboard expects structured reports, similar to how you might pipe JSON through a JSON formatter during local debugging.

How do you configure tfsec for your Terraform project?

tfsec installs as a single binary. It scans directories, modules, and variables without needing cloud credentials. That makes it safe to run on every pull request in a shared runner.

Install and run tfsec

# Linux amd64 example
curl -s https://raw.githubusercontent.com/aquasecurity/tfsec/master/scripts/install_linux.sh | bash

tfsec --version
tfsec .

Documentation and rule references are maintained on the tfsec GitHub repository. Aqua Security also documents tfsec alongside Trivy for broader container and IaC scanning.

Minimum severity and ignore files

Do not silence everything on day one. Start with defaults, then tune. Use --minimum-severity HIGH in CI if your existing codebase is noisy. Track intentional exceptions in tfsec.yml or inline comments with expiry dates.

# tfsec.yml at repo root
minimum_severity: MEDIUM

exclude:
  - aws-s3-enable-bucket-logging  # legacy bucket; ticket INFRA-142

# Inline exception example in main.tf:
# tfsec:ignore:AWS021 -- public ALB by design, WAF attached

Every ignore needs a ticket or comment explaining why. Blanket ignores defeat the purpose of running tfsec at all. Pair this discipline with the secret-handling rules in handle secrets in CI/CD pipelines safely so scan logs never leak credentials from tfvars.

TFLint vs tfsec Focus AreasTFLint• Invalid resource attributes• Provider version drift• Naming conventions• Deprecated syntax• Regional availability• Module call errorsNeeds provider pluginstfsec• Public S3 / storage• Open security groups• Missing encryption• Weak IAM policies• Logging disabled• Metadata service risksNo cloud creds requiredSame .tf files — run both in parallel
TFLint targets correctness and conventions; tfsec targets security misconfigurations in the same Terraform source tree.

How do you add tflint and tfsec in your pipeline?

The exact YAML differs by platform. The sequence does not. Initialise TFLint plugins, run both scanners, fail on non-zero exit codes, and publish reports. Below are patterns I use on GitLab CI and GitHub Actions. They mirror the structure in our GitLab CI pipeline tutorial and Terraform with Azure DevOps pipelines guides.

GitLab CI job example

stages:
  - validate
  - security

.terraform_base:
  image:
    name: hashicorp/terraform:1.9
    entrypoint: [""]
  before_script:
    - apk add --no-cache curl bash
    - curl -s https://raw.githubusercontent.com/terraform-linters/tflint/master/install_linux.sh | bash
    - curl -s https://raw.githubusercontent.com/aquasecurity/tfsec/master/scripts/install_linux.sh | bash

terraform-validate:
  extends: .terraform_base
  stage: validate
  script:
    - terraform fmt -check -recursive
    - terraform init -backend=false
    - terraform validate

tflint:
  extends: .terraform_base
  stage: security
  script:
    - tflint --init
    - tflint --recursive --format junit > tflint-report.xml
  artifacts:
    reports:
      junit: tflint-report.xml

tfsec:
  extends: .terraform_base
  stage: security
  script:
    - tfsec . --format junit --out tfsec-report.xml --minimum-severity HIGH
  artifacts:
    reports:
      junit: tfsec-report.xml

Cache the TFLint plugin directory between jobs to shave 10–20 seconds off each run. Pin Terraform to the version your team uses in production. HashiCorp documents version constraints in the official Terraform language documentation.

GitHub Actions workflow snippet

name: Terraform Scan
on:
  pull_request:
    paths:
      - '**.tf'
      - '.tflint.hcl'

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: 1.9.0

      - name: Setup TFLint
        uses: terraform-linters/setup-tflint@v4

      - name: Run TFLint
        run: |
          tflint --init
          tflint --recursive

      - name: Run tfsec
        uses: aquasecurity/tfsec-action@v1.0.0
        with:
          soft_fail: false
          minimum_severity: HIGH

Limit path filters to Terraform files so unrelated PHP or Laravel changes do not trigger IaC jobs. If you also run application pipelines, compare runner choices in GitHub Actions vs Azure Pipelines before standardising on one platform.

  1. terraform fmt -check — fast, no plugins.
  2. terraform init -backend=false plus validate — catches HCL structure errors.
  3. TFLint — provider-aware lint rules.
  4. tfsec — security static analysis.
  5. terraform plan — only on protected branches with remote state and credentials.

Keep plan and apply in separate jobs with environment approvals. Scanners need no write access to cloud accounts. That separation reduces blast radius if a pipeline variable is misconfigured.

Parallel Scan Stageterraform validateTFLint job~15–45 sectfsec job~10–30 secMerge gate passedBoth jobs greenTypical total added time: under 60 secondsCache plugins between pipeline runs
Run TFLint and tfsec in parallel after validate to keep pull request feedback under one minute on most modules.

How do TFLint and tfsec compare to Checkov and Terrascan?

Teams often ask whether tfsec alone is enough. It is a strong baseline. Checkov and Terrascan cast a wider net across Helm, CloudFormation, and Kubernetes manifests. TFLint has no real substitute for deep Terraform provider linting.

ToolPrimary focusBest forTypical CI cost
TFLintTerraform lint + provider rulesCatching invalid configs before planLow (seconds)
tfsecTerraform security misconfigurationsFast security gate, no cloud credsLow (seconds)
CheckovMulti-framework IaC policiesPolicy-as-code across many file typesMedium
TerrascanOPA/Rego policy engine for IaCCustom org policies at scaleMedium–high

My practical stack for Terraform-only repos: TFLint plus tfsec on every PR. Add Checkov when you also ship Kubernetes YAML or Dockerfiles in the same repo. Read the full comparison in IaC security scan with tfsec, Checkov, and Terrascan before buying a commercial scanner you may not need yet.

For small teams in Nepal running infrastructure on a tight budget, open-source gates save real money. A missed public RDS snapshot costs far more than the Rs 5,000/month (~USD 37) you might spend on a slightly larger CI runner to run parallel jobs. Good pipeline hygiene is part of what we deliver under Linux system administration in Nepal and testing and optimization in Nepal engagements.

What are common mistakes when wiring tflint and tfsec in your pipeline?

Most failures are operational, not tool bugs. Fix these before you tune rule severity.

Skipping plugin init in CI

TFLint exits with plugin errors if you forget tflint --init. Cache ~/.tflint.d/plugins keyed on your .tflint.hcl hash. Without caching, every job re-downloads AWS or Azure rulesets.

Running scanners only on main

That trains developers to merge first and fix later. Run on pull requests. Block merges when severity thresholds fail. This matches the automation principles in build pipeline automation best practices.

Ignoring false positives without documentation

Inline tfsec:ignore comments without tickets become permanent holes. Require a reason and an owner. Review ignores quarterly the same way you review dependency updates.

Mixing scanner and plan credentials

tfsec and TFLint need file access, not AWS keys. Keep plan jobs in a separate role with least privilege. See Azure DevOps YAML pipelines: a practical guide for environment-scoped service connections.

PR Failure TriagePipeline failed?TFLinttfsecFix attributeUpdate provider docsReal risk?Review severityCommit fixRemediate configRestrict accessFalse positiveDocumented ignore + ticketRe-run pipelineMerge when all gates pass
Triage flow when tflint and tfsec fail a pull request: fix real issues first, document only verified false positives.

Real-world payoff on infrastructure projects

On infrastructure-heavy work like SRP Infrastructure Development Nepal, predictable modules and automated gates reduce review load. Reviewers focus on architecture and cost, not whether someone left port 22 open to the internet. The same mindset applies when you extend pipelines with AI code review in CI — automate the repetitive checks, keep humans on design decisions.

If you manage Terraform alongside application deploys, align scanner stages with your broader CI/CD strategy. Our Bitbucket Pipelines CI/CD guide and Jenkins declarative pipeline tutorial show where a security stage fits without slowing feedback loops.

Key Takeaways

  • Run tflint and tfsec in your pipeline on every pull request that touches .tf files, not only on main after merge.
  • Commit .tflint.hcl and a documented tfsec.yml so local runs match CI exactly.
  • Order stages as fmt → validate → TFLint + tfsec (parallel) → plan → apply with separate credentials.
  • Start with tfsec --minimum-severity HIGH on legacy repos, then tighten to MEDIUM once noise drops.
  • Cache TFLint plugins and pin tool versions in CI to avoid flaky, slow jobs.
  • Pair tfsec with TFLint rather than replacing one with Checkov unless you need multi-framework policy scans.

People Also Ask

Does tfsec need AWS credentials to scan Terraform?

No. tfsec performs static analysis on your HCL files and modules. It never calls cloud APIs during a scan. That makes it safe to run on shared CI runners without exposing production keys.

Can TFLint replace terraform validate?

No. Run both. terraform validate checks internal consistency of configuration and references. TFLint adds provider-specific rules, naming policies, and deprecation checks that validate does not include.

What exit code should fail a CI job?

Both TFLint and tfsec exit non-zero when they find issues at or above your configured severity. Configure your pipeline to fail the job on any non-zero exit unless you explicitly use soft-fail for a transition period.

Is tfsec still maintained in 2026?

Yes. Aqua Security maintains tfsec and integrates its rules into the broader Trivy ecosystem. Many teams still run the standalone tfsec binary in Terraform-only pipelines because it is fast and focused.

Ship safer Terraform on every merge

Adding tflint and tfsec in your pipeline takes less than an hour for a typical module layout. The ongoing cost is small. The alternative — discovering a public bucket or invalid instance type after apply — is not. Start with the GitLab or GitHub examples above, pin your versions, and treat scanner failures like failing unit tests.

If you want help wiring IaC security into GitLab CI, Azure DevOps, or a mixed Laravel plus Terraform stack, contact us or explore custom software development in Nepal. For more pipeline patterns, browse the blog or read about who builds these systems.

Frequently Asked Questions

TFLint is a pluggable linter for Terraform and OpenTofu that checks syntax, naming, and provider-specific rules beyond what terraform validate covers. tfsec is a static analysis tool that reads your .tf files and flags insecure defaults such as public S3 buckets, open security groups, missing encryption, and weak IAM policies. Running both in CI on every pull request catches invalid instance types, regional constraint violations, and obvious security holes before terraform apply runs. Pipelines do not forget; laptop-only scans do. Wire them into the same stage as terraform fmt -check and terraform validate, failing the job on errors or HIGH and CRITICAL findings.

No. tfsec performs static analysis on your HCL files and modules without calling cloud APIs. It is safe to run on shared CI runners without production keys.

No. Run both. terraform validate checks internal HCL consistency and references. TFLint adds provider rules, naming policies, and deprecation checks that validate does not include.

Run terraform fmt -check first because it is fast and needs no plugins. Follow with terraform init -backend=false and terraform validate to catch HCL structure errors. Then run TFLint and tfsec in parallel after validate and before plan or apply on protected branches. Keep plan and apply in separate jobs with environment approvals. Scanners need file access only, not write access to cloud accounts, which reduces blast radius if pipeline variables are misconfigured. On typical modules, parallel scanning keeps pull request feedback under one minute.

Install TFLint from the official GitHub release binary or install script, verify with tflint --version, then run tflint --init once per repo to initialise plugins. Place a .tflint.hcl file at the repo or module root, enable the Terraform language plugin with the recommended preset, and enable your cloud provider plugin such as the AWS ruleset. Pin the plugin version in config and commit the file so local and CI runs share the same rule set. Scan nested modules with tflint --recursive --format compact, and use tflint --fix where rules support auto-fix. Pin the TFLint version in CI so local and pipeline runs match.

Install tfsec as a single binary via the official install script, then scan your directory with tfsec . without cloud credentials. Do not silence everything on day one; start with defaults, then tune. In CI, use --minimum-severity HIGH on noisy legacy codebases, then tighten to MEDIUM once findings drop. Track intentional exceptions in tfsec.yml at the repo root or with inline tfsec:ignore comments that include a ticket reference and expiry context. Every ignore needs a documented reason; blanket excludes defeat the purpose. Pair this discipline with safe secret handling in CI so scan logs never leak credentials from tfvars files.

terraform validate checks internal consistency of configuration and variable references but does not understand provider-specific constraints. TFLint catches deprecated arguments, wrong attribute names, resources that do not exist in a given provider version, and regional constraints such as unavailable EC2 instance types in a chosen AWS region. It also enforces naming conventions through rules like terraform_naming_convention with snake_case formatting. On real projects I have seen TFLint stop a copied module with an invalid instance_type for the target region before anyone ran plan. That is correctness checking validate simply cannot provide.

tfsec scans .tf files for insecure defaults aligned with CIS and cloud best practices. It flags public exposure such as world-readable S3 buckets and security group rules allowing 0.0.0.0/0 on sensitive ports like SSH. It also catches missing encryption at rest, overly permissive IAM policies, disabled logging, and metadata service issues on compute resources. Together with TFLint, you get correctness plus security posture with minimal overlap. Neither tool replaces human review or a full cloud audit, but they stop the obvious failures that slip through when teams move fast and copy modules without reading every attribute.

Create a shared base job using the hashicorp/terraform image, install TFLint and tfsec via their official curl install scripts in before_script, and split work into validate and security stages. The validate job runs terraform fmt -check -recursive, terraform init -backend=false, and terraform validate. The tflint job runs tflint --init then tflint --recursive --format junit, publishing a JUnit artifact. The tfsec job runs tfsec . --format junit --out tfsec-report.xml --minimum-severity HIGH with a JUnit artifact. Fail on non-zero exit codes. Cache the TFLint plugin directory keyed on your .tflint.hcl hash to save 10 to 20 seconds per job, and pin Terraform to your production version.

Trigger the workflow on pull requests that touch .tf files or .tflint.hcl using path filters so unrelated application changes do not fire IaC jobs. Check out the repo, install Terraform 1.9.0 with hashicorp/setup-terraform, then use terraform-linters/setup-tflint to install TFLint. Run tflint --init followed by tflint --recursive. Run tfsec with aquasecurity/tfsec-action, setting soft_fail to false and minimum_severity to HIGH so the job fails on real findings. Limit path filters deliberately when you also run PHP or Laravel pipelines in the same repository. The sequence mirrors GitLab CI: initialise plugins, scan, fail on errors, and publish reports where your dashboard supports them.

TFLint has no real substitute for deep Terraform provider linting; it focuses on syntax, naming, and provider rules with low CI cost measured in seconds. tfsec is a fast Terraform-only security gate that needs no cloud credentials, also low cost. Checkov casts a wider net across Helm, CloudFormation, and Kubernetes with medium CI cost and policy-as-code across many file types. Terrascan uses OPA and Rego for custom org policies at scale with medium to high cost. My practical stack for Terraform-only repos is TFLint plus tfsec on every pull request. Add Checkov when the same repo also ships Kubernetes YAML or Dockerfiles. Read a full multi-tool comparison before buying a commercial scanner you may not need yet.

The most frequent failure is skipping tflint --init in CI, which causes plugin errors; cache ~/.tflint.d/plugins keyed on your .tflint.hcl hash. Running scanners only on main trains developers to merge first and fix later; run on pull requests and block merges when severity thresholds fail. Inline tfsec:ignore comments without tickets become permanent holes; require a reason, an owner, and quarterly review like dependency updates. Mixing scanner and plan credentials is another trap: tfsec and TFLint need file access only, not AWS keys, so keep plan jobs in a separate role with least privilege. Triage failures by fixing real issues first, then documenting verified false positives only.

Both tools exit non-zero when they find issues at or above your configured severity. Configure your pipeline to fail the job on any non-zero exit unless you explicitly use soft-fail during a transition period.

Yes. Aqua Security maintains tfsec and integrates its rules into the broader Trivy ecosystem for container and IaC scanning. Many teams still run the standalone tfsec binary in Terraform-only pipelines because it remains fast, focused, and well documented on its GitHub repository. Documentation and rule references are actively maintained alongside Trivy guidance. For a Terraform-only repository where you want a lightweight security gate without cloud credentials, the standalone binary remains a practical choice in 2026 rather than a deprecated workaround you should rush to replace.

Both scanners are open source with low per-run cost, typically seconds on typical modules. Initial setup takes less than an hour for a standard module layout.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: