Terraform — API Surface#

API types#

Terraform exposes functionality through three distinct surface areas:

  1. CLI — the primary human-facing interface (terraform plan, terraform apply, etc.)
  2. gRPC Plugin Protocol — the provider/provisioner contract (tfplugin5.proto / tfplugin6.proto)
  3. gRPC RPC API (rpcapi) — a machine-facing gRPC interface for automation and HCP Terraform

There is no HTTP/REST API and no exported Go library API. Every internal package is internal/, making external programmatic access impossible without the RPC API.


CLI#

Framework#

github.com/hashicorp/cli (Mitchell Hashimoto’s CLI library, not cobra). Commands are registered as a map[string]cli.CommandFactory at startup in commands.go. Flags are parsed via stdlib flag through a helper extendedFlagSet(name, ...) that assembles reusable flag groups.

Command structure#

Commands are organized in three tiers: primary workflow (advertised prominently), plumbing (useful but secondary), and hidden (legacy aliases or internal plumbing).

Primary workflow commands#

CommandPurpose
initInitialize a working directory; install providers and modules
validateValidate configuration files syntactically and semantically
planCreate an execution plan, show what changes will be made
applyExecute the plan, making infrastructure changes
destroyDestroy all managed infrastructure (alias: apply -destroy)

Infrastructure management#

CommandPurpose
fmtReformat .tf files to canonical style
getDownload module dependencies
importImport existing infrastructure into state
refreshUpdate state file against real infrastructure
outputRead and display output values
showDisplay human-readable state or plan file
graphOutput DOT-format resource dependency graph
consoleInteractive REPL for expression evaluation
queryQuery infrastructure state

Provider management#

CommandPurpose
providersShow provider requirements and selections
providers lockUpdate .terraform.lock.hcl with provider checksums
providers mirrorMirror providers to a local filesystem directory
providers schemaPrint provider schema in JSON
loginObtain and store credentials for a Terraform host
logoutRemove stored credentials for a Terraform host
metadata functionsShow provider function metadata

State management (plumbing)#

CommandPurpose
state listList resources in state
state identitiesList resource identity attributes
state showShow attributes of a single resource
state mvMove an item in state (rename)
state rmRemove a resource from state
state pullOutput raw state to stdout
state pushPush a local state file to remote
state replace-providerReplace provider in state
force-unlockRelease a stuck state lock
taint / untaintMark resource for re-creation (deprecated)

Workspace management#

CommandPurpose
workspace listList workspaces
workspace selectSwitch to a workspace
workspace showShow current workspace name
workspace newCreate a new workspace
workspace deleteDelete a workspace

Stacks (multi-configuration orchestration)#

CommandPurpose
stacksStacks subcommand stub (delegates to rpcapi internally)

Hidden/legacy commands#

CommandNotes
rpcapiMachine-facing gRPC entry point; hidden from help
env / env list/select/new/deleteLegacy aliases for workspace commands
pushRemoved feature stub
internal-pluginInternal plugin server launcher

Experimental (gated by ExperimentsAllowed)#

CommandPurpose
cloudHCP Terraform integration commands
test cleanupClean up resources after terraform test

Flag patterns#

Flags are parsed using stdlib flag via a per-command extendedFlagSet. Arguments are decomposed into typed reusable structs in internal/command/arguments/:

  • arguments.View — global: -no-color, -compact-warnings, -json
  • arguments.State-state, -state-out, -backup
  • arguments.Operation-auto-approve, -parallelism, -refresh, -refresh-only, -replace, -target
  • arguments.Vars-var, -var-file
  • Per-command extras: e.g. Plan adds -out, -detailed-exitcode, -generate-config-out

Global flag: -chdir=<dir> is a pre-parse special flag handled before command dispatch in main.go, changing the working directory before running any command.

Persistent env var injection: TF_CLI_ARGS and TF_CLI_ARGS_<command> are read at startup and prepended to os.Args before parsing, allowing automation to inject flags globally.

Output format#

Every command supports two output modes:

  • Human (default): colored, TTY-aware, with ANSI formatting. Width adapts to terminal columns.
  • JSON (-json): newline-delimited JSON messages, designed for machine parsing. Structured via internal/command/json*/ renderers.

The views/ sub-package abstracts the choice: each command gets a views.Plan, views.Apply, etc. type that dispatches to either the human or JSON renderer.


gRPC Plugin Protocol (Provider / Provisioner API)#

This is the contract between Terraform Core and provider binaries. It is the most public API surface in Terraform — thousands of provider binaries implement it.

Proto files#

  • internal/tfplugin5/tfplugin5.proto — Protocol 5 (SDK v2 providers)
  • internal/tfplugin6/tfplugin6.proto — Protocol 6 (SDK v2 with new features)
  • docs/plugin-protocol/tfplugin5.proto / tfplugin6.proto — canonical reference copies

Both protocols are maintained simultaneously. The grpcwrap package adapts providers.Interface Go implementations to serve either protocol. The plugin and plugin6 packages provide the gRPC client stubs (one per protocol version).

Protocol 5 — service Provider#

RPCPurpose
GetMetadataProvider capabilities/metadata
GetSchemaFull provider, resource, data-source schemas
PrepareProviderConfigValidate and normalize provider config
ValidateResourceTypeConfigValidate resource config
ValidateDataSourceConfigValidate data source config
UpgradeResourceStateUpgrade persisted state from older schema version
GetResourceIdentitySchemasIdentity attribute schemas
UpgradeResourceIdentityUpgrade stored identity
ConfigurePass configured provider credentials/settings
ReadResourceRefresh resource state against real infrastructure
PlanResourceChangeCompute diff for a resource
ApplyResourceChangeApply resource CRUD operations
ImportResourceStateImport existing resource into state
MoveResourceStateMove resource to another provider instance
ReadDataSourceRead a data source
GenerateResourceConfigGenerate HCL config for imported resource
ValidateEphemeralResourceConfigValidate ephemeral resource config
OpenEphemeralResourceOpen an ephemeral resource lifetime
RenewEphemeralResourceRenew ephemeral resource lease
CloseEphemeralResourceClose ephemeral resource lifetime
ListResourceStream resource list results
ValidateListResourceConfigValidate list resource config
GetFunctionsGet provider-defined functions
CallFunctionCall a provider-defined function
PlanActionPlan a provider-defined action
InvokeActionInvoke a provider-defined action (streaming)
ValidateActionConfigValidate action config
StopGraceful shutdown signal

Protocol 6 — service Provider#

Identical set of RPCs as Protocol 5 with the same semantics. Protocol 6 adds native support for newer Terraform features (e.g., ephemeral resources, list resources, actions, state stores) while Protocol 5 carries polyfills for backward compatibility.

State Store RPCs (Protocol 6 extension):

RPCPurpose
ValidateStateStoreConfigValidate state store backend config
ConfigureStateStoreConfigure the state store
ReadStateBytesStream state bytes from provider-managed store
WriteStateBytesStream state bytes to provider-managed store
LockStateAcquire state lock
UnlockStateRelease state lock
GetStatesList all state entries
DeleteStateDelete a state entry

service Provisioner (Protocol 5, deprecated)#

RPCPurpose
GetSchemaProvisioner config schema
ValidateProvisionerConfigValidate config
ProvisionResourceRun provisioner, streaming log output
StopGraceful stop

Transport#

Providers are launched as subprocesses by hashicorp/go-plugin. The plugin protocol uses gRPC over a loopback TCP listener (or Unix socket), with the address negotiated over stdin/stdout using a magic cookie handshake. The subprocess is verified by matching MAGIC_COOKIE environment variables.


gRPC RPC API (rpcapi)#

The rpcapi subsystem exposes Terraform Core as a go-plugin gRPC server, intended for consumption by HCP Terraform and other automation. It is launched via the hidden rpcapi CLI command.

Entry point#

rpcapi.ServePlugin(ctx, opts) — validates the go-plugin magic cookie (TERRAFORM_RPCAPI_COOKIE), then serves protocol version 1 with a tfcore plugin set.

Proto files and services#

All live under internal/rpcapi/terraform1/:

setup/setup.protoservice Setup#

Capability negotiation between client and server.

RPCPurpose
HandshakeVersion negotiation, capability exchange
StopGraceful server shutdown

dependencies/dependencies.protoservice Dependencies#

Provider installation and lock file management.

RPCPurpose
OpenSourceBundle / CloseSourceBundleOpen a source bundle (config + modules)
OpenDependencyLockFile / CreateDependencyLocks / CloseDependencyLocksManage .terraform.lock.hcl
GetLockedProviderDependenciesQuery locked provider versions
BuildProviderPluginCacheInstall providers (streaming progress)
OpenProviderPluginCache / CloseProviderPluginCacheManage provider cache handle
GetCachedProvidersList cached providers
GetBuiltInProvidersList built-in providers
GetProviderSchemaRetrieve provider schema

packages/packages.protoservice Packages#

Provider and module package registry operations (no session state required).

RPCPurpose
ProviderPackageVersionsList available versions for a provider
FetchProviderPackageDownload a provider package
ModulePackageVersionsList available module versions
ModulePackageSourceAddrResolve module source address
FetchModulePackageDownload a module package

stacks/stacks.protoservice Stacks#

The Stacks execution model for multi-configuration orchestration.

RPCPurpose
OpenStackConfiguration / CloseStackConfigurationOpen a .tfstack.hcl config
ValidateStackConfigurationValidate a stack configuration
FindStackConfigurationComponentsEnumerate components in a stack
OpenState / CloseStateOpen a Stacks state object (streamed)
PlanStackChangesCompute a stacks plan (streaming events)
OpenPlan / ClosePlanHandle plan objects
ApplyStackChangesApply a stacks plan (streaming events)
OpenStackInspector / InspectExpressionResultInteractive expression evaluation
OpenTerraformState / CloseTerraformStateWrap classic Terraform state for migration
MigrateTerraformStateMigrate classic state into Stacks
ListResourceIdentitiesList identities of managed resources

OTel interceptors#

Both unary and streaming gRPC calls are instrumented with OpenTelemetry via otelgrpc.UnaryServerInterceptor() / otelgrpc.StreamServerInterceptor() (set up in server.go).


Plugin / Extension system#

Provider plugins (hashicorp/go-plugin + gRPC)#

  • Mechanism: Out-of-process subprocess, gRPC over go-plugin
  • Extension points:
    • Implement service Provider in either tfplugin5.proto or tfplugin6.proto
    • Provider binaries discovered via registry, filesystem mirror, or dev override
    • providers.Interface is the Go-side abstraction; 30+ methods
  • Registration: ContextOpts.Providers map[addrs.Provider]providers.Factory; built-in providers (e.g., terraform, registry.terraform.io/hashicorp/null) are registered the same way but never launched as subprocesses

Stacks / Cloud plugins (stacksproto1.proto, cloudproto1.proto)#

  • Mechanism: CommandService.Execute — a single streaming RPC that proxies sub-commands from Terraform Core to the cloud/stacks plugin process
  • Extension points: internal/stacksplugin and internal/cloudplugin — separate plugin processes for HCP Terraform’s Stacks and Cloud features

Credentials helpers#

  • Mechanism: ~/.terraformrccredentials_helper block → external binary that speaks the credential helper protocol (JSON over stdin/stdout)
  • Discovered via pluginDiscovery.FindPlugins("credentials", globalPluginDirs)

Backend plugins (state storage)#

  • Not a runtime plugin system — the nine remote state backends (S3, GCS, Azure, Consul, k8s, PG, OCI, COS, OSS) are separate Go modules in the same repository with their own go.mod files, wired via replace directives
  • Each implements backend.Backend and statemgr.Full

Summary of key design choices#

  1. No exported library API. internal/ everywhere is intentional. External tools must use the RPC API or the plugin protocol.

  2. Two machine-facing surfaces with different audiences. The plugin protocol (tfplugin5/6) is for provider authors; the rpcapi gRPC is for orchestration tooling (HCP Terraform). They are entirely separate wire formats.

  3. Dual protocol maintenance. Protocol 5 and 6 coexist because the provider ecosystem cannot be upgraded atomically. New features are Protocol-6-only; Protocol 5 receives minimal maintenance.

  4. CLI output duality. Every command outputs either human-readable or structured JSON (-json). The views/ abstraction makes this systematic rather than ad hoc.

  5. Flag composition over inheritance. CLI flags are assembled from reusable typed structs (State, Operation, Vars) rather than a deep flag-inheritance hierarchy. This keeps each command’s accepted flags explicit and documentable.