Skip to main content
Ctrlplane uses CEL (Common Expression Language) for writing powerful selectors and matching expressions. CEL is a simple, fast, and safe expression language developed by Google.

Overview

CEL expressions are used in:
  • Resource selectors — Match resources to environments
  • Policy selectors — Target policies to specific releases
  • Relationship rules — Define how entities connect
# Example: Environment resource selector
resourceSelector: resource.metadata["environment"] == "production"

# Example: Policy selector
selector: environment.name == "Production" && deployment.metadata["critical"] == "true"

Basic Syntax

Accessing Fields

# Direct field access
resource.name
resource.kind
resource.identifier

# Metadata access (map)
resource.metadata["environment"]
resource.metadata["region"]

# Config access
resource.config["namespace"]

Comparison Operators

OperatorDescriptionExample
==Equalresource.kind == "KubernetesCluster"
!=Not equalresource.metadata["env"] != "dev"
<Less thanresource.metadata["priority"] < "5"
>Greater thanresource.metadata["replicas"] > "1"
<=Less or equalversion.metadata["build"] <= "100"
>=Greater or equalresource.metadata["tier"] >= "2"

Logical Operators

OperatorDescriptionExample
&&Logical ANDa == "x" && b == "y"
||Logical ORa == "x" || a == "y"
!Logical NOT!resource.metadata["deprecated"]

Arithmetic Operators

OperatorDescriptionExample
+Addition / Concatenation"prefix-" + resource.name
-Subtractionint(a) - int(b)
*Multiplicationint(a) * 2
/Divisionint(a) / 2
%Moduloint(a) % 2 == 0

Available Variables

Resource Context

When writing resource selectors:
VariableTypeDescription
resource.idstringUnique resource ID
resource.namestringResource display name
resource.kindstringResource type (e.g., KubernetesCluster)
resource.identifierstringExternal identifier
resource.versionstringResource version
resource.metadatamapKey-value metadata
resource.configmapResource configuration

Environment Context

When writing environment selectors:
VariableTypeDescription
environment.idstringEnvironment ID
environment.namestringEnvironment name
environment.metadatamapEnvironment metadata

Deployment Context

When writing deployment selectors:
VariableTypeDescription
deployment.idstringDeployment ID
deployment.namestringDeployment name
deployment.metadatamapDeployment metadata

Version Context

When writing version selectors:
VariableTypeDescription
version.idstringVersion ID
version.tagstringVersion tag
version.namestringVersion name
version.metadatamapVersion metadata

String Functions

contains

Check if a string contains a substring:
resource.name.contains("prod")
resource.metadata["tags"].contains("critical")

startsWith

Check if a string starts with a prefix:
resource.identifier.startsWith("k8s-")
resource.metadata["region"].startsWith("us-")

endsWith

Check if a string ends with a suffix:
resource.name.endsWith("-cluster")
resource.metadata["zone"].endsWith("a")

matches

Regular expression matching:
# Match identifiers like prod-cluster-1, prod-cluster-2
resource.identifier.matches("^prod-cluster-[0-9]+$")

# Match any US region
resource.metadata["region"].matches("^us-(east|west)-[0-9]$")

size

Get string length:
resource.name.size() > 0
resource.metadata["description"].size() < 100

toLowerCase / toUpperCase

Case conversion:
resource.metadata["env"].toLowerCase() == "production"

List Operations

in

Check if a value is in a list:
resource.metadata["region"] in ["us-east-1", "us-west-2", "eu-west-1"]
resource.kind in ["KubernetesCluster", "KubernetesNamespace"]

size

Get list length:
resource.metadata["tags"].size() > 0

exists

Check if any element matches:
# Check if any tag starts with "team-"
resource.metadata["tags"].exists(t, t.startsWith("team-"))

all

Check if all elements match:
# Check if all regions are in US
resource.metadata["regions"].all(r, r.startsWith("us-"))

Map Operations

has

Check if a key exists in a map:
has(resource.metadata["team"])
has(resource.config["namespace"])
This is safer than direct access when the key might not exist.

Key Access

Access map values:
resource.metadata["environment"]
resource.config["server"]

Conditional Expressions

Ternary Operator

resource.metadata["tier"] == "critical" ? "high-priority" : "normal"

Null-Safe Access

Use has() for optional fields:
has(resource.metadata["deprecated"]) && resource.metadata["deprecated"] == "true"
Or use the default pattern:
# Default to empty string if not present
(has(resource.metadata["team"]) ? resource.metadata["team"] : "") == "platform"

Common Patterns

Production Resources

resource.metadata["environment"] == "production"

Multi-Region Targeting

resource.metadata["region"] in ["us-east-1", "us-west-2"]

Kind Filtering

resource.kind == "KubernetesCluster" && 
resource.metadata["environment"] == "production"

Team-Based Selection

resource.metadata["team"] == "platform" || 
resource.metadata["team"] == "infrastructure"

Exclude Deprecated

!has(resource.metadata["deprecated"]) || 
resource.metadata["deprecated"] != "true"

Critical Tier in Production

resource.metadata["environment"] == "production" && 
resource.metadata["tier"] == "critical"

US Regions Only

resource.metadata["region"].startsWith("us-")

Canary Resources

resource.metadata["environment"] == "production" && 
has(resource.metadata["canary"]) && 
resource.metadata["canary"] == "true"

Complex Multi-Condition

(resource.metadata["environment"] == "production" && 
 resource.metadata["region"] in ["us-east-1", "us-west-2"]) ||
(resource.metadata["environment"] == "staging" && 
 resource.metadata["region"] == "us-east-1")

Policy Selector Examples

Target Production Environment

environment.name == "Production"

Target Critical Deployments

deployment.metadata["tier"] == "critical"

Target Specific Environment + Deployment

environment.name == "Production" && 
deployment.metadata["requires-approval"] == "true"

Version Filtering

version.tag.startsWith("v2.") && 
!version.tag.contains("beta")

Relationship Rule Examples

Match by Region

# fromSelector for VPCs
resource.kind == "vpc"

# toSelector for clusters  
resource.kind == "KubernetesCluster"

# Property matcher expression
from.metadata["region"] == to.metadata["region"]

Match by Account

from.metadata["account"] == to.metadata["account"] &&
from.metadata["region"] == to.metadata["region"]

Type Coercion

CEL is strongly typed. Use these functions to convert types:

String to Integer

int(resource.metadata["replicas"]) > 3

Integer to String

string(42) == resource.metadata["count"]

Type Checking

type(resource.metadata["count"]) == string

Error Handling

Safe Field Access

Always use has() for optional fields to avoid runtime errors:
# Bad: may error if "team" doesn't exist
resource.metadata["team"] == "platform"

# Good: safe access
has(resource.metadata["team"]) && resource.metadata["team"] == "platform"

Default Values

Provide defaults for optional fields:
# Use empty string as default
(has(resource.metadata["tier"]) ? resource.metadata["tier"] : "standard") == "critical"

Performance Tips

Prefer Simple Expressions

# Fast: simple equality
resource.metadata["env"] == "production"

# Slower: regex matching
resource.identifier.matches("^prod-.*")

Short-Circuit Evaluation

CEL uses short-circuit evaluation. Put cheap checks first:
# Good: check env first (fast), then regex (slow)
resource.metadata["env"] == "production" && 
resource.identifier.matches("^prod-cluster-[0-9]+$")

Avoid Redundant Checks

# Bad: redundant
resource.kind == "KubernetesCluster" && 
resource.kind != "VM"

# Good: single check
resource.kind == "KubernetesCluster"

Debugging

Test Expressions

Use the API to test your CEL expressions:
curl -X POST "https://app.ctrlplane.dev/api/v1/workspaces/{workspaceId}/resources/query" \
  -H "Authorization: Bearer $CTRLPLANE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "filter": "resource.metadata[\"environment\"] == \"production\""
  }'

Common Errors

ErrorCauseFix
no such keyAccessing missing map keyUse has() check
type mismatchComparing different typesUse type coercion
syntax errorInvalid CEL syntaxCheck quotes, brackets
undeclared referenceUnknown variableCheck variable names

Escaping Quotes

In YAML, escape quotes properly:
# Single quotes around expression, double quotes inside
resourceSelector: 'resource.metadata["environment"] == "production"'

# Or use YAML literal block
resourceSelector: |
  resource.metadata["environment"] == "production"
In JSON:
{
  "filter": "resource.metadata[\"environment\"] == \"production\""
}

Reference

Reserved Keywords

These words are reserved in CEL:
  • true, false, null
  • in
  • as
  • break, const, continue, else, for, function, if, import, let, loop, package, namespace, return, var, void, while

Operator Precedence

From highest to lowest:
  1. () - Grouping
  2. . [] - Member access
  3. - ! - Unary
  4. * / % - Multiplicative
  5. + - - Additive
  6. < <= > >= in - Relational
  7. == != - Equality
  8. && - Logical AND
  9. || - Logical OR
  10. ?: - Conditional

Next Steps