Skip to main content
Config and Outputs are Pydantic models that define your resource’s interface. Config specifies what users configure, while Outputs defines what your resource exposes to dependent resources.

Config Basics

Config classes inherit from Config, which extends Pydantic’s BaseModel with extra="forbid" to catch typos. Every field is declared with one of the SDK’s field types, most often Field[T]:
Key behaviors:
  • Unknown fields raise ValidationError (catches typos in YAML)
  • Type coercion is automatic (string "10" becomes int 10)
  • Validation constraints go inside the field type; see Validation Patterns
Two rules are enforced when the class is defined, so a violation fails as soon as the provider package is imported, and pragma providers publish fails with it:
  • Every config field uses an SDK field type: Field[T], ImmutableField[T], SensitiveField[T], ImmutableSensitiveField[T], Dependency[T], ImmutableDependency[T], SensitiveDependency[T], or a file field. A bare type such as name: str raises TypeError.
  • Every Config and Outputs class that declares fields has a class docstring. A class without one raises TypeError. Document each field in an Attributes: section; a docstring without one only warns.

Outputs Basics

Outputs classes inherit from Outputs with the same extra="forbid" setting. Output fields use plain types:
Outputs are returned from on_observe, on_create, and on_update. on_observe returns the same outputs the write path returned for the object’s current state, so every field is derived from identity or config, or read back from the external object. Values that change without a platform write belong in health(), not in outputs. Other resources can reference these fields through the dependency system.

Field Types

Required Fields

Fields without defaults are required:

Optional Fields with Defaults

Provide sensible defaults for common cases, and name each default in the docstring:

Optional Fields That May Be Absent

Use a None default for truly optional fields:

FieldReference for Dynamic Values

Field[T] accepts either a direct value or a reference to another resource’s output:
Users can provide a direct value:
Or reference another resource’s output:

How FieldReference Works

When the runtime processes a resource, it resolves all FieldReference values before calling your lifecycle methods. By the time on_create runs, self.config.database_url contains the actual string value, not the reference. A reference also orders the work: the platform runs the resource only once the referenced resource is ready.
You don’t need to handle FieldReference resolution in your code. The runtime resolves all references to their actual values before invoking lifecycle methods.

Dependency for Whole-Resource Access

When you need access to an entire resource (its config, outputs, and methods) rather than just a single field, use Dependency[T]:

Resolving Dependencies

In your lifecycle methods, call resolve() to get the typed resource instance:
The platform runs the handler only once the dependency is ready, and resolves it first. resolve() returns that pre-resolved instance, and raises RuntimeError when the dependency was not resolved. Never wait for a dependency inside a handler.

YAML Syntax

Users specify whole-resource dependencies without a field key:

Dependency vs FieldReference

A config can hold both: a Field[str] for the one connection string, and a Dependency[T] for the whole database:
The field reference includes a field key and yields a single value:
The dependency omits it and yields the full resource:

Validation Patterns

A Field[T] also accepts a field reference, so a constraint applied to the whole field would also run against the reference. Put constraints inside the type parameter instead, where they apply only to direct values. Before any handler runs, the runtime validates the resolved values against the same class, so a referenced value is checked too.

Field Constraints

Wrap the value type in Annotated with Pydantic’s Field or StringConstraints:
Import Pydantic’s Field as PydanticField to avoid confusion with the SDK’s Field type alias used for FieldReference support. Never assign PydanticField(...) as the field’s default on a Field[T] field: the constraint then applies to the reference too, and the class fails to validate references or to build.

Field Validators

Validate individual values with AfterValidator inside the type parameter, so references pass through untouched:

Model Validators

Validate relationships between fields. Skip the check while either value is still a reference; it runs again when the runtime validates the resolved values:

Type Coercion

Pydantic automatically coerces compatible types:
Both calls produce size_gb=100 and enabled=True. For strict typing without coercion, use a strict type inside the parameter; count below rejects the string "10":

Output Design Best Practices

Expose What Dependents Need

Think about what downstream resources will need: connection details for applications, the identifier for management operations, and the resource path for cloud operations:

Use Consistent Naming

Follow these conventions:

Keep Outputs Stable

Output field names are part of your API. Renaming one, say resource_name to secret_name, breaks every resource that references it. Expose only what dependents use, never internal state such as a raw API response, because a field you remove later breaks its references too.

Include Sufficient Context

Provide enough information for dependents to work without additional API calls. A bare secret_id makes every dependent rebuild the full path; full paths let them use the secret directly:

Complete Example

Here’s a well-designed Config and Outputs pair:

What’s Next

Lifecycle Methods

Implement on_observe, on_create, on_update, and on_delete handlers.

Building Providers Overview

Full guide to creating and deploying providers.