What are Terraform providers behind the scenes — gRPC plugin protocol?
Quick Answer
Terraform providers are separate binary processes that communicate with the Terraform core via a gRPC-based plugin protocol. The core sends RPCs like PlanResourceChange and ApplyResourceChange, and the provider translates these into cloud API calls. This architecture allows providers to be developed, versioned, and distributed independently of the core binary.
Detailed Answer
Terraform's provider architecture is built on HashiCorp's go-plugin framework, which uses gRPC for inter-process communication. Think of it like a restaurant: Terraform core is the head chef who designs the menu (configuration) and coordinates the kitchen, while providers are specialized line cooks (AWS, Azure, GCP) who know how to prepare specific dishes (resources). They communicate via tickets (gRPC messages), and each cook can be replaced or upgraded without retraining the head chef.
When you run terraform init, Terraform downloads provider binaries from the configured registry (registry.terraform.io by default) and stores them in the .terraform/providers directory. Each provider is a standalone Go binary compiled for your platform. When Terraform needs to interact with a provider, it launches the binary as a subprocess. The provider binary starts a gRPC server on a random local port and communicates the port back to Terraform core via stdout using a handshake protocol. This is the 'plugin negotiation' phase.
The gRPC protocol defines several key RPCs. GetProviderSchema returns the provider's complete schema: all resource types, data sources, and their attributes with types and validation rules. This is how Terraform knows what arguments aws_rds_cluster accepts without hardcoding AWS-specific knowledge. ValidateResourceConfig checks that user-provided configuration is structurally valid before any API calls. PlanResourceChange computes the proposed new state for a resource given the current state and desired configuration. ApplyResourceChange executes the actual infrastructure change by calling cloud APIs. ReadResource fetches the current state of a resource for drift detection.
The protocol uses Protocol Buffers for serialization, with a schema called tfplugin6.proto (for protocol version 6, used by modern providers). Resource attribute values are encoded as MessagePack within the protobuf messages, using a type system called cty (a Go library for dynamic typing). This double encoding exists because protobuf alone cannot express Terraform's complex type system (nested blocks, sets of objects, sensitive values).
Provider development uses the Terraform Plugin Framework (the modern approach) or the older Terraform Plugin SDK v2. The framework provides abstractions for implementing the gRPC service methods. When you write a provider resource, you implement methods like Create, Read, Update, Delete, and the framework translates these into the appropriate gRPC responses.
Production implications of this architecture include: provider version pinning is critical because different provider versions expose different gRPC schemas, and a schema mismatch can corrupt state. The subprocess model means provider crashes do not crash Terraform core, but they can leave resources in an inconsistent state. Provider parallelism is controlled by the -parallelism flag, which limits how many concurrent RPC calls Terraform makes. Network-isolated environments need a provider mirror or filesystem mirror because init downloads from the internet by default. Understanding this architecture is essential for debugging provider-specific errors: when Terraform reports 'provider produced inconsistent result,' it means the provider's ApplyResourceChange returned state that does not match its PlanResourceChange prediction.