Compatibility
Which Terraform provider SDK features terrably supports today, and which patterns are not yet possible.
This page documents the current feature support matrix for terrably compared to the Terraform Plugin Protocol v6 and the official Go SDK.
The items marked Not supported will cause Terraform to surface a generic error or silently ignore the feature. If your provider requires one of these patterns, please open an issue.
Supported today
| Feature | Notes |
|---|---|
| Managed resources (CRUD + plan hook) | Full lifecycle: create, read, update, delete, plan |
| Data sources | Read-only |
| Provider functions (Terraform ≥ 1.8) | Parameters, variadic, return type, deprecation |
terraform import | Implement import() on a resource |
State schema migration (upgrade) | Increment version and implement upgrade() |
requires_replace attributes | Force-replaces on attribute change |
sensitive attributes | Redacted in plan output |
write_only attributes (Terraform ≥ 1.11) | Accepted but never stored in state |
| Block-level descriptions and deprecation | BlockOptions on Block / Schema constructor |
| Nested blocks: single, list, set, map, group | All five nesting modes |
| All scalar types: string, number, bool | — |
| Collection types: list, set, map | — |
object attribute type: single, list, set, map nesting | Use ObjectAttribute — HCL = syntax, not block syntax |
normalizedJson type | JSON stored as a sorted-key string; key-order changes are not diffs |
| Structured logging | createLogger() / TF_LOG_PROVIDER |
object attribute type
Use ObjectAttribute to define a typed object on a resource attribute. This uses HCL assignment (=) syntax, unlike NestedBlock which uses block syntax.
Provider schema (TypeScript)
import { Attribute, ObjectAttribute, types } from "terrably";
new ObjectAttribute("metadata", [
new Attribute("owner", types.string(), { optional: true, computed: true }),
new Attribute("environment", types.string(), { optional: true, computed: true }),
], "single", { optional: true, computed: true })Terraform configuration (HCL)
resource "mycloud_server" "web" {
name = "web-01"
metadata = {
owner = "alice"
environment = "prod"
}
}Nesting modes
| Mode | HCL shape | TypeScript nesting arg |
|---|---|---|
"single" | attr = { key = "val" } | "single" (default) |
"list" | attr = [{ key = "val" }, ...] | "list" |
"set" | attr = [{ key = "val" }, ...] | "set" |
"map" | attr = { label = { key = "val" } } | "map" |
For "set" nesting, terrably compares elements field-by-field (order-insensitive) so a reordered set does not appear as a plan diff.
ObjectAttribute vs NestedBlock
Both represent a group of typed sub-fields. The difference is purely syntactic –
ObjectAttribute | NestedBlock | |
|---|---|---|
| HCL syntax | attr = { ... } | attr { ... } |
| SDK class | ObjectAttribute | NestedBlock / Block |
Supports null | Yes (if optional) | No — blocks cannot be null |
| Terraform type system | object(...) attribute | Block (no type in protocol) |
Not supported
tuple type
A fixed-length, heterogeneously-typed list — e.g. tuple([string, number, bool]).
What you cannot write today
resource "mycloud_route" "example" {
# "range" is [start_port, end_port] — both numbers but positional semantics
range = [8080, 8090]
}Workaround
Use a list(number) attribute with a validate() check, or two separate attributes (range_start, range_end).
dynamic type
An attribute whose type is not known until runtime (cty.DynamicPseudoType in Go). Terraform serialises it with both the type and value in the msgpack payload.
What you cannot write today
resource "mycloud_config" "example" {
# "value" can be a string, number, list, or object depending on context
value = { key = "hello" }
}Workaround
Use normalizedJson to store the value as a JSON string, or use a NestedBlock to represent the structure explicitly.
Ephemeral resources (Terraform ≥ 1.10)
Resources whose values are available during plan/apply but are never written to state. Designed for short-lived secrets (API tokens, one-time passwords, TLS certificates).
What you cannot write today
# provider.tf
ephemeral "mycloud_token" "deploy" {
role = "deployer"
ttl = "15m"
}
resource "mycloud_server" "example" {
api_token = ephemeral.mycloud_token.deploy.value
}Attempting to use an ephemeral resource with a terrably provider will result in –
Error: Ephemeral resources not supportedThere is no workaround yet.
Ephemeral resources require implementing the OpenEphemeralResource, RenewEphemeralResource, and CloseEphemeralResource RPCs plus advertising the capability in GetMetadata.
MoveResourceState (renamed / moved resources)
Allows Terraform's moved block to move state from one resource type to another across providers — for example, renaming a resource type in a new major provider version.
What you cannot write today
# After renaming mycloud_instance → mycloud_server:
moved {
from = mycloud_instance.web
to = mycloud_server.web
}Cross-type moves between different resource types within the same provider do work through normal Terraform state manipulation; what is not supported is provider-assisted cross-type moves where the provider validates and transforms state.
Attempting a cross-provider moved block that reaches this RPC will result in –
Error: MoveResourceState is not implementedThere is no workaround yet.
Private provider data (per-resource opaque bytes)
Terraform passes an opaque []byte blob (planned_private / private) between plan and apply on a per-resource basis. Go providers use this to store information that should influence apply but must not be visible to users (e.g. the ETag of a resource fetched at plan time, or a lock token).
What you cannot do today
The private field is silently discarded — terrably always returns private: new Uint8Array(). If you return a value from plan() that depends on external state fetched at plan time, you cannot safely pass it to apply() through private data.
Workaround
Store any plan-time state as a computed attribute in the resource schema. This is slightly less clean (it appears in terraform show) but functionally equivalent for most use cases.
Capability summary
| Terraform feature | Supported | Notes / Workaround | Tracking issue |
|---|---|---|---|
| Managed resources | ✅ | ||
| Data sources | ✅ | ||
| Provider functions | ✅ | ||
terraform import | ✅ | ||
| State schema migration | ✅ | ||
object attribute type | ✅ | ||
tuple type | ❌ | Use separate attributes or list + validation | #19 |
dynamic type | ❌ | Use normalizedJson or explicit schema | #20 |
| Ephemeral resources | ❌ | Not prioritised yet | #21 |
moved block (cross-type) | ❌ | Not prioritised yet | |
| Private provider data | ❌ | Use a computed attribute instead | #23 |
| Resource identity schemas | ❌ | Returns empty; not prioritised yet | #24 |
| List resources | ❌ | Terraform list command; not prioritised yet | #25 |
| State stores | ❌ | Provider-managed state backends; not prioritised yet | |
| Actions | ❌ | Fairly new; not prioritised yet | #26 |
GenerateResourceConfig | ❌ | Auto-generate HCL from imported resource | |
| Provider meta schema | ❌ | Module-level provider config |
Last updated on