Terraform — Interfaces#
Interface catalog#
providers.Interface#
- Package:
internal/providers - File:
internal/providers/provider.go:17 - Methods (35+):
GetProviderSchema(),GetResourceIdentitySchemas(),ValidateProviderConfig(),ValidateResourceConfig(),ValidateDataResourceConfig(),ValidateEphemeralResourceConfig(),ValidateListResourceConfig(),UpgradeResourceState(),UpgradeResourceIdentity(),ConfigureProvider(),Stop() error,ReadResource(),PlanResourceChange(),ApplyResourceChange(),ImportResourceState(),GenerateResourceConfig(),MoveResourceState(),ReadDataSource(),OpenEphemeralResource(),RenewEphemeralResource(),CloseEphemeralResource(),CallFunction(),ListResource(),ValidateStateStoreConfig(),ConfigureStateStore(),ReadStateBytes(),WriteStateBytes(),LockState(),UnlockState(),GetStates(),DeleteState(),PlanAction(),InvokeAction(),ValidateActionConfig(),Close() error - Purpose: Defines the complete contract for a Terraform provider plugin — the component that actually communicates with cloud APIs. Every resource CRUD, schema negotiation, state upgrade, ephemeral resource lifecycle, function call, and state store operation is declared here.
- Implementations:
internal/plugin.GRPCProvider— Protocol 5 gRPC client stub (go-plugin subprocess)internal/plugin6.GRPCProvider— Protocol 6 gRPC client stubinternal/providers/mock_provider.go— Programmable test mockinternal/grpcwrap— adaptsproviders.Interfaceto serve as a gRPC server (used for test providers embedded in Terraform itself)
- Design quality: Intentionally broad — this is a value-object protocol boundary, not a use-case interface. The breadth reflects the complete provider protocol rather than the interface segregation principle (ISP). The companion
StateStoreChunkSizeSetteroptional interface carves out one stateful negotiation concern. The interface grew over time as new resource types (ephemeral, list, actions, state stores) were added to the protocol.
backend.Backend#
- Package:
internal/backend - File:
internal/backend/backend.go:44 - Methods:
ConfigSchema() *configschema.Block,PrepareConfig(cty.Value) (cty.Value, tfdiags.Diagnostics),Configure(cty.Value) tfdiags.Diagnostics,StateMgr(workspace string) (statemgr.Full, tfdiags.Diagnostics),DeleteWorkspace(name string, force bool) tfdiags.Diagnostics,Workspaces() ([]string, tfdiags.Diagnostics) - Purpose: Minimal contract for state-storage backends. Provides configuration lifecycle (schema → validate → configure) and workspace-aware state manager creation. Notably does not include operation execution — that is split into
backendrun.OperationsBackend. - Implementations:
internal/backend/local.Local,internal/cloud.Cloud,internal/backend/remote.Remote, plus nine remote-state backends (S3, GCS, Azure, Consul, k8s, PG, OCI, COS, OSS) each as separate Go modules. - Design quality: Well-segregated. The clean separation from
OperationsBackendmeans the majority of backends (pure remote state) implement only 6 methods and have no dependency on the operations packages.
backendrun.OperationsBackend#
- Package:
internal/backend/backendrun - File:
internal/backend/backendrun/operation.go:38 - Methods: Embeds
backend.Backend+Operation(context.Context, *Operation) (*RunningOperation, error),ServiceDiscoveryAliases() ([]HostAlias, error) - Purpose: Extension of
Backendfor the two backends that actually execute Terraform operations (local and HCP Terraform remote).Operation()is non-blocking — it returns aRunningOperationwhose context the caller blocks on. - Implementations:
internal/backend/local.Local,internal/cloud.Cloud,internal/backend/remote.Remote - Design quality: Good use of interface extension via embedding. The comment in the source explicitly notes that most backends should not implement this — it’s an opt-in enrichment for operation-capable backends.
terraform.EvalContext#
- Package:
internal/terraform - File:
internal/terraform/eval_context.go:36 - Methods (30+):
StopCtx() context.Context,Path() addrs.ModuleInstance,Hook(func(Hook) (HookAction, error)) error,Input() UIInput,InitProvider(),Provider(),ProviderSchema(),CloseProvider(),ConfigureProvider(),ProviderInput(),SetProviderInput(),Provisioner(),ProvisionerSchema(),ClosePlugins(),EvaluateBlock(),EvaluateExpr(),EvaluateReplaceTriggeredBy(),EvaluateImportIdExpression(),EvaluateImportReferences(),NamedValues(),Changes(),State(),RefreshState(),Checks(),Instances(),Deferrals(),EphemeralResources(),GetEncryptionKey(),WithPath() EvalContext,Actions() - Purpose: The evaluation context threaded through every graph node during a walk. Provides each node access to providers, state, planned changes, named values (variables/outputs/locals), schema, and all other runtime resources. Acts as a service locator scoped to a module instance path.
- Implementations:
BuiltinEvalContext(sole production implementation);MockEvalContextfor tests. - Design quality: Large interface by design — it is the central service locator for the graph walk. The
WithPath()method returning a newEvalContextscoped to a child module is an elegant design that avoids global mutable state while supporting deeply nested module calls. The single production implementation is a sign that this is an internal abstraction for testability, not for extensibility.
terraform.GraphNodeExecutable#
- Package:
internal/terraform - File:
internal/terraform/execute.go:10 - Methods:
Execute(EvalContext, walkOperation) tfdiags.Diagnostics - Purpose: The single method that makes a DAG node “executable” during a graph walk. Implemented by every node type that performs actual work (resource planning/applying, variable evaluation, output setting, module calls, provider initialization/closing, etc.). Nodes that do not implement this interface are skipped by the walker.
- Implementations: ~20+ concrete node types:
NodeAbstractResourceInstance,NodeApplyableResourceInstance,NodeDestroyableResourceInstance,NodeApplyableOutput,NodeDestroyableOutput,NodeModuleExpand,nodeExpandApplyableModuleVariable,NodeApplyableProvider,NodeCloseProvider, etc. - Design quality: Exemplary ISP adherence — a single-method interface that orthogonally composes with the ~15 other behavioral mix-in interfaces used by the same node types. The pattern allows the walker to check
Executecapability separately from schema-attachment capability, reference capability, provider-consumer capability, etc.
terraform.GraphTransformer#
- Package:
internal/terraform - File:
internal/terraform/transform.go:15 - Methods:
Transform(*Graph) error - Purpose: A single step in the graph build pipeline. Each
GraphTransformerreceives the entire graph and may add vertices, add edges, remove vertices, or validate structure.BasicGraphBuilderruns a slice of these sequentially to construct operation-specific graphs. - Implementations: 30+ concrete transformers:
TransitiveReductionTransformer,ReferenceTransformer,ProviderTransformer,OrphanResourceInstanceTransformer,ModuleExpansionTransformer,TargetsTransformer,CountBoundaryTransformer, etc. - Companion:
GraphVertexTransformer— a narrower variant for per-vertex replacement,Transform(dag.Vertex) (dag.Vertex, error). - Design quality: Pipeline/chain-of-responsibility pattern via a minimal interface. Composability is high — new transforms can be added without modifying existing ones. The
GraphTransformMulticombinator function shows the pattern is self-aware.
terraform.Hook#
- Package:
internal/terraform - File:
internal/terraform/hook.go:56 - Methods (~20):
PreApply(),PostApply(),PreDiff(),PostDiff(),PreProvisionInstance(),PostProvisionInstance(),PreProvisionInstanceStep(),PostProvisionInstanceStep(),ProvisionOutput(),PreRefresh(),PostRefresh(),PreImportState(),PostImportState(),PrePlanImport(),PostPlanImport(),PreApplyImport(),PostApplyImport(), and action hooks - Purpose: Observer interface for Terraform lifecycle events. Used by the CLI to drive progress output (the
UiHook) and by the test framework. Each hook method returns(HookAction, error)— returningHookActionHaltcancels the in-progress operation. - Implementations:
UiHook(CLI progress),NilHook(no-op base for embedding),CountHookandStateHook(internal state tracking during apply), test mocks. - Design quality: The
NilHookembedding pattern is idiomatic for large observer interfaces — implementors embedNilHookand override only the methods they care about. TheHookActionreturn type giving the observer the ability to halt execution is unusual and powerful.
statemgr.Full (composed interface)#
- Package:
internal/states/statemgr - File:
internal/states/statemgr/statemgr.go:26 - Composed from:
Storage(=Transient+Persistent) +LockerTransient:WriteState(*states.State),State() *states.StatePersistent:Refresher(RefreshState() error) +Persister(PersistState(*schemarepo.Schemas) error) +OutputReader(GetRootOutputValues())Locker:Lock(info *LockInfo) (string, error),Unlock(id string) error
- Purpose: The full state manager contract returned by
backend.Backend.StateMgr(). Separates in-memory transient state (fast reads/writes within one Terraform run) from persistent storage (shared across processes) with an optional distributed lock. - Implementations:
remote.State(wraps aremote.Client),filesystem.Filesystem,inmem.State, plus one per remote backend (S3, GCS, etc.) - Design quality: Excellent example of interface composition. Each sub-interface is independently useful:
Lockeris checked separately (if locker, ok := stateMgr.(statemgr.Locker); ok),OutputReaderis a refinement allowing special permissions for reading outputs vs. the full state. The fine-grained composition enables progressive enhancement without forcing all implementations to provide all capabilities.
Interface patterns#
Size distribution: Bimodal. Core protocol interfaces (
providers.Interface,EvalContext) are intentionally broad (30–35 methods) because they represent complete wire protocols or service locators. Behavioral mix-in interfaces for graph nodes are single- or two-method (GraphNodeExecutable,GraphTransformer,GraphNodeDynamicExpandable,GraphNodeReferenceable). No medium-size interfaces — the design makes a deliberate choice between “complete protocol” and “single capability”.Embedding: Used extensively for composition:
backendrun.OperationsBackendembedsbackend.Backendstatemgr.Fullcomposes five interfaces via two layers of embeddingstatemgr.Storage=Transient + PersistentNilHookis designed to be embedded by partialHookimplementors
Implicit satisfaction: Mixed. Provider interfaces (
providers.Interface) are defined by the framework (consumer side), and provider binary implementations satisfy them at runtime via gRPC. Graph-node mix-in interfaces are defined by transformers (also consumer side) — nodes silently gain capabilities by implementing matching method sets. Thestatemgrinterfaces are defined by the framework and satisfied by backends.stdlib interfaces used: Minimal.
erroris used inStop() errorandClose() error.context.Contextappears inEvalContext.StopCtx()andOperationsBackend.Operation().io.Reader/io.Writerare not primary abstractions here — the codebase uses customUIOutput/UIInputinterfaces instead.
Key abstractions#
providers.Interface— The most load-bearing interface in the entire codebase. Every interaction with a cloud provider flows through this 35-method contract. The dual gRPC protocol implementations (Protocol 5 and 6) both satisfy it, and the mock implementation enables comprehensive integration testing without real cloud credentials. This interface is the Terraform provider protocol.terraform.GraphNodeExecutable— The most architecturally elegant interface: a single method that separates “what a node does” from “what structural roles a node plays”. The ~20 orthogonal behavioral mix-in interfaces (GraphNodeProvider, GraphNodeReferenceable, GraphNodeDynamicExpandable, etc.) compose with it to describe a node’s full identity without inheritance.terraform.GraphTransformer— Enables the composable pipeline that builds operation-specific graphs. The ~30 implementations of this one-method interface are what makePlanGraphBuilderandApplyGraphBuilderdiffer — they use different[]GraphTransformerslices. Adding new graph behavior means adding a newGraphTransformer, not modifying existing ones.backend.Backend+backendrun.OperationsBackend— The clean two-level split defines who does what:Backendowns state,OperationsBackendowns execution. This is why nine remote-state backends (S3, GCS, etc.) can exist as separate Go modules with no dependency on operation execution code.statemgr.Full— The composed state manager interface shows ISP applied correctly at scale: five fine-grained interfaces compose into one aggregate that backends return, while callers that need only locking or only output-reading can type-assert to the narrower interface they require.
Interface-driven extensibility#
Terraform uses interfaces at two distinct scales of extensibility:
External extensibility (provider protocol): providers.Interface is the plugin contract. Third-party providers — AWS, GCP, Azure, Kubernetes, and thousands of community providers — implement this contract as out-of-process gRPC servers. They never import Terraform’s internal/ packages; the interface is expressed purely as a gRPC wire protocol via tfplugin5.proto / tfplugin6.proto. The grpcwrap package translates between the Go interface and the gRPC protocol in both directions.
Internal extensibility (graph node capabilities): The ~15 behavioral mix-in interfaces in internal/terraform/transform_*.go make the graph build pipeline open for extension. Each GraphTransformer inspects the graph vertices via type assertions against these interfaces and connects or decorates nodes accordingly. Adding a new resource capability means:
- Define a new
GraphNodeXxxinterface (1 method) - Implement it on relevant node types
- Write a
GraphTransformerthat acts on nodes satisfying that interface
The two-level approach (gRPC for external plugins, in-process interfaces for internal graph nodes) gives Terraform complete isolation from provider code while keeping internal composition efficient and testable.