Production-grade platform engineering handbook — Kubernetes, Terraform, Flux CD, GitHub Actions, AWS, and more.
67
84%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Passed
No known issues
Write, test, audit, debug, and migrate Kyverno policies using the new CEL-based policy types.
All new policies use apiVersion: policies.kyverno.io/v1. Legacy ClusterPolicy (kyverno.io/v1) still works but is deprecated in v1.17 and planned for removal in v1.20.
When invoked with no arguments, ask before proceeding:
Q1 — Mode?
What do you need?
1. generate — write a new production-ready Kyverno policy
2. test — write kyverno-test.yaml fixtures and run kyverno-cli
3. audit — analyse PolicyReport data from a running cluster
4. debug — diagnose why a policy is not behaving as expected
5. migrate — convert a legacy ClusterPolicy or PodSecurityPolicy
Enter 1–5 or mode name:Q2 — Context (after mode selected, one at a time):
Describe the policy — what should it validate, mutate, or enforce? (e.g. "require app.kubernetes.io/team label on all Deployments"):Paste or describe the policy to test:Paste the PolicyReport JSON or run: kubectl get policyreport -A -o json | jq '[.items[].results[] | select(.result == "fail")]'Describe the symptom — is the policy not blocking, not mutating, or not appearing in PolicyReport?Paste the existing ClusterPolicy or PodSecurityPolicy YAML to migrate:Then proceed into the relevant mode below.
Write a production-ready Kyverno policy from a description.
Steps:
validationActions: [Audit] unless the user explicitly requests Deny — blocking admission with an untested policy is high blast radiusapiVersion: policies.kyverno.io/v1annotations block: policies.kyverno.io/title, category, severity, descriptionmatchConstraints.resourceRules targeting only the required kinds and operationsmatchConditions to exclude system namespaces (kube-system, kube-public, and platform tooling namespaces) — this replaces the old exclude blockValidatingPolicy: validations with CEL boolean expressions; use messageExpression for dynamic messages that include the resource nameMutatingPolicy: mutations with patchType: ApplyConfiguration (prefer for adds/merges) or patchType: JSONPatch (for precise path operations); use jsonpatch.escapeKey() for special characters in pathsGeneratingPolicy: variables with dyn() for inline resource definitions; generate with generator.Apply(namespace, [resources]); set evaluation.synchronize.enabled: trueImageValidatingPolicy: matchImageReferences with glob or CEL; attestors with cosign keyless or key-based; validations using verifyImageSignatures() CEL functionkyverno apply <policy.yaml> --resource <manifest.yaml> --detailed-resultsReference: references/kyverno.md → ValidatingPolicy, MutatingPolicy, GeneratingPolicy, ImageValidatingPolicy
Write a kyverno-test.yaml manifest and companion resource fixtures.
Steps:
matchConstraints resource rules, matchConditions, and what each validation expression approves vs deniesmatchConditions exclude a namespace, a resource in an excluded namespace (result: skip)kyverno-test.yaml:
name: <policy-name>-test
policies:
- <policy-file>.yaml
resources:
- resources/<passing-manifest>.yaml
- resources/<failing-manifest>.yaml
results:
- policy: <policy-name>
rule: <validation-name-or-autogen>
resource: <passing-resource-name>
kind: <Kind>
result: pass
- policy: <policy-name>
rule: <validation-name-or-autogen>
resource: <failing-resource-name>
kind: <Kind>
result: failkyverno test . — all results must matchresource.Get() / resource.List() (used in GeneratingPolicy) are not available in CLI tests — those require cluster-side testingReference: references/kyverno.md → kyverno-cli Testing
Analyse PolicyReport data from a running cluster and produce an actionable violation summary.
Steps:
kubectl get policyreport -A -o json \
| jq '[.items[].results[] | select(.result == "fail")]'
kubectl get clusterpolicyreport -o json \
| jq '[.items[].results[] | select(.result == "fail")]'validationActionskubectl patch validatingpolicy <name> \
--type merge \
-p '{"spec":{"validationActions":["Deny"]}}'[Deny] mode with active PolicyReport violations — indicates a suppressed PolicyException that needs reviewReference: references/kyverno.md → Audit → Enforce Promotion, PolicyReport
Diagnose why a Kyverno policy is not behaving as expected.
Steps:
kubectl get validatingpolicy <name> -o yamlkubectl get <kind> <name> -n <ns> -o yamlkubectl describe <kind> <name> -n <ns>kubectl logs -n kyverno -l app.kubernetes.io/component=admission-controller --tail=100kubectl logs -n kyverno -l app.kubernetes.io/component=background-controller --tail=100kubectl get policyreport -n <ns> -o yamlkubectl get validatingwebhookconfigurations — if missing, Kyverno is not running or failed to registerspec.matchConstraints.resourceRules kinds, apiGroups, and operations against the actual resourcematchConditions expression against the resource manually with kyverno applyevaluation.background.enabled: trueevaluation.mutateExisting.enabled: truekyverno apply CLIkubectl get policyexception -A — check if an exception covers this resource and policykyverno apply <policy.yaml> --resource <manifest.yaml> --detailed-resultsReference: references/kyverno.md → Troubleshooting
Migrate from legacy ClusterPolicy (kyverno.io/v1) or PodSecurityPolicy to the new policy types.
Map each rule type to the new kind:
| Legacy rule type | New kind |
|---|---|
validate rule | ValidatingPolicy |
mutate rule | MutatingPolicy |
generate rule | GeneratingPolicy |
verifyImages rule | ImageValidatingPolicy |
Key syntax changes:
spec.rules[].match.any[].resources → spec.matchConstraints.resourceRules[]spec.rules[].exclude → spec.matchConditions with CEL negationvalidate.pattern (JMESPath anchors) → validations[].expression (CEL boolean)validate.deny.conditions → validations[].expression with inverted CELmutate.patchStrategicMerge → mutations[].patchType: ApplyConfiguration with Object{...} CELmutate.patchesJSON6902 → mutations[].patchType: JSONPatch with [JSONPatch{...}] CELgenerate.data / generate.clone → generate[].expression using generator.Apply() and resource.Get()verifyImages[].attestors → attestors[] + validations[].expression using verifyImageSignatures()validationFailureAction: Enforce → validationActions: [Deny]validationFailureAction: Audit → validationActions: [Audit]Migration workflow:
[Audit] mode[Deny]Map each PSP field to a ValidatingPolicy:
| PSP field | ValidatingPolicy CEL expression |
|---|---|
privileged: false | object.spec.containers.all(c, !has(c.securityContext) || !c.securityContext.?privileged.orValue(false)) |
hostNetwork: false | !has(object.spec.hostNetwork) || object.spec.hostNetwork == false |
runAsNonRoot: true | object.spec.containers.all(c, has(c.securityContext) && c.securityContext.runAsNonRoot == true) |
readOnlyRootFilesystem: true | object.spec.containers.all(c, has(c.securityContext) && c.securityContext.readOnlyRootFilesystem == true) |
Deploy all new policies in [Audit] mode first, fix workloads, then switch to [Deny] before removing PSPs.
Reference: references/kyverno.md → Common Policy Patterns
.claude-plugin
.github
commands
docs
examples
agent-self-improve
argocd
awesome-docs
aws
cloudfront
functions
lambda-edge
functions
azure
compliance
conventional-commits
datadog
llm-observability
demo
documentation
dora
dynatrace
fluxcd
github-actions
composite-actions
configure-cloud
db-migrate
docker-build-push
k8s-deploy
notify-slack
pr-comment
release-tag
security-scan
setup-env
setup-terraform
terraform-plan
helm
web-service
templates
kubernetes
kyverno
mcp
observability
openshift
pr-review
ownership
runtime-security
supply-chain
terraform
references
scripts
skills
platform-skills
tests