From qa-iac
Configures policy-as-code testing using OPA / Conftest / Cedar - authors policies in Rego (OPA''''s language), runs Conftest against Kubernetes manifests / Terraform plans / Dockerfiles / arbitrary structured data, integrates with CI for PR-time policy gates. Per OPA''''s docs: "an open source, general-purpose policy engine that unifies policy enforcement across the stack." Use to express + enforce custom policies (cost limits, tagging requirements, security baselines) that Checkov / tfsec / KICS don''''t cover.
How this skill is triggered — by the user, by Claude, or both
Slash command
/qa-iac:policy-as-code-runnerThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
[opa]: https://www.openpolicyagent.org/docs/
Conftest is a CLI that uses OPA (opa-docs) to test structured configuration files (Kubernetes manifests, Terraform plans, Dockerfiles, etc.) against Rego policies. This skill covers Conftest invocation + Rego policy authoring as the custom policy layer alongside Checkov / tfsec / KICS (which carry built-in checks).
:latest tags, no apt-get upgrade, etc.).# macOS
brew install conftest
# Or download binary
curl -LO https://github.com/open-policy-agent/conftest/releases/latest/download/conftest_<version>_Linux_x86_64.tar.gz
# policies/kubernetes/required_labels.rego
package main
# All pods must have these labels
required_labels := {"app", "version", "owner"}
deny[msg] {
input.kind == "Deployment"
label := required_labels[_]
not input.spec.template.metadata.labels[label]
msg := sprintf("Deployment '%s' missing required label '%s'", [input.metadata.name, label])
}
# All deployments must specify resource limits
deny[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
not container.resources.limits.memory
msg := sprintf("Container '%s' in deployment '%s' missing memory limit", [container.name, input.metadata.name])
}
deny[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
not container.resources.limits.cpu
msg := sprintf("Container '%s' in deployment '%s' missing CPU limit", [container.name, input.metadata.name])
}
The deny rule pattern: each deny[msg] rule that holds adds a
message to the deny set. If deny is non-empty, the test fails.
# Test a single manifest
conftest test deployment.yaml
# Test all manifests
conftest test manifests/
# With specific policy directory
conftest test deployment.yaml --policy ./policies/
# JSON output for CI parsing
conftest test deployment.yaml --output json
helm template myrelease charts/mychart/ -f values.yaml | conftest test -
The chart renders to manifests; Conftest validates them.
terraform plan -out=plan.tfplan
terraform show -json plan.tfplan > plan.json
conftest test plan.json --policy policies/terraform/
# policies/terraform/cost_center_tag.rego
package main
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_instance"
not resource.change.after.tags.cost_center
msg := sprintf("EC2 instance '%s' missing cost_center tag", [resource.address])
}
# policies/dockerfile/no_latest.rego
package main
deny[msg] {
input[i].Cmd == "from"
val := input[i].Value
contains(val[0], ":latest")
msg := sprintf("Avoid :latest tag in FROM (line %d)", [i])
}
deny[msg] {
input[i].Cmd == "run"
val := input[i].Value[0]
contains(val, "apt-get upgrade")
msg := sprintf("Avoid 'apt-get upgrade' (line %d) — pin specific package versions", [i])
}
conftest test Dockerfile
Policies themselves should be tested:
# policies/kubernetes/required_labels_test.rego
package main
test_deployment_missing_labels {
deny[_] with input as {
"kind": "Deployment",
"metadata": {"name": "test"},
"spec": {"template": {"metadata": {"labels": {}}}}
}
}
test_deployment_with_all_labels {
count(deny) == 0 with input as {
"kind": "Deployment",
"metadata": {"name": "test"},
"spec": {"template": {"metadata": {"labels": {"app": "x", "version": "1.0", "owner": "team"}}}, "spec": {"containers": [{"resources": {"limits": {"memory": "512Mi", "cpu": "500m"}}}]}}
}
}
opa test policies/
jobs:
policy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- run: |
curl -L https://github.com/open-policy-agent/conftest/releases/download/v0.50.0/conftest_0.50.0_Linux_x86_64.tar.gz | tar xz
sudo mv conftest /usr/local/bin/
- name: Test policies
run: opa test policies/
- name: Conftest manifests
run: conftest test manifests/ --policy policies/
- name: Conftest Helm
run: helm template myrelease charts/mychart/ -f values.yaml | conftest test - --policy policies/
- name: Conftest Terraform
run: |
terraform plan -out=plan.tfplan
terraform show -json plan.tfplan > plan.json
conftest test plan.json --policy policies/terraform/
For runtime enforcement (vs PR-time):
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: requiredlabels
spec:
crd:
spec:
names: { kind: RequiredLabels }
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
# Same Rego from Step 2
Cluster admins install Gatekeeper; the constraint blocks Deployments that violate the policy at admission time.
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Untested policies | Policy bugs let bad config through; over-strict policies block good config. | opa test against test fixtures (Step 7). |
| Policies in production without PR-time gating | Surprises at admission time; deployments fail. | CI gate first (Step 8); Gatekeeper second. |
| One mega-policy file | Hard to reason about; merge conflicts. | Per-domain policy files. |
| Rego complexity for simple checks | Hard to maintain; Rego learning curve. | Use Checkov / tfsec for built-in checks; OPA for custom. |
| Skipping the negation tests | Policy may pass for the wrong reason. | Test both deny and not-deny cases (Step 7). |
bdd-step-library-curator),
policies can multiply; periodic review needed.conftest.dev.open-policy-agent.github.io/gatekeeper/.checkov-policy,
tfsec-policy,
kics-policy - sister scanners with
built-in checks (use these first; OPA for custom).Provides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.
npx claudepluginhub testland/qa --plugin qa-iac