GeneratingPolicy
The GeneratingPolicy enables the creation of Kubernetes resources using Common Expression Language (CEL) expressions. It provides the same core functionality as Kyverno’s generate rules, but is designed with a CEL-first approach for improved flexibility, expressiveness, and alignment with Kubernetes’ evolving policy standards.
By leveraging CEL, GeneratingPolicy aligns with native Kubernetes constructs like ValidatingAdmissionPolicy and MutatingAdmissionPolicy, providing a consistent and powerful experience.
Key Benefits:
Declarative Generation: Define and manage resources using clear, concise CEL expressions directly within your policies.
Powerful Cloning: Easily clone existing resources like Secrets and ConfigMaps across namespaces using CEL-based accessors.
Robust Synchronization: Keep generated resources in sync with changes to the policy, the trigger, or the source resource.
Looping: Replicate the functionality of forEach loops to generate resources based on lists.
Kubernetes-Native Alignment: Uses a familiar, CEL-based approach that integrates smoothly with the broader Kubernetes ecosystem.
Core Concepts
Understanding these terms is key to working with GeneratingPolicy.
Term | Description |
---|---|
Trigger | The resource that initiates the generation process. It is defined by the matchConstraints in the policy. |
Downstream | The resource(s) generated by the policy as a result of a trigger event. |
Source | An existing resource that is used as a template for cloning. It is fetched using resource.Get or resource.List . |
A GeneratingPolicy is composed of several key fields within its spec.
Field | Description |
---|---|
evaluation | Configures the policy’s runtime behavior, including synchronize , generateExisting , and orphanDownstreamOnPolicyDelete . |
matchConstraints | Defines the Trigger resource. The policy activates when a resource matching these rules is created or updated. |
matchConditions | Optional CEL expressions that further filter the trigger resources. The policy only runs if these conditions are met. |
variables | A list of named CEL expressions that can be reused throughout the policy, simplifying complex logic. |
generate | A list of CEL expressions that execute the generation logic, typically using the generator.Apply() function. |
Data Source
The Data Source mode allows users to define the downstream resources directly within the policy using CEL expressions.
When to Use: Ideal for generating fixed or templated resources, such as default NetworkPolicies, RoleBindings, or configuration ConfigMaps, in response to a trigger (e.g., a new Namespace).
The below policy demonstrates how to generate a ConfigMap in response to a Namespace creation:
1apiVersion: policies.kyverno.io/v1alpha1
2kind: GeneratingPolicy
3metadata:
4 name: zk-kafka-address
5spec:
6 evaluation:
7 synchronize:
8 enabled: true
9 matchConstraints:
10 resourceRules:
11 - apiGroups: [""]
12 apiVersions: ["v1"]
13 operations: ["CREATE"]
14 resources: ["namespaces"]
15 variables:
16 - name: nsName
17 expression: "object.metadata.name"
18 - name: downstream
19 expression: >-
20 [
21 {
22 "kind": dyn("ConfigMap"),
23 "apiVersion": dyn("v1"),
24 "metadata": dyn({
25 "name": "zk-kafka-address",
26 "namespace": string(variables.nsName),
27 }),
28 "data": dyn({
29 "KAFKA_ADDRESS": "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092",
30 "ZK_ADDRESS": "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
31 })
32 }
33 ]
34 generate:
35 - expression: generator.Apply(variables.nsName, variables.downstream)
This policy is triggered whenever a Namespace is created. It captures the Namespace’s name in nsName
and defines the full ConfigMap object in downstream
. Then, the generator.Apply()
function creates the ConfigMap defined in downstream
inside the target namespace.
Clone Source
This mode copies an existing Kubernetes resource (the source) to create a new resource (the downstream). It makes use of resource.Get()
or resource.List()
to fetch source resources and clone them into the target namespace using generator.Apply()
.
When to Use: Perfect for distributing common resources like image pull secrets (regcred), TLS certificates, or standardized NetworkPolicies from a central namespace to other namespaces.
Clone Single Resource
To clone a single resource, you can use resource.Get()
to fetch the source resource and then apply it in the target namespace.
The following example demonstrates how to clone a Secret from the default
namespace to the target namespace:
1apiVersion: policies.kyverno.io/v1alpha1
2kind: GeneratingPolicy
3metadata:
4 name: generate-secret
5spec:
6 matchConstraints:
7 resourceRules:
8 - apiGroups: [""]
9 apiVersions: ["v1"]
10 operations: ["CREATE", "UPDATE"]
11 resources: ["namespaces"]
12 variables:
13 - name: nsName
14 expression: "object.metadata.name"
15 - name: source
16 expression: resource.Get("v1", "secrets", "default", "regcred")
17 generate:
18 - expression: generator.Apply(variables.nsName, [variables.source])
This policy is triggered whenever a Namespace is created. It captures the trigger’s namespace name in nsName
and uses resource.Get()
to fetch the regcred
Secret from the default
namespace, storing it in source
. Then, the generator.Apply()
takes the fetched secret and creates a copy of it in the new namespace.
Clone Multiple Resources
To clone multiple resources, you can use resource.List()
to fetch a list of resources and then apply them in the target namespace. The following example demonstrates how to clone all Secrets from the default
namespace:
1apiVersion: policies.kyverno.io/v1alpha1
2kind: GeneratingPolicy
3metadata:
4 name: generate-secrets
5spec:
6 matchConstraints:
7 resourceRules:
8 - apiGroups: [""]
9 apiVersions: ["v1"]
10 operations: ["CREATE", "UPDATE"]
11 resources: ["namespaces"]
12 variables:
13 - name: nsName
14 expression: "object.metadata.name"
15 - name: sources
16 expression: resource.List("v1", "secrets", "default")
17 generate:
18 - expression: generator.Apply(variables.nsName, [variables.sources])
This policy is triggered whenever a Namespace is created. It captures the trigger’s namespace name in nsName
and uses resource.List()
to fetch all Secrets from the default
namespace, storing them in sources
. The generator.Apply()
then creates copies of all fetched Secrets in the new namespace.
Looping
GeneratingPolicy supports forEach-style logic using CEL’s list processing capabilities. This is a powerful technique for generating multiple resources based on a dynamic list.
When to Use: When you need to generate resources across a list of namespaces defined in a central configuration, or create multiple, varied resources based on items in a list.
Looping with a Data Source
The below policy creates a NetworkPolicy into a list of existing namespaces which is stored as a comma-separated string in a ConfigMap.
1apiVersion: policies.kyverno.io/v1alpha1
2kind: GeneratingPolicy
3metadata:
4 name: generate-network-policy
5spec:
6 matchConstraints:
7 resourceRules:
8 - apiGroups: [""]
9 apiVersions: ["v1"]
10 operations: ["CREATE"]
11 resources: ["configmaps"]
12 variables:
13 - name: nsList
14 expression: "object.data.namespaces.split(',')"
15 - name: downstream
16 expression: >-
17 [
18 {
19 "kind": dyn("NetworkPolicy"),
20 "apiVersion": dyn("networking.k8s.io/v1"),
21 "metadata": dyn({
22 "name": "test-network-policy",
23 }),
24 "spec": dyn({
25 "podSelector": dyn({}),
26 "policyTypes": dyn(["Ingress", "Egress"])
27 })
28 }
29 ]
30 generate:
31 - expression: >
32 variables.nsList.all(ns, generator.Apply(ns, variables.downstream))
This policy is triggered whenever a ConfigMap is created. It captures the list of namespaces from the ConfigMap’s data field, splits it into a list, and stores it in nsList
and defines the full NetworkPolicy object in downstream
. It then uses variables.nsList.all(ns, ...)
to iterate over each namespace in nsList
, generating the NetworkPolicy defined in downstream
to each namespace.
Sometimes, you need to create uniquely named resources during a loop. By creating a list of indexes, you can use the index to construct dynamic names and reference items from the original list.
The policy below creates a uniquely named NetworkPolicy (e.g., np-0, np-1) in each namespace defined in the ConfigMap.
1apiVersion: policies.kyverno.io/v1alpha1
2kind: GeneratingPolicy
3metadata:
4 name: generate-network-policy
5spec:
6 matchConstraints:
7 resourceRules:
8 - apiGroups: [""]
9 apiVersions: ["v1"]
10 operations: ["CREATE"]
11 resources: ["configmaps"]
12 variables:
13 # nsList becomes ["ns1", "ns2", "ns3"]
14 - name: nsList
15 expression: "object.data.namespaces.split(',')"
16 # indexed becomes [0, 1, 2]
17 - name: indexed
18 expression: >
19 variables.nsList.map(ns, variables.nsList.indexOf(ns))
20 generate:
21 - expression: >
22 variables.indexed.all(i, generator.Apply(variables.nsList[i], [
23 {
24 "kind": dyn("NetworkPolicy"),
25 "apiVersion": dyn("networking.k8s.io/v1"),
26 "metadata": dyn({
27 "name": "np-" + string(i),
28 }),
29 "spec": dyn({
30 "podSelector": dyn({}),
31 "policyTypes": dyn(["Ingress", "Egress"])
32 })
33 }]
34 ))
This policy defines two variables:
nsList
: Reads the namespaces key from the trigger ConfigMap and splits the comma-separated string into a list of names (e.g., [’ns1’, ’ns2’, ’ns3’]).indexed
: Creates a new list containing the numerical index of each item from nsList. For annsList
of [’ns1’, ’ns2’, ’ns3’], the indexed list becomes [0, 1, 2]. This provides a unique, stable number for each namespace.
Then, the generate
block does the following:
variables.indexed.all(i, ...):
This CEL expression iterates through each numberi
in the indexed list. The loop variablei
holds the current index (0, then 1, then 2).generator.Apply(variables.nsList[i], ...):
Inside the loop, this function generates the resource.- It uses
variables.nsList[i]
to get the target namespace (e.g., wheni
is 1, it getsns2
). - It dynamically creates the NetworkPolicy name by appending the index
i
(e.g.,"np-" + string(1)
results in the namenp-1
).
- It uses
This approach allows you to generate uniquely named resources in each namespace defined in the ConfigMap, ensuring that each NetworkPolicy is distinct and correctly associated with its target namespace.
There might be cases where you want to apply a filter to the list before generating resources.
The following policy combines filtering and indexing to generate resources for specific, pre-approved namespaces while maintaining their original index for naming consistency.
1apiVersion: policies.kyverno.io/v1alpha1
2kind: GeneratingPolicy
3metadata:
4 name: generate-network-policy
5spec:
6 matchConstraints:
7 resourceRules:
8 - apiGroups: [""]
9 apiVersions: ["v1"]
10 operations: ["CREATE", "UPDATE"]
11 resources: ["configmaps"]
12 variables:
13 # nsList is ["filtered-ns-1", "filtered-ns-2", "filtered-ns-3"]
14 - name: nsList
15 expression: "object.data.namespaces.split(',')"
16 # filteredList is ["filtered-ns-1", "filtered-ns-3"]
17 - name: filteredList
18 expression: "variables.nsList.filter(ns, ['filtered-ns-1', 'filtered-ns-3'].exists(v, v == ns))"
19 # indexed is [0, 2]
20 - name: indexed
21 expression: >
22 variables.filteredList.map(ns, variables.nsList.indexOf(ns))
23 generate:
24 - expression: >
25 variables.indexed.all(i, generator.Apply(variables.nsList[i], [
26 {
27 "kind": dyn("NetworkPolicy"),
28 "apiVersion": dyn("networking.k8s.io/v1"),
29 "metadata": dyn({
30 "name": "filtered-np-" + string(i),
31 }),
32 "spec": dyn({
33 "podSelector": dyn({}),
34 "policyTypes": dyn(["Ingress", "Egress"])
35 })
36 }]
37 ))
This policy defines three variables:
nsList:
Reads the namespaces key from the trigger ConfigMap and splits it into the full source list. Example: [‘filtered-ns-1’, ‘filtered-ns-2’, ‘filtered-ns-3’].filteredList:
This is the key step. It usesfilter()
to create a new list containing only the namespaces fromnsList
that also exist in a hardcoded “allow list”(['filtered-ns-1', 'filtered-ns-3'])
. The result is['filtered-ns-1', 'filtered-ns-3']
.indexed:
This variable iterates over thefilteredList
but finds the original index of each item from the unfilterednsList
. The result is a list of the original indexes of the allowed items:[0, 2]
.
The generate
block then uses these variables to create NetworkPolicies in the filtered namespaces:
variables.indexed.all(i, ...):
The loop iterates over the indexed list, soi
will be 0 and then 2.generator.Apply(variables.nsList[i], ...):
Inside the loop, it uses the indexi
to target the correct namespace from the original list and to create a uniquely named resource.- When
i
is 0, it createsfiltered-np-0
in namespacefiltered-ns-1
. - When
i
is 2, it createsfiltered-np-2
in namespacefiltered-ns-3
. - Namespace
filtered-ns-2
is skipped entirely because it was not in thefilteredList
.
- When
This approach allows you to generate resources only in specific namespaces while maintaining a consistent naming scheme based on their original index in the unfiltered list.
Looping with a Clone Source
When cloning resources, you can also use looping to generate multiple resources based on a list of items. This is particularly useful when you want to clone a resource for each item in a list.
The following policy demonstrates how to clone a Secret for each namespace defined in a ConfigMap:
1apiVersion: policies.kyverno.io/v1alpha1
2kind: GeneratingPolicy
3metadata:
4 name: generate-secrets
5spec:
6 matchConstraints:
7 resourceRules:
8 - apiGroups: [""]
9 apiVersions: ["v1"]
10 operations: ["CREATE", "UPDATE"]
11 resources: ["configmaps"]
12 variables:
13 - name: nsList
14 expression: "object.data.namespaces.split(',')"
15 - name: source
16 expression: resource.Get("v1", "secrets", "default", "regcred")
17 generate:
18 - expression: >
19 variables.nsList.all(ns, generator.Apply(ns, [variables.source]))
This policy is triggered whenever a ConfigMap is created. It captures the list of namespaces from the ConfigMap’s data field, splits it into a list, and stores it in nsList
. It also fetches the regcred
Secret from the default
namespace and stores it in source
. The generate
block then uses variables.nsList.all(ns, ...)
to iterate over each namespace in nsList
, generating a copy of the regcred
Secret in each namespace.
You can also clone multiple resources for each item in the list. The following example demonstrates how to clone all Secrets from the default
namespace into each namespace defined in the ConfigMap:
1apiVersion: policies.kyverno.io/v1alpha1
2kind: GeneratingPolicy
3metadata:
4 name: foreach-clone-list-sync-create
5spec:
6 evaluation:
7 synchronize:
8 enabled: true
9 matchConstraints:
10 resourceRules:
11 - apiGroups: [""]
12 apiVersions: ["v1"]
13 operations: ["CREATE", "UPDATE"]
14 resources: ["configmaps"]
15 variables:
16 - name: nsList
17 expression: "object.data.namespaces.split(',')"
18 - name: sources
19 expression: resource.List("v1", "secrets", "default")
20 generate:
21 - expression: >
22 variables.nsList.all(ns, generator.Apply(ns, [variables.sources]))
This policy is triggered whenever a ConfigMap is created. It captures the list of namespaces from the ConfigMap’s data field, splits it into a list, and stores it in nsList
. It also fetches all Secrets from the default
namespace and stores them in sources
. The generate
block then uses variables.nsList.all(ns, ...)
to iterate over each namespace in nsList
, generating copies of all fetched Secrets in each namespace.
Synchronization
Synchronization ensures that downstream resources remain consistent with their source. The source can be the policy itself (for data source) or a cloned resource (for clone source). When synchronization is enabled, Kyverno monitors for changes and automatically updates or deletes downstream resources as needed.
This is configured using the evaluation.synchronize.enabled: true
field.
Synchronization with a Data Source
When synchronize is enabled for a data source, the lifecycle of the downstream resource is tied to the policy and the trigger. For example, if you modify the policy, the generated resource is updated. If you delete the trigger, the generated resource is deleted.
The following table shows the behavior of deletion and modification events on the components of a GeneratingPolicy with a data source declaration.
Action | Sync Effect | NoSync Effect |
---|---|---|
Delete Downstream | Downstream recreated | Downstream deleted |
Delete Rule/Policy | Downstream retainedorphanDownstreamOnPolicyDelete.enabled: true | Downstream retained |
Delete Rule/Policy | Downstream deletedorphanDownstreamOnPolicyDelete.enabled: false | Downstream retained |
Delete Trigger | Downstream deleted | None |
Modify Downstream | Downstream reverted | Downstream modified |
Modify Rule/Policy | Downstream synced | Downstream unmodified |
Modify Trigger | Downstream deleted | None |
The evaluation.orphanDownstreamOnPolicyDelete
property can be used to preserve generated resources on policy deletion when synchronization is enabled. The default is false. When set to true, the generated resources will be retained (orphaned) in the cluster after the policy that created them is deleted.
Synchronization with a Clone Source
For clone source mode, synchronization keeps the downstream copies in sync with the original source resource. If the source resource is updated or deleted, all downstream clones are updated or deleted accordingly.
The following table shows the behavior of events on components of a GeneratingPolicy with a clone source. Note that deleting the policy does not delete the downstream resources, as their lifecycle is primarily tied to the source.
Action | Sync Effect | NoSync Effect |
---|---|---|
Delete Downstream | Downstream recreated | Downstream deleted |
Delete Rule/Policy | Downstream retained | Downstream retained |
Delete Source | Downstream deleted | Downstream retained |
Delete Trigger | Downstream deleted | None |
Modify Downstream | Downstream reverted | Downstream modified |
Modify Rule/Policy | Downstream unmodified | Downstream unmodified |
Modify Source | Downstream synced | Downstream unmodified |
Modify Trigger | Downstream deleted | None |
Generating for Existing Resources
By default, a GeneratingPolicy triggers only for CREATE
, UPDATE
and DELETE
events on resources that match its rules. This means if you apply a policy to a cluster that already contains matching resources (e.g., existing namespaces), the policy will not run for them.
To address this, you can set evaluation.generateExisting.enabled
to true. This setting instructs Kyverno to perform a one-time scan for all existing resources that match the trigger rules and apply the generation logic to them when the policy is created.
When to Use: Use this when you need to retroactively apply a generatingpolicy to existing resources in your cluster, such as distributing a standard Secret or NetworkPolicy to all existing Namespaces.
The following policy clones a secret to all new and existing namespaces:
1apiVersion: policies.kyverno.io/v1alpha1
2kind: GeneratingPolicy
3metadata:
4 name: generate-secrets
5spec:
6 evaluation:
7 generateExisting:
8 enabled: true
9 matchConstraints:
10 resourceRules:
11 - apiGroups: [""]
12 apiVersions: ["v1"]
13 operations: ["CREATE", "UPDATE"]
14 resources: ["namespaces"]
15 variables:
16 - name: nsName
17 expression: "object.metadata.name"
18 - name: source
19 expression: resource.Get("v1", "secrets", "default", "regcred")
20 generate:
21 - expression: generator.Apply(variables.nsName, [variables.source])
When you apply this policy to a cluster with, for example, 10 existing namespaces, generateExisting.enabled: true
ensures that Kyverno will immediately generate a copy of the regcred
secret in each of those 10 namespaces. Without this field, the secret would only be created in namespaces created after the policy was applied.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.