Explain how to develop a custom Terraform provider, including the plugin protocol (gRPC), schema definition, CRUD lifecycle, and how you would test it. Give a concrete example of when a custom provider is the right choice.
Quick Answer
Use the Terraform Plugin Framework (not the legacy SDKv2) to define resources with schema, implement CRUD operations via gRPC protocol, handle state management, and test with acceptance tests that create real resources. Custom providers are appropriate for internal platforms, proprietary APIs, or when existing providers lack features.
Detailed Answer
When to Build a Custom Provider
- Internal platform APIs (service catalog, config management) that have no public provider - Wrapping a proprietary SaaS API (custom CMDB, internal DNS) - When the official provider lacks resources you need and contributing upstream is too slow - NOT for simple API calls (use null_resource with provisioners or http data source instead)
Plugin Architecture
Terraform providers communicate with the Terraform core via gRPC using the Plugin Protocol (v6 for Plugin Framework). The provider binary is a separate process that Terraform launches. The protocol defines operations: GetProviderSchema, ValidateProviderConfig, ConfigureProvider, ReadResource, PlanResourceChange, ApplyResourceChange, ImportResourceState.
Development with Plugin Framework
The modern Plugin Framework (hashicorp/terraform-plugin-framework) replaces SDKv2. Key concepts: 1. Provider: Configures authentication (API keys, endpoints) 2. Resources: Define schema (attributes with types, required/optional/computed), implement CRUD methods 3. Data Sources: Read-only lookups 4. Schema types: StringAttribute, Int64Attribute, ListAttribute, ObjectAttribute with plan modifiers and validators
Testing Strategy
- Unit tests: Test individual functions (parsing, validation) - Acceptance tests: Use resource.Test() framework that runs real Terraform apply/destroy cycles - Use TF_ACC=1 environment variable to enable acceptance tests - Implement ImportState for every resource to support terraform import
Publishing
Publish to the Terraform Registry via GitHub releases with GoReleaser. Sign with GPG key. Follow naming convention: terraform-provider-<name>.
Code Example
# Provider implementation (Go - Plugin Framework)
package provider
import (
"context"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
)
type serviceResource struct {
client *internal.APIClient
}
func (r *serviceResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{Computed: true},
"name": schema.StringAttribute{Required: true},
"tier": schema.StringAttribute{Optional: true, Default: stringdefault.StaticString("standard")},
},
}
}
func (r *serviceResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var plan serviceModel
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
svc, err := r.client.CreateService(plan.Name.ValueString(), plan.Tier.ValueString())
if err != nil {
resp.Diagnostics.AddError("Create failed", err.Error())
return
}
plan.ID = types.StringValue(svc.ID)
resp.Diagnostics.Append(resp.State.Set(ctx, plan)...)
}
# Acceptance test
func TestAccServiceResource(t *testing.T) {
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: `resource "myplatform_service" "test" { name = "test-svc" }`,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("myplatform_service.test", "name", "test-svc"),
resource.TestCheckResourceAttrSet("myplatform_service.test", "id"),
),
},
},
})
}
# Build and install locally
go build -o terraform-provider-myplatform
mkdir -p ~/.terraform.d/plugins/mycompany.com/internal/myplatform/1.0.0/darwin_arm64
cp terraform-provider-myplatform ~/.terraform.d/plugins/mycompany.com/internal/myplatform/1.0.0/darwin_arm64/Interview Tip
Provider development questions test deep understanding of Terraform's architecture. Know the difference between Plugin Framework and SDKv2 (Framework is the modern approach). Emphasize that you'd contribute to existing providers before building custom ones. Mention acceptance testing as critical - unit tests alone are insufficient for infrastructure providers.