Skip to content

Advanced JMESPath Patterns for Kyverno

Advanced JMESPath techniques for complex Kyverno policies. Label transformations, array operations, and string parsing.

TL;DR

Advanced JMESPath enables sophisticated policy logic: dynamic validation based on labels, array filtering across resources, and string manipulation for naming conventions.


Pattern 5: Label and Annotation Transformations

Extract, transform, and validate metadata. Build dynamic rules based on labels.

Complete Policy

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: label-based-validation
spec:
  validationFailureAction: enforce
  background: true
  rules:
    - name: cost-center-must-match-namespace
      match:
        resources:
          kinds:
            - Deployment
            - StatefulSet
      validate:
        message: "cost-center label must match namespace prefix"
        deny:
          conditions:
            any:
              - key: "{{ request.object.metadata.labels.\"cost-center\" || '' }}"
                operator: NotEquals
                value: "{{ request.namespace | split(@, '-')[0] }}"

    - name: validate-label-format
      match:
        resources:
          kinds:
            - Pod
            - Deployment
      validate:
        message: "Environment label must be one of: dev, staging, prod"
        pattern:
          metadata:
            labels:
              environment: "dev | staging | prod"

Advanced: Dynamic Validation

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: dynamic-label-validation
spec:
  validationFailureAction: enforce
  background: true
  rules:
    - name: tier-based-replica-count
      match:
        resources:
          kinds:
            - Deployment
      validate:
        message: "High-tier deployments must have >= 3 replicas"
        deny:
          conditions:
            all:
              - key: "{{ request.object.metadata.labels.tier || '' }}"
                operator: Equals
                value: "high"
              - key: "{{ request.object.spec.replicas || `1` }}"
                operator: LessThan
                value: 3

Use Cases

  • Cost center tracking and validation
  • Environment-based policies (dev/staging/prod)
  • Replica requirements based on criticality labels
  • Owner/team validation against namespace

Pattern 6: Array Operations

Work with arrays: filter, count, check existence, validate all elements.

Complete Policy

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: array-operations
spec:
  validationFailureAction: enforce
  background: true
  rules:
    - name: no-container-uses-host-port
      match:
        resources:
          kinds:
            - Pod
            - Deployment
      validate:
        message: "Containers must not use host ports"
        deny:
          conditions:
            any:
              - key: "{{ request.object.spec.containers[?ports[?hostPort]] | length(@) }}"
                operator: GreaterThan
                value: 0

    - name: all-volumes-must-be-typed
      match:
        resources:
          kinds:
            - Pod
            - Deployment
      validate:
        message: "All volumes must specify a type (configMap, secret, persistentVolumeClaim, etc.)"
        deny:
          conditions:
            any:
              - key: "{{ request.object.spec.volumes[?!configMap && !secret && !persistentVolumeClaim && !emptyDir] | length(@) }}"
                operator: GreaterThan
                value: 0

Use Cases

  • Validate no container uses privileged ports
  • Ensure all volumes are properly typed
  • Check that all environment variables are from ConfigMaps/Secrets
  • Verify no container mounts hostPath volumes

Pattern 7: String Operations

Parse, split, and validate string values. Common for image strings, URLs, and formatted fields.

Complete Policy

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: string-operations
spec:
  validationFailureAction: enforce
  background: true
  rules:
    - name: validate-registry-region
      match:
        resources:
          kinds:
            - Pod
            - Deployment
      validate:
        message: "Images must use us-east or eu-west registries"
        foreach:
          - list: "request.object.spec.containers[]"
            deny:
              conditions:
                any:
                  - key: "{{ element.image | split(@, '.')[0] | contains(@, 'us-east') || element.image | split(@, '.')[0] | contains(@, 'eu-west') }}"
                    operator: Equals
                    value: false

    - name: validate-namespace-naming
      match:
        resources:
          kinds:
            - Namespace
      validate:
        message: "Namespace must follow team-environment-app pattern"
        deny:
          conditions:
            any:
              - key: "{{ request.object.metadata.name | split(@, '-') | length(@) }}"
                operator: NotEquals
                value: 3

Use Cases

  • Validate image registry regions
  • Parse and validate DNS names
  • Enforce naming conventions
  • Extract and validate URL schemes

Best Practices

1. Start Simple, Add Complexity

# Start with basic filter
spec.containers[?image == 'nginx']

# Add multiple conditions
spec.containers[?image == 'nginx' && securityContext.privileged == `true`]

# Extract and transform
spec.containers[?image == 'nginx'].name | [0]

2. Use Preconditions for Performance

# Only evaluate JMESPath if condition met
preconditions:
  any:
    - key: "{{ request.object.metadata.labels.tier || '' }}"
      operator: Equals
      value: "production"

3. Provide Helpful Error Messages

message: |
  Memory limits ({{ request.object.spec.containers[0].resources.limits.memory }})
  must be >= requests ({{ request.object.spec.containers[0].resources.requests.memory }})

4. Handle Null Values

# Provide defaults for optional fields
key: "{{ request.object.metadata.labels.tier || 'default' }}"

# Check for existence before accessing
conditions:
  all:
    - key: "{{ request.object.metadata.labels.tier || '' }}"
      operator: NotEquals
      value: ""

5. Test Before Deploying

Always test JMESPath expressions with kyverno jp CLI and use audit mode before enforce.

# Test expression
kyverno jp query 'expression'

# Use audit mode first
validationFailureAction: audit

Performance Optimization

Avoid Expensive Operations

# BAD: Multiple passes over large arrays
conditions:
  all:
    - key: "{{ request.object.spec.containers | length(@) }}"
      operator: GreaterThan
      value: 0
    - key: "{{ request.object.spec.containers[0].name }}"
      operator: Equals
      value: "app"

# GOOD: Single pass with filter
conditions:
  all:
    - key: "{{ request.object.spec.containers[?name == 'app'] | length(@) }}"
      operator: GreaterThan
      value: 0

Use Preconditions to Skip Evaluation

# Skip expensive JMESPath for non-production namespaces
preconditions:
  any:
    - key: "{{ request.namespace }}"
      operator: In
      value: ["prod-east", "prod-west"]

Cache Results with Variables

# Instead of repeating expensive expressions
context:
  - name: criticalContainers
    variable:
      jmesPath: request.object.spec.containers[?securityContext.privileged == `true`]

# Use cached variable
conditions:
  any:
    - key: "{{ criticalContainers | length(@) }}"
      operator: GreaterThan
      value: 0

Next Steps


External References

Comments