
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
Infrastructure as Code failures rarely stem from syntax errors alone; they usually result from subtle misconfigurations or security gaps that standard validation misses. Integrating tflint and tfsec in your pipeline provides the necessary defense layers by combining deep linting with dedicated security policy enforcement before you ever run a plan. This guide walks through the exact configuration needed to make these tools effective gatekeepers in your CI workflow, ensuring your infrastructure is both valid and secure.
Why do you need both tflint and tfsec in your pipeline?
A common mistake I see when teams adopt infrastructure as code with Terraform is assuming that terraform validate is sufficient quality control. It is not. Validate only checks HCL syntax and internal consistency; it does not know if your S3 bucket is public or if your security group allows unrestricted SSH access. You need specialized tooling to bridge this gap.
TFLint focuses on correctness, style, and provider-specific best practices. It catches issues like invalid instance types, deprecated attributes, and naming convention violations that technically parse correctly but will fail during apply or cause operational debt. TFSec, conversely, is a security-first scanner that evaluates your configuration against compliance frameworks like CIS, NIST, and SOC 2. It identifies exposed secrets, missing encryption, and overly permissive network rules.
Using them together covers the full spectrum of pre-deployment risk. TFLint ensures your code works as intended; tfsec ensures it doesn't introduce unacceptable security posture. In my experience helping organizations achieve ISO 27001 certification, this combination is often the minimum viable control set for automated infrastructure governance.
How do you configure tflint for multi-provider environments?
TFLint requires explicit plugin configuration to understand provider-specific resources. Without plugins, it can only lint core HCL syntax. Create a .tflint.hcl file in your repository root to define rules and enable provider inspection.
# .tflint.hcl
config {
module = true
force = false
}
plugin "aws" {
enabled = true
version = "0.38.0"
source = "github.com/terraform-linters/tflint-ruleset-aws"
}
plugin "google" {
enabled = true
version = "0.32.0"
source = "github.com/terraform-linters/tflint-ruleset-google"
}
rule "terraform_deprecated_interpolation" {
enabled = true
}
rule "terraform_unused_declarations" {
enabled = true
}
rule "terraform_naming_convention" {
enabled = true
format = "snake_case"
} Installing and running locally
Before adding tflint to CI, verify your configuration locally. Install the binary and initialize plugins to download the correct rule sets:
- Run
tflint --initto fetch configured plugins - Execute
tflint --recursiveto scan all modules in subdirectories - Use
--fixflag for auto-correctable issues like formatting
In practice, pin plugin versions explicitly as shown above. Floating versions can introduce breaking rule changes between developer machines and CI runners, causing frustrating "works on my machine" discrepancies. For teams managing multiple environments, consider storing this config in a shared module or using Terragrunt to inject consistent linting standards across repositories.
How do you tune tfsec to reduce false positives?
TFSec ships with hundreds of built-in checks. Running it without configuration on an existing codebase typically generates overwhelming noise. The key to sustainable adoption is aggressive tuning based on your actual risk profile and compliance requirements.
# .tfsec/config.yml
minimum_severity: MEDIUM
exclude:
- aws-s3-enable-bucket-encryption # Managed by KMS policy elsewhere
- generic-secrets-no-plaintext # Using Vault references
include:
- aws-*
- azure-storage-*
custom_checks:
- code: CUS001
description: All EC2 instances must have CostCenter tag
severity: LOW
resource_type: aws_instance
required_labels:
- tags.CostCenter Managing exclusions responsibly
Never globally disable high-severity security checks without documented justification. Instead, use inline ignores with explanatory comments directly in your Terraform code. This keeps the audit trail adjacent to the decision:
resource "aws_s3_bucket" "logs" {
bucket = "app-logs-archive"
# tfsec:ignore:aws-s3-enable-versioning
# Versioning disabled intentionally - logs are immutable
# and replicated to DR region via cross-account replication
} This approach satisfies auditors because the rationale travels with the code. When reviewing PRs, engineers can immediately see why a check was bypassed rather than hunting through external documentation. For teams pursuing DevSecOps shift-left practices, this transparency is essential for maintaining security velocity without sacrificing governance.
What is the difference between tflint and tfsec capabilities?
Understanding the distinct roles of each tool prevents redundant configuration and ensures comprehensive coverage. While there is some overlap in basic checks, their primary functions serve different purposes in the validation chain.
| Capability | TFLint | TFSec |
|---|---|---|
| Primary Focus | Code quality, syntax, best practices | Security vulnerabilities, compliance |
| Provider Awareness | Deep (via plugins, validates attribute values) | Broad (pattern matching, metadata analysis) |
| Custom Rules | Rego-based custom rulesets | YAML custom checks, Rego policies |
| Module Inspection | Recursive module analysis supported | Scans referenced modules automatically |
| SARIF Output | Supported for GitHub/GitLab integration | Native SARIF support for code scanning |
| Auto-fix Capability | Limited (formatting, simple fixes) | None (advisory only) |
| Compliance Frameworks | Best practice oriented | CIS, NIST, SOC2, HIPAA, PCI-DSS |
In production environments, I treat tflint as a mandatory quality gate that should block merges on any error-level finding. TFSec operates as a security advisory layer where HIGH and CRITICAL findings block deployment, but MEDIUM issues may generate warnings tracked in your backlog. This tiered approach balances safety with delivery velocity, especially important for teams managing multiple environments in IaC where risk tolerance varies between staging and production.
How do you integrate tflint and tfsec in GitHub Actions CI?
Automating these checks in CI removes human inconsistency from the review process. Below is a production-tested GitHub Actions workflow that runs both tools with proper caching and artifact generation.
# .github/workflows/terraform-validate.yml
name: Terraform Validation
on:
pull_request:
paths:
- '/*.tf'
- '.tflint.hcl'
- '.tfsec/'
jobs:
lint-and-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache TFLint plugins
uses: actions/cache@v4
with:
path: ~/.tflint.d/plugins
key: tflint-plugins-${{ hashFiles('.tflint.hcl') }}
- name: Run TFLint
uses: terraform-linters/setup-tflint@v4
with:
tflint_version: v0.55.0
- run: |
tflint --init
tflint --recursive --format compact
- name: Run TFSec
uses: aquasecurity/[email protected]
with:
soft_fail: false
format: sarif
output_file: tfsec-results.sarif
- name: Upload TFSec SARIF
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: tfsec-results.sarif Optimizing for monorepos and large codebases
If your repository contains multiple Terraform roots, naive recursive scanning wastes time re-evaluating unchanged modules. Use directory filtering to target only modified paths:
- Generate changed directories using
dorny/paths-filteraction - Loop through affected roots with matrix strategy
- Cache plugin downloads aggressively to avoid rate limits
- Set appropriate timeouts to prevent hung jobs on large scans
For organizations with strict compliance requirements, archive the SARIF outputs as build artifacts. These serve as timestamped evidence for audits, proving that security scanning occurred on every change. When preparing for SOC 2 or ISO 27001 assessments, having automated proof of continuous validation significantly reduces auditor questioning and evidence collection effort.
Making Static Analysis Sustainable Long-Term
The technical setup of tflint and tfsec in your pipeline is straightforward; the organizational challenge is preventing tool fatigue. Start with permissive configurations that warn rather than fail, giving teams time to remediate existing debt. Establish a regular cadence—perhaps quarterly—to tighten rules as baseline quality improves. Track metrics like mean time to resolution for security findings and false positive rates to demonstrate value to stakeholders.
Remember that these tools augment, not replace, human judgment. They excel at catching known patterns and enforcing consistency at scale, but architectural decisions still require experienced engineers who understand business context. If you're building out your IaC validation strategy or need help tuning these tools for compliance frameworks, reach out to discuss your specific infrastructure challenges.