Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions content/blog/esc-schema-validation-fn-validate/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
---
title: "Schema Validation Comes to Pulumi ESC with fn::validate"
date: 2026-02-09
draft: false
meta_desc: "Validate configuration values against JSON Schema with fn::validate in Pulumi ESC."
meta_image: meta.png
authors:
- pablo-terradillos
- claire-gaestel
tags:
- esc
- features
schema_type: auto
social:
twitter:
linkedin:
aliases:
- /blog/esc-schema-validation-fn-conform/
---

Pulumi ESC environments can now validate configuration values against JSON Schema with the new `fn::validate` built-in function. Invalid configurations are caught immediately when you save, preventing misconfigurations from reaching your deployments.
<!--more-->

Configuration errors are often discovered too late during deployment or, worse, in production. With `fn::validate`, you define validation rules directly in your environment, and ESC enforces them at save time. If a value doesn't match its schema, the environment cannot be saved until the issue is resolved.

## How it works

The `fn::validate` function takes a JSON Schema and a value. If the value conforms to the schema, it passes through unchanged. If not, ESC raises a validation error.

```yaml
values:
port:
fn::validate:
schema: { type: number, minimum: 1, maximum: 65535 }
value: 8080
```

This validates that `port` is a number between 1 and 65535. The evaluated result is simply `8080`.

## Validating objects with required fields

For complex configurations, you can enforce structure and required fields:

```yaml
values:
database:
fn::validate:
schema:
type: object
properties:
host: { type: string }
port: { type: number }
name: { type: string }
required: [host, port, name]
value:
host: "db.example.com"
port: 5432
name: "myapp"
```

If any required field is missing or has the wrong type, the environment cannot be saved.

## Reusing schemas across environments

Define schemas once and reference them across multiple environments. Using the [`environments` built-in property](/docs/esc/environments/syntax/builtin-properties/environments/) keeps the schema out of your environment's output:

**Schema environment (myorg/schemas)**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: we should be encouraging (org)/project/env naming for esc environments (not org/env)


```yaml
values:
database-schema:
type: object
properties:
host: { type: string }
port: { type: number }
required: [host, port]
```

**Environment using the schema**

```yaml
values:
database:
fn::validate:
schema: ${environments.myorg.schemas.database-schema}
value:
host: "prod-db.example.com"
port: 5432
```

This pattern ensures consistent validation rules across teams and projects.

## What happens when validation fails

When a value doesn't conform to its schema, ESC returns a clear error message:

```yaml
values:
port:
fn::validate:
schema: { type: string }
value: 8080
```

This raises: `expected string, got number`. The environment cannot be saved until you fix the value or update the schema.

## When to use schema validation

Enable `fn::validate` for:

- Values with specific type requirements (numbers, strings, arrays)
- Objects that must have certain fields present
- Numbers that must fall within a valid range
- Configurations shared across multiple environments
- Any value where catching errors early prevents downstream issues

## Getting started

The `fn::validate` function is available now in all Pulumi ESC environments. Add schema validation to your existing environments or use it when creating new ones.

For more information, see the [fn::validate documentation](/docs/esc/environments/syntax/builtin-functions/fn-validate/).
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ All function invocations take the form of an object with a single key that names

## Functions

- [`fn::fromJSON`](/docs/esc/environments/syntax/builtin-functions/fn-from-json)
- [`fn::concat`](/docs/esc/environments/syntax/builtin-functions/fn-concat)
- [`fn::validate`](/docs/esc/environments/syntax/builtin-functions/fn-validate)
- [`fn::fromBase64`](/docs/esc/environments/syntax/builtin-functions/fn-from-base64)
- [`fn::fromJSON`](/docs/esc/environments/syntax/builtin-functions/fn-from-json)
- [`fn::join`](/docs/esc/environments/syntax/builtin-functions/fn-join)
- [`fn::split`](/docs/esc/environments/syntax/builtin-functions/fn-split)
- [`fn::concat`](/docs/esc/environments/syntax/builtin-functions/fn-concat)
- [`fn::open`](/docs/esc/environments/syntax/builtin-functions/fn-open)
- [`fn::rotate`](/docs/esc/environments/syntax/builtin-functions/fn-rotate)
- [`fn::secret`](/docs/esc/environments/syntax/builtin-functions/fn-secret)
- [`fn::split`](/docs/esc/environments/syntax/builtin-functions/fn-split)
- [`fn::toBase64`](/docs/esc/environments/syntax/builtin-functions/fn-to-base64)
- [`fn::toJSON`](/docs/esc/environments/syntax/builtin-functions/fn-to-json)
- [`fn::toString`](/docs/esc/environments/syntax/builtin-functions/fn-to-string)
Expand Down
233 changes: 233 additions & 0 deletions content/docs/esc/environments/syntax/builtin-functions/fn-validate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
---
title: fn::validate
title_tag: fn::validate
h1: fn::validate
meta_desc: Pulumi ESC allows you to compose and manage hierarchical collections of configuration and secrets and consume them in various ways.
aliases:
- /docs/esc/environments/syntax/builtin-functions/fn-conform/
menu:
esc:
parent: esc-syntax-builtin-functions
identifier: esc-syntax-fn-validate
weight: 2
---

The `fn::validate` built-in function validates a value against a JSON Schema. If the value does not conform to the schema, a validation error is raised and the environment cannot be saved until the issues are resolved.

## Declaration

```yaml
fn::validate:
schema: json-schema
value: value-to-validate
```

### Parameters

| Property | Type | Description |
|----------|--------|------------------------------------------------------|
| `schema` | object | A JSON Schema definition to validate the value against.
| `value` | any | The value to validate.

### Returns

If validation passes, the original `value` is returned. If validation fails, an error is raised and the environment cannot be saved.

## Example

### Basic type validation

#### Definition

```yaml
values:
valid-string:
fn::validate:
schema: { type: string }
value: "hello"
```

#### Evaluated result

```json
{
"valid-string": "hello"
}
```

### Number constraints

#### Definition

```yaml
values:
validated-number:
fn::validate:
schema: { type: number, minimum: 0 }
value: 42
```

#### Evaluated result

```json
{
"validated-number": 42
}
```

### Object with required fields

#### Definition

```yaml
values:
user:
fn::validate:
schema:
type: object
properties:
name: { type: string }
email: { type: string }
required: [name, email]
value:
name: "Alice"
email: "alice@example.com"
```

#### Evaluated result

```json
{
"user": {
"name": "Alice",
"email": "alice@example.com"
}
}
```

### Dynamic schema from reference

You can define schemas as values and reference them dynamically:

#### Definition

```yaml
values:
my-schema:
type: string
minLength: 1
validated:
fn::validate:
schema: ${my-schema}
value: "hello"
```

#### Evaluated result

```json
{
"my-schema": {
"type": "string",
"minLength": 1
},
"validated": "hello"
}
```

### Array validation

#### Definition

```yaml
values:
tags:
fn::validate:
schema:
type: array
items: { type: string }
value: ["production", "us-east-1"]
```

#### Evaluated result

```json
{
"tags": ["production", "us-east-1"]
}
```

### Reusing schemas from other environments

You can define schemas in a separate environment and reference them for reuse across multiple environments. This allows you to centralize schema definitions and ensure consistent validation.

Using the [`environments` built-in property](/docs/esc/environments/syntax/builtin-properties/environments/) to reference the schema is the recommended approach because it doesn't merge the schema into your environment's output.

#### Schema environment (myproj/schemas)

```yaml
values:
user-schema:
type: object
properties:
name: { type: string }
email: { type: string }
required: [name, email]
```

#### Environment using the schema reference

```yaml
values:
user:
fn::validate:
schema: ${environments.myproj.schemas.user-schema}
value:
name: "Alice"
email: "alice@example.com"
```

#### Evaluated result

```json
{
"user": {
"name": "Alice",
"email": "alice@example.com"
}
}
```

This pattern is useful for enforcing consistent validation rules across teams and projects.

## Validation errors

When a value does not conform to its schema, `fn::validate` raises an error that prevents the environment from being saved. This ensures configuration issues are caught before the environment is used.

### Example: type mismatch

```yaml
values:
type-error:
fn::validate:
schema: { type: string }
value: 42
```

This raises an error: `expected string, got number`. The environment cannot be saved until the value conforms to the schema.

### Example: missing required field

```yaml
values:
missing-required:
fn::validate:
schema:
type: object
properties:
name: { type: string }
required: [name]
value:
other: "value"
```

This raises an error indicating that the required field `name` is missing. The environment cannot be saved until the required field is provided.
Loading