CEL Expressions

Expressions in ASO

ASO offers some support for Common Expression Language (CEL) expressions.

Expressions in ASO can be used to build your own Secret or ConfigMap output. This helps to solve problems like:

  • Applications that need connection strings with specific (possibly non-standard) formats.
  • Exporting arbitrary fields from a resource for use by other applications.
  • Exporting static text alongside dynamic data.

CEL expressions are currently supported on most resources via the spec.operatorSpec.configMapExpressions and spec.operatorSpec.secretExpressions fields, which allow configuring expressions output to ConfigMaps and Secrets, respectively.

Inputs

Expressions have access to the following inputs:

Type Variable(s)
ConfigMap self
Secret self, secret

self: The resource itself.

secret: A set of secrets associated with the resource. The specific secrets supported vary by resource type, but can be found on the spec.operatorSpec.secrets type. For example, eventhub Namespace supports the following 4 secrets:

  • primaryConnectionString
  • primaryKey
  • secondaryConnectionString
  • secondaryKey

Outputs

Expressions for spec.operatorSpec.configMapExpressions and spec.operatorSpec.secretExpressions must output string or map[string]string data. Any other expression output type will be rejected.

CEL options, language features, and libraries

Compare with Kubernetes supported options

Feature
Standard macros
Standard functions
Default UTC Time Zone
Eagerly Validate Declarations
Extended String Library, version 3
Optional Types
Cross Type Numeric comparisons

Escaping

ASO follows the same escaping rules as Kubernetes, except we don’t escape CEL keywords.

While escaping isn’t required for most properties, CEL doesn’t support the ., -, or / characters in expressions, so properties whose names contain these characters must be escaped. The escaping rules are:

Escape sequence Property name equivalent Unescaped example Escaped example
__underscores__ __ my__field my__underscores__field
__dot__ . my.field my__dot__field
__dash__ - my-field my__dash__field
__slash__ / my/field my__slash__field
__{keyword}__ CEL RESERVED keyword (false, true, in, null) false __false__

Examples

These examples are all written for a simple cache.Redis. There’s nothing special about Redis it’s just used as an example.

In each of these examples, we start with the given Redis and apply the expression as mentioned in the example itself.

It might be useful to refer to the DestinationExpression documentation to understand what the name/key/value fields are doing in all of these examples.

For more advanced examples, refer to the CEL language definition.

Sample Redis:

apiVersion: cache.azure.com/v1api20230801
kind: Redis
metadata:
  name: sampleredis1
  namespace: default
  annotations:
    foo: bar
    baz: qux
spec:
  location: westus2
  owner:
    name: aso-sample-rg
  sku:
    family: P
    name: Premium
    capacity: 1
  enableNonSslPort: false
  minimumTlsVersion: "1.2"
  redisConfiguration:
    maxmemory-delta: "10"
    maxmemory-policy: allkeys-lru
  redisVersion: "6"
  zones:
  - "1"
  - "2"
  - "3" 

A full example

Expression snippet:

spec:
  operatorSpec:
    configMapExpressions:
    - name: my-configmap
      key: location
      value: self.spec.location

Output (ConfigMap):

metadata:
  name: my-configmap
data:
  location: westus2

From here on, we’ll elide the supporting YAML of the above example and just focus on the CEL expression input (value) and the resulting output, still based on the example Redis above.

Description Expression Result
Hardcoded string "helloworld" helloworld
Formatted string "%s:%d".format([self.spec.location, 7]) westus2:7
String math self.metadata.namespace + ":" + self.metadata.name default:sampleredis1
Int output (error) self.spec.sku.capacity Error, expression “self.spec.sku.capacity” must return one of [string,map(string, string)], but was int
Coerce to string string(self.spec.sku.capacity) 1
Map output self.metadata.annotations {"foo": "bar", "baz": "qux"}
Array macro self.spec.zones.filter(a, int(a) % 2 == 0).join("-") 2-4
Select array item self.spec.zones[0] 1
Select map item self.metadata.annotations["foo"] bar