Skip to content
DevOps AI ToolKit
Newsletter
All prompts
AI for Terraform Difficulty: Advanced ClaudeChatGPTCursor

Terraform Checkov Custom Policy Authoring Prompt

Write custom Checkov policies for Terraform — Python checks extending BaseResourceCheck and YAML-based policies, a .checkov.yaml config, inline skip suppressions and baselines, then wire soft-fail vs hard-fail gating into CI.

Target user
Security/platform engineers extending IaC scanning
Difficulty
Advanced
Tools
Claude, ChatGPT, Cursor

The prompt

You are an application-security engineer who writes custom Checkov policies and knows when Python is warranted over a YAML policy.

I will provide:
- The control I want to enforce (e.g. "S3 buckets must have a specific tag", "no security group open to 0.0.0.0/0 on 22")
- Resource type(s) and provider
- Where Checkov runs (local, pre-commit, CI) and how strict gating should be

Your job:

1. **Python vs YAML** — decide per control. Simple attribute assertions belong in a YAML custom policy (`definition` with `and`/`or`, `cond_type: attribute`). Anything needing logic, cross-attribute checks, or lists belongs in a Python check extending `BaseResourceCheck`. Tell me which for MY control.

2. **Python custom check** — write a class extending `checkov.terraform.checks.resource.base_resource_check.BaseResourceCheck`, with a unique `id` in a custom namespace (e.g. `CKV_ORG_1`), the `supported_resources` list, `categories`, and a `scan_resource_conf(self, conf)` that returns `CheckResult.PASSED`/`FAILED`. Remember Checkov wraps every attribute value in a list.

3. **YAML custom policy** — write the equivalent simple control as a YAML policy with `metadata.id`, `scope.provider`, and a `definition` block.

4. **`.checkov.yaml` config** — set `directory`, `external-checks-dir` (where custom checks live), `framework: terraform`, `compact`, and `output`. Explain `--external-checks-dir` vs config.

5. **Suppression + baseline** — inline `#checkov:skip=CKV_ORG_1:reason` comments for accepted risk, and `checkov --create-baseline` / `--baseline .checkov.baseline` so existing findings don't block while new ones do.

6. **Soft-fail vs hard-fail** — DESTRUCTIVE-to-velocity warning: `--soft-fail` reports without failing the build; hard-fail (default non-zero exit) BLOCKS merges. An over-strict custom policy at hard-fail can wall off every PR. Recommend soft-fail (or `--soft-fail-on` for the new rule's ID) during rollout, hard-fail after a clean baseline. Use `--hard-fail-on`/`--soft-fail-on` to gate by severity/ID.

7. **CI wiring** — the job that installs Checkov, points at `external-checks-dir`, applies the baseline, and sets the fail mode.

Output: (a) Python-vs-YAML decision, (b) the Python check, (c) the YAML policy, (d) `.checkov.yaml`, (e) suppression/baseline plan, (f) CI job with the gating strategy.

Bias toward: YAML for simple asserts, unique custom IDs, baseline over blanket skips, and soft-fail during rollout.

---
Input template:
```
Control to enforce:
Resource type(s) + provider:
Where Checkov runs today:
Gating (advisory / blocking):
Existing findings to baseline? (y/n):
```

Run this prompt with AI

Test it, get an AI-improved version, or compare models — live in the Prompt Workspace. No copy-paste.

Why this prompt works

The most common Checkov mistake is writing Python for a control that a five-line YAML policy handles, and the second most common is flipping a brand-new check straight to hard-fail and walling off the entire team. This prompt forces the Python-vs-YAML decision per control and makes the rollout gating explicit — soft-fail with a baseline first, hard-fail once the codebase is clean. It also captures the one Checkov API gotcha that trips everyone: attribute values in conf are always wrapped in a list.

How to use it

  1. State the control precisely (resource, attribute, condition) in the template.
  2. Run the prompt and implement the YAML policy if the control is a plain attribute assertion.
  3. Drop custom checks into an external-checks-dir and confirm Checkov discovers them.
  4. Create a baseline so existing debt doesn’t block, and add inline skips only for genuinely accepted risk.
  5. Start at --soft-fail-on for the new ID, then promote to hard-fail.

Useful commands

# Scan a directory with custom checks loaded from a dir
checkov -d . --external-checks-dir ./policies

# Run a single custom check while iterating
checkov -d . --external-checks-dir ./policies --check CKV_ORG_1

# Establish a baseline so only NEW findings gate the build
checkov -d . --create-baseline
checkov -d . --baseline .checkov.baseline

# Report everything but never fail the build (rollout mode)
checkov -d . --external-checks-dir ./policies --soft-fail

# Gate hard only on high severity, keep the new custom rule advisory
checkov -d . --hard-fail-on HIGH --soft-fail-on CKV_ORG_1

Patterns

A Python custom check extending BaseResourceCheck — note every attribute value arrives wrapped in a list:

from checkov.common.models.enums import CheckCategories, CheckResult
from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck


class S3RequiresDataClassificationTag(BaseResourceCheck):
    def __init__(self):
        super().__init__(
            name="Ensure S3 buckets carry a data-classification tag",
            id="CKV_ORG_1",
            categories=[CheckCategories.CONVENTION],
            supported_resources=["aws_s3_bucket"],
        )

    def scan_resource_conf(self, conf):
        # conf["tags"] is [ { ... } ] — Checkov wraps values in a list.
        tags = conf.get("tags")
        if not tags or not isinstance(tags[0], dict):
            return CheckResult.FAILED
        if "data-classification" in tags[0]:
            return CheckResult.PASSED
        return CheckResult.FAILED


check = S3RequiresDataClassificationTag()

The equivalent simple control as a YAML custom policy plus the .checkov.yaml config:

# policies/s3_encryption.yaml
metadata:
  id: "CKV_ORG_2"
  name: "S3 bucket must set force_destroy = false"
  category: "GENERAL_SECURITY"
scope:
  provider: "aws"
definition:
  cond_type: "attribute"
  resource_types:
    - "aws_s3_bucket"
  attribute: "force_destroy"
  operator: "equals"
  value: false
# .checkov.yaml
directory:
  - .
framework:
  - terraform
external-checks-dir:
  - ./policies
compact: true
output:
  - cli
  - junitxml
soft-fail-on:
  - CKV_ORG_1   # new check stays advisory during rollout

Related prompts

More Terraform prompts & error guides

Browse every Terraform prompt and troubleshooting guide in one place.

Free download · 368-page PDF

Reading prompts? Get all 500 in one free PDF

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.