terrably
Core concepts

Provider-defined functions

Expose pure, stateless functions callable from Terraform configuration with provider::name::function().

Terraform ≥ 1.8 lets providers expose pure functions callable directly from HCL configuration –

output "upper" {
  value = provider::mycloud::uppercase("hello")
  # → "HELLO"
}

Functions are stateless and have no side effects. They run during plan, not apply.

Implementing a function

src/functions/uppercase.ts
import type { TerrablyFunction, FunctionSignature, FunctionCallContext, Provider } from "terrably";
import { types } from "terrably";

export class UppercaseFunction implements TerrablyFunction {
  constructor(_provider: Provider) {}

  getName(): string { return "uppercase"; }

  getSignature(): FunctionSignature {
    return {
      parameters: [
        { name: "input", type: types.string(), description: "The string to convert." },
      ],
      returnType: { type: types.string() },
      summary: "Convert a string to uppercase",
    };
  }

  call(_ctx: FunctionCallContext, args: unknown[]): unknown {
    return String(args[0]).toUpperCase();
  }
}

Registering functions

Add getFunctions() to your Provider implementation –

src/provider.ts
import { UppercaseFunction } from "./functions/uppercase.js";

export class MyCloudProvider implements Provider {
  // ...existing methods...

  getFunctions(): FunctionClass[] {
    return [UppercaseFunction];
  }
}

FunctionSignature

Prop

Type

FunctionParameter

Prop

Type

Variadic functions

Declare a variadicParameter to accept a variable number of trailing arguments –

export class ConcatFunction implements TerrablyFunction {
  getName() { return "concat"; }

  getSignature(): FunctionSignature {
    return {
      parameters: [
        { name: "separator", type: types.string() },
      ],
      variadicParameter: { name: "values", type: types.string() },
      returnType: { type: types.string() },
      summary: "Join strings with a separator",
    };
  }

  call(_ctx: FunctionCallContext, args: unknown[]): unknown {
    const [separator, ...values] = args as string[];
    return values.join(separator);
  }
}
output "joined" {
  value = provider::mycloud::concat(", ", "apple", "banana", "cherry")
  # → "apple, banana, cherry"
}

Error handling

Report errors via ctx.diagnostics. Terraform aborts plan/apply and displays the error alongside the output:

call(ctx: FunctionCallContext, args: unknown[]): unknown {
  const n = args[0] as number;
  if (n < 0) {
    ctx.diagnostics.addError(
      "Invalid argument",
      `Expected a non-negative number, got ${n}.`,
    );
    return 0; // return a safe default; Terraform discards it on error
  }
  return Math.sqrt(n);
}

Throwing a JavaScript exception also surfaces as a FunctionError to Terraform:

call(_ctx: FunctionCallContext, args: unknown[]): unknown {
  const n = args[0] as number;
  if (n < 0) throw new Error(`Expected non-negative, got ${n}`);
  return Math.sqrt(n);
}

Prefer ctx.diagnostics for user-facing validation errors — they produce cleaner Terraform output.

Last updated on

On this page