π‘οΈ Secure, Lint, and Validate Your Terraform Like a Pro

Table of contents
- π Introduction: Terraform Isnβt Just Code β Itβs Infrastructure
- π Terraform = Real Code + Real Risk
- β οΈ Real-World Pitfalls You Want to Avoid
- Letβs dive deep into what they are, how they help, and how to automate them in CI/CD π
- β Tool 1: tflint β The Terraform Linter
- π Tool 2: tfsec β Secure Terraform from the Start
- π‘οΈ Tool 3: checkov β Policy-as-Code & Compliance Gate
- π GitHub Actions Workflow β Plug & Play
- π§ͺ Sample main.tf to Try Locally
- π Why This Workflow Matters - 3 Layers of Protection
- π Real-World Wins After Adding These Tools
- π Closing Thoughts

βIt works, ship it!β β a phrase weβve all heard. But when it comes to Infrastructure as Code (IaC), that mindset can lead to serious security and compliance issues.
π Introduction: Terraform Isnβt Just Code β Itβs Infrastructure
In todayβs cloud-native environments, Infrastructure as Code (IaC) has become the backbone of scalable, consistent deployments. But with great power comes great responsibility.
A single misconfigured security group or a public S3 bucket can:
Expose sensitive data
Introduce downtime
Violate compliance standards
Lead to audit failures or breaches
π Terraform = Real Code + Real Risk
As DevOps, SRE, and Platform Engineers, we know Terraform is code. But itβs not just code β it builds cloud infrastructure.
π One security group open to the world?
π One IAM policy with *:*
?
π One public S3 bucket?
β οΈ Real-World Pitfalls You Want to Avoid
π« Scenario 1: βOops, S3 Bucket Left Publicβ
resource "aws_s3_bucket" "data" {
bucket = "my-sensitive-logs"
acl = "public-read"
}
Risk: Unencrypted, public bucket
Tool that catches it:
tfsec
,checkov
π« Scenario 2: Overly Permissive Security Group
resource "aws_security_group" "ssh_access" {
ingress {
from_port = 22
to_port = 22
cidr_blocks = ["0.0.0.0/0"]
}
}
Risk: Open SSH to the entire internet
Tool that flags it:
tfsec
,checkov
π« Scenario 3: IAM Wildcard Permissions
resource "aws_iam_policy" "admin_policy" {
policy = jsonencode({
Version = "2012-10-17",
Statement = [{
Action = "*",
Effect = "Allow",
Resource = "*"
}]
})
}
Risk: Full access, violates least privilege
Tool that blocks it:
checkov
with CIS/NIST rules
β‘οΈ Thatβs how breaches happen.
β‘οΈ Thatβs why I never deploy Terraform code without this golden trio:
I never merge a PR without these:
Tool Purpose Emoji tflint Lint for quality β tfsec Security misconfig scan π checkov Compliance & governance π‘οΈ
Letβs dive deep into what they are, how they help, and how to automate them in CI/CD π
β Tool 1: tflint β The Terraform Linter
π What is tflint?
tflint
is a static analysis linter for Terraform that catches bugs, anti-patterns, and style violations before you run terraform plan
or apply
.
π§ Why it Matters
Finds unused variables, typos, and invalid resource attributes
Helps you write clean, readable, and consistent Terraform code
Enforces provider-specific best practices (AWS, Azure, GCP)
π Common Issues Caught by tflint:
Using deprecated or invalid arguments
Misspelled variable or resource names
Misconfigured provider blocks
Poorly structured modules
tflint --init # Downloads plugins
tflint # Runs all checks
π§ Real-World Example:
Imagine a module with a typo like vpc_securty_group_ids
. Terraform will silently skip it unless caught. tflint
prevents these bugs early.
π More Info: TFLint Documentation
π Tool 2: tfsec β Secure Terraform from the Start
π What is tfsec?
tfsec
performs static analysis on your Terraform code to detect security vulnerabilities and misconfigurations.
It analyzes HCL files for patterns that can lead to:
Data leaks
Unintended access
Infrastructure exposure
π Example Findings:
Issue | Severity | Example |
S3 Bucket ACL set to public | High | "acl" = "public-read" |
SG open to the world on any port | Critical | "cidr_blocks = 0.0.0.0/0" |
EBS volume not encrypted | High | encrypted = false |
Secrets in user_data | Medium | Inline secrets in EC2 config |
π Command to Run:
tfsec .
You create a quick EC2 setup:
resource "aws_security_group" "example" {
ingress {
from_port = 22
to_port = 22
cidr_blocks = ["0.0.0.0/0"] # Dangerous
}
}
tfsec
will immediately flag this as a high-risk issue.
π‘οΈ Tool 3: checkov β Policy-as-Code & Compliance Gate
π What is checkov?
checkov
validates Terraform against security benchmarks and industry standards.
It enforces rules from:
π CIS AWS Foundations Benchmark
π‘οΈ NIST 800-53
π SOC 2, GDPR, HIPAA
π Custom YAML/Python-based policies
π Output includes:
Passed checks
Failed checks (with severity)
Skipped (manually ignored) checks
Suggestions and remediation tips
π Command to Run:
checkov -d .
π Sample Check:
resource "aws_s3_bucket" "public_logs" {
bucket = "my-bucket"
acl = "public-read" # checkov will flag this!
}
π‘ Bonus:
Shows passed β , failed β, skipped β οΈ
Works with multiple IaC formats: Terraform, CloudFormation, K8s YAML
π GitHub Actions Workflow β Plug & Play
Add to .github/workflows/terraform-checks.yml
:
name: Terraform Static Checks
on: [push, pull_request]
jobs:
terraform-lint-secure:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
- name: Install Security Tools
run: |
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.sh | bash
pip install checkov
- name: Run tflint
run: |
tflint --init
tflint
- name: Run tfsec
run: tfsec .
- name: Run checkov
run: checkov -d .
π‘ You can make these blocking steps by using GitHub's branch protection rules!
β
Optional: Add continue-on-error: false
to fail the PR if checks donβt pass.
π Example of tfsec
scanning output:
Run aquasecurity/tfsec-action@v1.0.0
/usr/bin/docker run --name febf3299d148674848491e8a8d0b99f8e1aeba_ab881b --label febf32 --workdir /github/workspace --rm -e "AWS_ACCESS_KEY_ID" -e "AWS_SECRET_ACCESS_KEY" -e "TERRAFORM_CLI_PATH" -e "INPUT_WORKING_DIRECTORY" -e "INPUT_VERSION" -e "INPUT_FORMAT" -e "INPUT_ADDITIONAL_ARGS" -e "INPUT_SOFT_FAIL" -e "HOME" -e "GITHUB_JOB" -e "GITHUB_REF" -e "GITHUB_SHA" -e "GITHUB_REPOSITORY" -e "GITHUB_REPOSITORY_OWNER" -e "GITHUB_REPOSITORY_OWNER_ID" -e "GITHUB_RUN_ID" -e "GITHUB_RUN_NUMBER" -e "GITHUB_RETENTION_DAYS" -e "GITHUB_RUN_ATTEMPT" -e "GITHUB_ACTOR_ID" -e "GITHUB_ACTOR" -e "GITHUB_WORKFLOW" -e "GITHUB_HEAD_REF" -e "GITHUB_BASE_REF" -e "GITHUB_EVENT_NAME" -e "GITHUB_SERVER_URL" -e "GITHUB_API_URL" -e "GITHUB_GRAPHQL_URL" -e "GITHUB_REF_NAME" -e "GITHUB_REF_PROTECTED" -e "GITHUB_REF_TYPE" -e "GITHUB_WORKFLOW_REF" -e "GITHUB_WORKFLOW_SHA" -e "GITHUB_REPOSITORY_ID" -e "GITHUB_TRIGGERING_ACTOR" -e "GITHUB_WORKSPACE" -e "GITHUB_ACTION" -e "GITHUB_EVENT_PATH" -e "GITHUB_ACTION_REPOSITORY" -e "GITHUB_ACTION_REF" -e "GITHUB_PATH" -e "GITHUB_ENV" -e "GITHUB_STEP_SUMMARY" -e "GITHUB_STATE" -e "GITHUB_OUTPUT" -e "RUNNER_OS" -e "RUNNER_ARCH" -e "RUNNER_NAME" -e "RUNNER_ENVIRONMENT" -e "RUNNER_TOOL_CACHE" -e "RUNNER_TEMP" -e "RUNNER_WORKSPACE" -e "ACTIONS_RUNTIME_URL" -e "ACTIONS_RUNTIME_TOKEN" -e "ACTIONS_CACHE_URL" -e "ACTIONS_RESULTS_URL" -e GITHUB_ACTIONS=true -e CI=true -v "/var/run/docker.sock":"/var/run/docker.sock" -v "/home/runner/work/_temp/_github_home":"/github/home" -v "/home/runner/work/_temp/_github_workflow":"/github/workflow" -v "/home/runner/work/_temp/_runner_file_commands":"/github/file_commands" -v "/home/runner/work/tictocgame-app/tictocgame-app":"/github/workspace" febf32:99d148674848491e8a8d0b99f8e1aeba
+ TFSEC_VERSION=latest
+ '[' latest '!=' latest ']'
++ wget -q https://api.github.com/repos/aquasecurity/tfsec/releases/latest -O -
++ grep -o -E 'https://.+?tfsec-linux-amd64'
++ head -n1
+ wget -O - -q https://github.com/aquasecurity/tfsec/releases/download/v1.28.14/tfsec-linux-amd64
+ install tfsec /usr/local/bin/
+ '[' -n /github/workspace ']'
+ cd /github/workspace
+ '[' -n '' ']'
+ '[' -n '' ']'
+ FORMAT=default
+ tfsec --format=default .
======================================================
tfsec is joining the Trivy family
tfsec will continue to remain available
for the time being, although our engineering
attention will be directed at Trivy going forward.
You can read more here:
https://github.com/aquasecurity/tfsec/discussions/1994
======================================================
timings
ββββββββββββββββββββββββββββββββββββββββββ
disk i/o 14.317Β΅s
parsing 154.272Β΅s
adaptation 81.464Β΅s
checks 2.895977ms
total 3.14603ms
counts
ββββββββββββββββββββββββββββββββββββββββββ
modules downloaded 0
modules processed 1
blocks processed 3
files read 1
results
ββββββββββββββββββββββββββββββββββββββββββ
passed 1
ignored 0
critical 0
high 0
medium 0
low 0
No problems detected!
π§ͺ Sample main.tf
to Try Locally
provider "aws" {
region = "us-east-1"
}
resource "aws_s3_bucket" "my_bucket" {
bucket = "public-test-bucket"
acl = "public-read"
}
resource "aws_security_group" "bad_sg" {
ingress {
from_port = 22
to_port = 22
cidr_blocks = ["0.0.0.0/0"]
}
}
Run all tools:
tflint
tfsec .
checkov -d .
π Why This Workflow Matters - 3 Layers of Protection
Tool | Role | Key Value |
β tflint | Linting & Best Practices | Clean, typo-free code |
π tfsec | Security Vulnerability Scans | Detects open/public resources |
π‘ checkov | Compliance & Policy Checks | Enforces standards like CIS/NIST |
Together, they:
β
Prevent misconfigurations
π Shift security left
π Ensure compliance by design
π Real-World Wins After Adding These Tools
βοΈ Fewer post-merge hotfixes
βοΈ Better security posture out-of-the-box
βοΈ Faster reviews: Devs catch issues before PR
βοΈ Happier security & compliance teams
βοΈ Infrastructure thatβs ready for audit
π Closing Thoughts
Every terraform plan
should feel like a final step, not a gamble.
Start treating your infrastructure as real production code β because it is.
π Use tflint
, tfsec
, and checkov
in every PR.
π¬ How do you secure your Terraform deployments?
Letβs share tips in the comments π
Subscribe to my newsletter
Read articles from Venkata Pavan Vishnu Rachapudi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Venkata Pavan Vishnu Rachapudi
Venkata Pavan Vishnu Rachapudi
I'm Venkata Pavan Vishnu, a cloud enthusiast with a strong passion for sharing knowledge and exploring the latest in cloud technology. With 3 years of hands-on experience in AWS Cloud, I specialize in leveraging cloud services to deliver practical solutions and insights for real-world scenarios. Whether it's through engaging content, cloud security best practices, or deep dives into storage solutions, I'm dedicated to helping others succeed in the ever-evolving world of cloud computing. Let's connect and explore the cloud together!