Custom policies

YAML and Python checks.

Intermediate14 min · lesson 6 of 12

Built-in checks cover the common cases, but every org has its own rules — “all resources must have a cost-center tag,” “only these instance types,” “no security group named legacy-*.” Checkov lets you write custom policies two ways: simple ones in YAML (a declarative attribute check), and complex ones in Python (full logic against the parsed resource). You point Checkov at your policy directory with --external-checks-dir.

policies/require_tags.yaml
metadata:
id: "CKV_ACME_1"
name: "Ensure resources have a cost-center tag"
category: "CONVENTION"
definition:
cond_type: "attribute"
resource_types: ["aws_instance", "aws_s3_bucket"]
attribute: "tags.cost_center"
operator: "exists"
terminal
$ checkov -d . --external-checks-dir ./policies
Check: CKV_ACME_1: "Ensure resources have a cost-center tag"
FAILED for resource: aws_s3_bucket.logs

YAML vs Python, and reuse

Use YAML policies for straightforward attribute/connection checks (a field equals/exists/matches) — they are readable and quick. Drop to Python when you need real logic: cross-resource relationships, computed conditions, custom messages. Either way, custom policies are code: keep them in a versioned repo, test them against fixtures, and distribute them as a shared pack so every team’s CI enforces the same org rules. This is where Checkov overlaps with OPA/Conftest — choose based on how much programmability you need.

Custom policies are security code — test them
A custom check with a logic bug can pass resources it should fail (a wrong operator, a typo in the attribute path), giving false confidence exactly where you added the rule to gain assurance. Test custom policies against known-good and known-bad fixtures, review them, and version them like any security-critical code — an untested custom check is worse than none because you trust it.