Scanning Terraform With Checkov and tfsec, Then Fixing With AI
Scan Terraform with Checkov and tfsec, emit SARIF in CI, manage skip comments, and let AI triage the findings to draft remediations a human always reviews.
- #terraform
- #security
- #checkov
- #tfsec
- #ai
The first time I ran a static scanner against a Terraform repo I thought I had a small backlog of cleanup work. Checkov returned 140 failed checks across modules I had personally reviewed and approved. Some were noise. A handful were genuinely going to leak data. The hard part was never running the scanner — it was triaging that wall of findings, deciding which ones mattered, and writing the corrected HCL without breaking everything downstream.
That triage-and-fix loop is exactly where a language model earns its keep. Not as an autopilot that rewrites your infrastructure, but as a fast junior engineer who reads every finding, drafts a patch, and explains its reasoning so you can approve or reject it in seconds. This post walks through the scanners I rely on, how I wire them into CI, and the disciplined way I hand findings to AI.
Why two scanners, not one
Checkov and tfsec overlap, but they disagree often enough that running both is worth it. Checkov (by Prisma Cloud) has a huge policy library and understands Terraform plan output, modules, and even Helm and CloudFormation. tfsec — now folded into Trivy — is fast, has tight AWS/Azure/GCP coverage, and its rule IDs read cleanly in diffs.
I treat them as a quorum. If both flag the same resource, it is almost always real. If only one does, it goes to the triage pile. Start simple:
# Checkov, human-readable compact output
checkov -d . --compact
# tfsec, or its successor Trivy in config mode
tfsec .
trivy config .
--compact strips the guideline URLs and ASCII art so you get one line per finding — ideal for skimming locally before you commit to a fix.
A finding worth fixing
Here is a stripped-down version of the kind of thing scanners catch. An S3 bucket with no server-side encryption and a security group flung wide open:
resource "aws_s3_bucket" "logs" {
bucket = "acme-app-logs"
}
resource "aws_security_group" "web" {
name = "web-sg"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
Checkov flags CKV_AWS_19 (bucket encryption) and CKV_AWS_24 (SSH open to the world); tfsec raises aws-ec2-no-public-ingress-sgr. The corrected HCL is not exotic — it just has to be written carefully so it does not break the bucket’s consumers:
resource "aws_s3_bucket" "logs" {
bucket = "acme-app-logs"
}
resource "aws_s3_bucket_server_side_encryption_configuration" "logs" {
bucket = aws_s3_bucket.logs.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
}
}
}
resource "aws_security_group" "web" {
name = "web-sg"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [var.bastion_cidr] # was 0.0.0.0/0
}
}
The encryption fix is mechanical. The security group fix is a judgment call — you need to know where SSH should come from. That distinction is the whole reason a human stays in the loop.
SARIF output for CI and code scanning
Eyeballing compact output is fine on your laptop. In CI you want a machine format, and SARIF is the lingua franca. GitHub’s code scanning, GitLab, and most dashboards ingest it directly, which means findings show up as annotations on the pull request instead of buried in log scrollback.
# Checkov SARIF into the current directory (results.sarif)
checkov -d . -o sarif --output-file-path .
# Trivy SARIF
trivy config --format sarif --output trivy.sarif .
A minimal GitHub Actions step looks like this:
- name: Checkov scan
uses: bridgecrewio/checkov-action@v12
with:
directory: .
output_format: sarif
output_file_path: results.sarif
soft_fail: true # report, do not block — yet
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
I keep soft_fail: true while a team adopts scanning, so the pipeline surfaces findings without blocking every merge on day one. Once the baseline is clean, flip it to hard fail and let new findings break the build.
Pro Tip: Run the scan against terraform plan -out JSON, not just raw .tf files. Checkov can evaluate resolved variables and module inputs from a plan, which kills a whole class of false positives where a value “looks” insecure in HCL but is overridden at apply time.
Suppressions you can defend
Not every finding is a fix. Sometimes a public-read bucket genuinely needs to be public, or a check does not apply to your environment. Both tools support inline suppressions, and the rule is simple: every suppression carries a reason a reviewer can read.
resource "aws_s3_bucket" "public_assets" {
#checkov:skip=CKV_AWS_20:Static site assets are intentionally public, fronted by CloudFront WAF
bucket = "acme-public-assets"
}
tfsec uses the same idea with #tfsec:ignore:aws-s3-enable-bucket-encryption. I treat an unexplained skip as a failing finding in review — a suppression without a justification is just a silenced alarm. Keep them inline and next to the resource so they travel with the code and show up in the diff when someone changes that resource later.
Handing findings to AI
This is where the workflow gets fast. I export the SARIF, plus the relevant .tf snippets, and give the model a tightly scoped job: triage and draft, never apply.
A prompt I reuse, roughly:
You are reviewing static-analysis findings for Terraform. For each finding, output: (1) severity in context, (2) whether it is a true positive or likely false positive and why, (3) a proposed HCL diff to remediate, or a justified skip comment if suppression is correct. Do not invent resources. Flag anything that needs human domain knowledge (CIDR allowlists, KMS key ownership). Here are the findings and the relevant files.
The model is excellent at the mechanical 80%: adding aws_s3_bucket_server_side_encryption_configuration blocks, enabling versioning, tightening IAM wildcards into named actions. It produces a clean diff and a short rationale per finding faster than I can read the rule documentation. Tools like Claude, Cursor, or GitHub Copilot all handle this well when the findings and source are pasted in as context. I keep the exact wording in a saved prompt so the output format stays consistent across runs.
What it is bad at is precisely the 20% that matters most: it does not know your bastion’s CIDR, which KMS key your compliance team mandates, or whether that “public” bucket is intentional. So the rule is non-negotiable.
Pro Tip: Never give the model your state file, cloud credentials, or apply permissions. It reads HCL and SARIF and writes diffs to your working tree — nothing more. The AI proposes; terraform plan and a human dispose. If you want this loop running against pull requests automatically, a gated code review flow keeps the draft-and-review pattern enforced rather than optional.
Closing the loop: plan, review, apply
The full cycle I run looks like this:
checkov -d . -o sarifandtrivy config --format sarifin CI, results uploaded to code scanning.- AI triages the SARIF and drafts diffs plus reasoning into the PR.
- A human reads every diff, accepts the mechanical ones, and makes the judgment calls on CIDRs and keys.
terraform planruns against the merged change; the plan, not the model, is the source of truth.
The AI compresses hours of documentation-reading and boilerplate into minutes, but it never touches state and never merges itself. Treat it like the sharpest, fastest junior on the team: hand it the boring 80%, review its work like you would anyone’s, and keep your hands on the wheel for the decisions that need real-world context.
If you want a head start on the prompts and CI snippets, I keep curated versions in the Terraform category and a downloadable set in the prompt packs store.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.