
September 10, 2026
12 min read
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.
.tflint.hcl and tfsec ignore files so scans stay fast and consistent across local and CI runs.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.
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.
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.
Recommended stage order
terraform fmt -check— fast, no plugins.terraform init -backend=falseplusvalidate— catches HCL structure errors.- TFLint — provider-aware lint rules.
- tfsec — security static analysis.
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.
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.
| Tool | Primary focus | Best for | Typical CI cost |
|---|---|---|---|
| TFLint | Terraform lint + provider rules | Catching invalid configs before plan | Low (seconds) |
| tfsec | Terraform security misconfigurations | Fast security gate, no cloud creds | Low (seconds) |
| Checkov | Multi-framework IaC policies | Policy-as-code across many file types | Medium |
| Terrascan | OPA/Rego policy engine for IaC | Custom org policies at scale | Medium–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.
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
.tffiles, not only on main after merge. - Commit
.tflint.hcland a documentedtfsec.ymlso local runs match CI exactly. - Order stages as fmt → validate → TFLint + tfsec (parallel) → plan → apply with separate credentials.
- Start with tfsec
--minimum-severity HIGHon 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
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.

