terrably
Development

Local testing

Two workflows for iterating on your provider locally — dev_overrides for daily development, and dev mode for attaching a debugger.

The simplest workflow. Terraform uses your local binary directly instead of downloading from the registry; no terraform init required. Read the official Terraform documentation on dev_overrides to learn more.

Build the provider binary

pnpm run build
# → bin/terraform-provider-mycloud

Rebuild after any TypeScript change.

Configure dev_overrides

Point Terraform at the directory containing your binary –

tf-workspace/.terraformrc
provider_installation {
  dev_overrides {
    "myorg/mycloud" = "/absolute/path/to/your-provider/bin"
  }
  direct {}
}

Then export the path:

export TF_CLI_CONFIG_FILE="$PWD/tf-workspace/.terraformrc"

Keeps your global Terraform config clean.

~/.terraformrc
provider_installation {
  dev_overrides {
    "myorg/mycloud" = "/absolute/path/to/your-provider/bin"
  }
  direct {}
}

Affects all Terraform workspaces on your machine.

Write a Terraform config

tf-workspace/main.tf
terraform {
  required_providers {
    mycloud = { source = "myorg/mycloud" }
  }
}

provider "mycloud" {
  api_url = "http://127.0.0.1:8765"
}

resource "mycloud_server" "example" {
  name   = "hello"
  region = "us-east-1"
}

Run Terraform

cd tf-workspace
terraform plan
terraform apply -auto-approve
terraform destroy -auto-approve

No terraform init needed with dev_overrides. Terraform skips the registry lookup and spawns the binary directly.

Fast iteration loop

# Edit TypeScript → recompile → plan (no restart needed)
pnpm exec tsc && terraform plan

Each terraform plan spawns a fresh process, so there is no server to restart.


Mode 2 — dev mode

Use this when you need to attach a debugger, print detailed logs, or inspect the exact gRPC messages Terraform sends.

The provider starts manually and stays alive. Terraform reconnects to it on each command.

Enable dev mode in your entry point

src/main.ts
import { serve } from "terrably";
import { MyProvider } from "./provider.js";

const dev = process.argv.includes("--dev") || process.env["TF_PLUGIN_DEBUG"] === "1";
serve(new MyProvider(), { dev }).catch(console.error);

Build and start the provider

pnpm exec tsc

TF_PLUGIN_MAGIC_COOKIE=d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2 \
TF_PLUGIN_DEBUG=1 node dist/src/main.js

The provider prints:

Dev mode — set this environment variable –

    export TF_REATTACH_PROVIDERS='{"registry.terraform.io/myorg/mycloud":{"Protocol":"grpc","ProtocolVersion":6,"Pid":12345,"Test":true,"Addr":{"Network":"unix","String":"/tmp/tf-js-provider-12345-...sock"}}}'

Copy the export and run Terraform

In a separate shell –

export TF_REATTACH_PROVIDERS='...paste from above...'
terraform plan

Terraform connects to the running process. Run as many plan/apply cycles as you like without restarting. Kill the provider with Ctrl-C when done.

Copy a fresh TF_REATTACH_PROVIDERS value every time you restart the provider. The Unix socket path changes on each run.

Last updated on

On this page