Kyverno Command
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.
Interactive Wizard (fires when no arguments are provided)
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):
- generate:
Describe the policy — what should it validate, mutate, or enforce? (e.g. "require app.kubernetes.io/team label on all Deployments"): - test:
Paste or describe the policy to test: - audit:
Paste the PolicyReport JSON or run: kubectl get policyreport -A -o json | jq '[.items[].results[] | select(.result == "fail")]' - debug:
Describe the symptom — is the policy not blocking, not mutating, or not appearing in PolicyReport? - migrate:
Paste the existing ClusterPolicy or PodSecurityPolicy YAML to migrate:
Then proceed into the relevant mode below.
Mode: generate
Write a production-ready Kyverno policy from a description.
Steps:
- Ask for: policy type (ValidatingPolicy / MutatingPolicy / GeneratingPolicy / ImageValidatingPolicy), target resource kinds, whether cluster-wide or namespace-scoped, and whether to start in Audit or Deny mode
- Start in
validationActions: [Audit]unless the user explicitly requests Deny — blocking admission with an untested policy is high blast radius - Generate the policy with:
apiVersion: policies.kyverno.io/v1annotationsblock:policies.kyverno.io/title,category,severity,descriptionmatchConstraints.resourceRulestargeting only the required kinds and operationsmatchConditionsto exclude system namespaces (kube-system,kube-public, and platform tooling namespaces) — this replaces the oldexcludeblock- For
ValidatingPolicy:validationswith CEL boolean expressions; usemessageExpressionfor dynamic messages that include the resource name - For
MutatingPolicy:mutationswithpatchType: ApplyConfiguration(prefer for adds/merges) orpatchType: JSONPatch(for precise path operations); usejsonpatch.escapeKey()for special characters in paths - For
GeneratingPolicy:variableswithdyn()for inline resource definitions;generatewithgenerator.Apply(namespace, [resources]); setevaluation.synchronize.enabled: true - For
ImageValidatingPolicy:matchImageReferenceswith glob or CEL;attestorswith cosign keyless or key-based;validationsusingverifyImageSignatures()CEL function
- Show the kyverno-cli command to dry-run:
kyverno apply <policy.yaml> --resource <manifest.yaml> --detailed-results
Reference: references/kyverno.md → ValidatingPolicy, MutatingPolicy, GeneratingPolicy, ImageValidatingPolicy
Mode: test
Write a kyverno-test.yaml manifest and companion resource fixtures.
Steps:
- Read the policy — identify: kind,
matchConstraintsresource rules,matchConditions, and what each validation expression approves vs denies - For each validation, create:
- A passing resource — a manifest that satisfies the CEL expression (result: pass)
- A failing resource — a manifest that violates the CEL expression (result: fail)
- If
matchConditionsexclude a namespace, a resource in an excluded namespace (result: skip)
- Build
kyverno-test.yaml:name: <policy-name>-testpolicies:- <policy-file>.yamlresources:- resources/<passing-manifest>.yaml- resources/<failing-manifest>.yamlresults:- 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: fail - Run:
kyverno test .— all results must match - Note: CEL
resource.Get()/resource.List()(used in GeneratingPolicy) are not available in CLI tests — those require cluster-side testing
Reference: references/kyverno.md → kyverno-cli Testing
Mode: audit
Analyse PolicyReport data from a running cluster and produce an actionable violation summary.
Steps:
- Collect violation data:
kubectl get policyreport -A -o json \| jq '[.items[].results[] | select(.result == "fail")]'kubectl get clusterpolicyreport -o json \| jq '[.items[].results[] | select(.result == "fail")]'
- Group violations by policy (highest severity first), then by resource kind — identify highest-volume policies first
- For each violated policy:
- State the policy name, severity, and current
validationActions - List affected resources (namespace/name)
- Assess fix effort: update the resource manifest, or use PolicyException for a legitimate carve-out?
- State the policy name, severity, and current
- Recommend remediation order: fix high-severity violations first; PolicyException is a last resort requiring documented justification
- Show the promote command for each zero-violation policy:
kubectl patch validatingpolicy <name> \--type merge \-p '{"spec":{"validationActions":["Deny"]}}'
- Flag any policy in
[Deny]mode with active PolicyReport violations — indicates a suppressed PolicyException that needs review
Reference: references/kyverno.md → Audit → Enforce Promotion, PolicyReport
Mode: debug
Diagnose why a Kyverno policy is not behaving as expected.
Steps:
- Collect:
- Policy YAML:
kubectl get validatingpolicy <name> -o yaml - Resource YAML:
kubectl get <kind> <name> -n <ns> -o yaml - Admission events:
kubectl describe <kind> <name> -n <ns> - Kyverno admission controller logs:
kubectl logs -n kyverno -l app.kubernetes.io/component=admission-controller --tail=100 - Background controller logs (for GeneratingPolicy/MutatingPolicy existing):
kubectl logs -n kyverno -l app.kubernetes.io/component=background-controller --tail=100 - PolicyReport:
kubectl get policyreport -n <ns> -o yaml
- Policy YAML:
- Check in order:
- Webhook not registered:
kubectl get validatingwebhookconfigurations— if missing, Kyverno is not running or failed to register - matchConstraints not covering the resource: compare
spec.matchConstraints.resourceRuleskinds, apiGroups, and operations against the actual resource - matchConditions filtering out the resource: evaluate each
matchConditionsexpression against the resource manually withkyverno apply - validationActions is [Audit] not [Deny]: policy reports violation but doesn't block — check PolicyReport, not admission events
- background scan not yet run: existing resources may not appear in PolicyReport until next background scan (default interval 1h); check
evaluation.background.enabled: true - MutatingPolicy mutateExisting not set: existing resources are not patched — set
evaluation.mutateExisting.enabled: true - CEL expression syntax error: look for CEL evaluation error in admission events; test the expression with
kyverno applyCLI - PolicyException suppressing:
kubectl get policyexception -A— check if an exception covers this resource and policy
- Webhook not registered:
- State the most likely root cause with the exact field to change
- Show the corrected policy section and the command:
kyverno apply <policy.yaml> --resource <manifest.yaml> --detailed-results
Reference: references/kyverno.md → Troubleshooting
Mode: migrate
Migrate from legacy ClusterPolicy (kyverno.io/v1) or PodSecurityPolicy to the new policy types.
From legacy ClusterPolicy
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.matchConditionswith CEL negationvalidate.pattern(JMESPath anchors) →validations[].expression(CEL boolean)validate.deny.conditions→validations[].expressionwith inverted CELmutate.patchStrategicMerge→mutations[].patchType: ApplyConfigurationwithObject{...}CELmutate.patchesJSON6902→mutations[].patchType: JSONPatchwith[JSONPatch{...}]CELgenerate.data/generate.clone→generate[].expressionusinggenerator.Apply()andresource.Get()verifyImages[].attestors→attestors[]+validations[].expressionusingverifyImageSignatures()validationFailureAction: Enforce→validationActions: [Deny]validationFailureAction: Audit→validationActions: [Audit]
Migration workflow:
- Write the new-style policy in
[Audit]mode - Deploy alongside the existing ClusterPolicy — both can coexist
- Verify PolicyReport shows identical violations from the new policy
- Switch new policy to
[Deny] - Remove the legacy ClusterPolicy
From PodSecurityPolicy
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