Terraform

Manage bem functions and workflows declaratively with Terraform

Hand off to an LLM

The bem Terraform provider lets you declare functions and workflows as code, version them in your repo, and apply changes through your existing infrastructure pipeline. It calls the same V3 API as the SDKs and the dashboard, so anything you can build in the UI you can manage with Terraform.

Capabilities

The provider exposes the two primitives at the heart of bem and read-only data sources for everything you'd query.

ResourcePurpose
bem_functionCreate and update functions of any type — extract, classify, split, join, enrich, payload_shaping, send. Owns the function's output schema, configuration, and tags.
bem_workflowWire functions into a DAG. Owns nodes, edges, the entry-point node, connectors (e.g. Box, Dropbox, S3), and tags.
Data sourceReturns
bem_functionA single function by name.
bem_functionsA list of functions in the environment.
bem_workflowA single workflow by name.
bem_workflowsA list of workflows in the environment.

Authentication uses an API key, picked up from the BEM_API_KEY environment variable or set explicitly in the provider block.

Functions and workflows are versioned on the bem side. Each terraform apply that changes a resource creates a new version — older versions remain readable and existing calls keep working.

Prerequisites

Step 1: Set up and initialize the provider

Create a main.tf and declare the provider:

terraform {
  required_providers {
    bem = {
      source  = "bem-team/bem"
      version = "~> 0.1"
    }
  }
}

provider "bem" {
  # Reads BEM_API_KEY from the environment when omitted.
}

Export your API key and initialize the working directory:

export BEM_API_KEY='your-api-key-here'
terraform init

terraform init downloads the provider from the Terraform Registry and writes a lock file. You're ready to declare resources.

Step 2: Create a function and a workflow

Add an extract function and a single-node workflow that uses it. The output_schema is a standard JSON Schema, passed as a string via jsonencode so HCL stays readable:

resource "bem_function" "invoice_extractor" {
  function_name      = "invoice-extractor"
  type               = "extract"
  display_name       = "Invoice Extractor"
  output_schema_name = "Invoice"

  output_schema = jsonencode({
    type     = "object"
    required = ["invoiceNumber", "vendor", "totalAmount"]
    properties = {
      invoiceNumber = { type = "string", description = "Unique invoice identifier" }
      invoiceDate   = { type = "string", description = "Invoice date (YYYY-MM-DD)" }
      vendor = {
        type = "object"
        properties = {
          name    = { type = "string" }
          address = { type = "string" }
        }
      }
      totalAmount = { type = "number" }
    }
  })

  tags = ["finance", "managed-by-terraform"]
}

resource "bem_workflow" "invoice_intake" {
  name           = "invoice-intake"
  display_name   = "Invoice Intake"
  main_node_name = "extract"

  nodes = [{
    name = "extract"
    function = {
      name        = bem_function.invoice_extractor.function_name
      version_num = bem_function.invoice_extractor.function.version_num
    }
  }]

  tags = ["finance"]
}

The workflow references the function by name, and pins to its current version_num — Terraform will roll the workflow forward whenever the function version changes. Because there's only one node, edges can be omitted.

Apply the configuration:

terraform plan
terraform apply

The apply creates the function first, then the workflow that depends on it. Once it finishes, you can call the workflow exactly like any workflow created through the UI or SDKs.

Step 3: Update the function and workflow

Iterating is the same pattern as any Terraform resource: edit the configuration, plan, apply.

Suppose the schema needs a lineItems array and you want to add a payload-shaping step that reformats the output before delivery. Update the function and add a second node plus an edge to the workflow:

resource "bem_function" "invoice_extractor" {
  function_name      = "invoice-extractor"
  type               = "extract"
  display_name       = "Invoice Extractor"
  output_schema_name = "Invoice"

  output_schema = jsonencode({
    type     = "object"
    required = ["invoiceNumber", "vendor", "totalAmount"]
    properties = {
      invoiceNumber = { type = "string", description = "Unique invoice identifier" }
      invoiceDate   = { type = "string", description = "Invoice date (YYYY-MM-DD)" }
      vendor = {
        type = "object"
        properties = {
          name    = { type = "string" }
          address = { type = "string" }
        }
      }
      lineItems = {
        type = "array"
        items = {
          type = "object"
          properties = {
            description = { type = "string" }
            quantity    = { type = "number" }
            unitPrice   = { type = "number" }
            amount      = { type = "number" }
          }
        }
      }
      totalAmount = { type = "number" }
    }
  })

  tags = ["finance", "managed-by-terraform"]
}

resource "bem_function" "invoice_shaper" {
  function_name = "invoice-shaper"
  type          = "payload_shaping"
  display_name  = "Invoice Shaper"

  shaping_schema = jsonencode({
    invoice_id = "invoiceNumber"
    vendor     = "vendor.name"
    total      = "totalAmount"
    items      = "lineItems"
  })
}

resource "bem_workflow" "invoice_intake" {
  name           = "invoice-intake"
  display_name   = "Invoice Intake"
  main_node_name = "extract"

  nodes = [
    {
      name = "extract"
      function = {
        name        = bem_function.invoice_extractor.function_name
        version_num = bem_function.invoice_extractor.function.version_num
      }
    },
    {
      name = "shape"
      function = {
        name        = bem_function.invoice_shaper.function_name
        version_num = bem_function.invoice_shaper.function.version_num
      }
    },
  ]

  edges = [{
    source_node_name      = "extract"
    destination_node_name = "shape"
  }]

  tags = ["finance"]
}

Plan and apply:

terraform plan
terraform apply

Terraform issues an in-place update to the function (creating a new function version on bem's side) and updates the workflow to reference both functions and the new edge. Existing calls continue against their pinned version; new calls use the new one.

To remove the workflow, run terraform destroy — the workflow is deleted first, then the functions it referenced.

Reference

On this page