Skip to content

CustomResource

A CustomResource allows you to define infrastructure resources that are not natively supported by Kyku. It runs a user-provided handler function during apply and destroy phases, with access to provider clients and secret management. This is the escape hatch for extending Kyku to any cloud API.

Property Type Required Description
name string yes Unique resource name
id string no Explicit ID (auto-generated UUID if omitted)
provider string no Provider label for multi-provider configs
tags Record<string, string> no Arbitrary key-value metadata
handlerId string yes Registered handler identifier
properties Record<string, unknown> yes Arbitrary properties passed to the handler
interface CustomResourceHandler {
apply: (context: CustomResourceContext) => Promise<CustomResourceResult>
destroy?: (context: CustomResourceContext) => Promise<void>
}
interface CustomResourceContext {
readonly id: string
readonly name: string
readonly properties: Record<string, unknown>
readonly previousProperties?: Record<string, unknown>
readonly previousState?: Record<string, unknown>
readonly previousProviderId?: string
getClient: (name: string) => ProviderClient
setSecret: <T>(value: T) => T & { __secret: true }
getOutput: (resourceId: string, key: string) => Promise<unknown>
readonly log: (message: string) => void
}
interface CustomResourceResult {
providerId: string
outputs?: Record<string, unknown>
status?: string
}
interface ProviderClient {
readonly client: unknown
readonly region: string
}
import { CustomResource, CustomResourceRegistry } from '@kykucloud/types'
// Register a handler
CustomResourceRegistry.register('my-custom-handler', {
async apply(context) {
const aws = context.getClient('aws') // AWS SDK client
context.log('Creating custom resource…')
return { providerId: 'custom-123', outputs: { endpoint: '' } }
},
async destroy(context) {
context.log('Cleaning up…')
},
})
// Use it in config
const myCustom = new CustomResource({
name: 'my-custom',
handlerId: 'my-custom-handler',
properties: {
configA: 'value1',
configB: 42,
},
})
Provider Supported Backend
AWS Wired — can access any AWS SDK v3 service
GCP Wired — can access any GCP API client
Hetzner Wired — can access Hetzner API client
DigitalOcean Wired — can access DO API client
  • Handler registration: Handlers must be registered with CustomResourceRegistry.register(handlerId, handler) before the plan/apply phase.
  • Provider clients: context.getClient('aws') returns the AWS SDK v3 client, context.getClient('gcp') returns the GCP auth client. The available clients depend on the provider used.
  • Secrets: Use context.setSecret(value) (or the exported setSecret()) to wrap a value. The crypto walk encrypts the inner string and unwraps the marker so state stores ENC: ciphertext.
  • Outputs: Return outputs in the result to expose values that can be referenced by other resources.
  • Destroy: If destroy is not provided, the resource will be removed from state without any cleanup.
  • State tracking: The result’s providerId is used for tracking and drift detection on subsequent applies.