How Providers Work

Scenario: Terraform's plan clearly understands fakecloud_server.api as a resource address, yet it cannot actually create anything until a separate program starts up and negotiates with Terraform over a defined protocol.

Here's a distinction that clears up a lot of early confusion: Terraform is really two things bundled into one CLI. Terraform Core is the engine you've been learning about - it parses your HCL, builds the dependency graph, calculates plans, and manages state. Core has no built-in knowledge of any cloud or service whatsoever. It doesn't know what a "server" or a "certificate" is. That knowledge lives entirely in provider plugins - separate executables that Core downloads (during init) and launches as background processes, then talks to over a well-defined RPC protocol.

Each provider plugin owns everything specific to its service: the exact fields a fakecloud_server resource accepts (its schema), validation rules, how to authenticate to that service's API, how to actually create/read/update/delete objects, how to import existing objects, and how to refresh state by re-reading real infrastructure. This is why a resource type's prefix (fakecloud_..., aws_..., google_...) conventionally tells you which provider handles it - though the authoritative link is the explicit required_providers block, not the naming convention alone. Provider configuration itself (credentials, region, endpoint) typically lives once in the root module; reusable child modules just declare that they need a provider and receive an actual configuration from whoever calls them.

Analogy: Terraform Core is an operating system kernel; each provider is a device driver. The kernel schedules work and coordinates resources in general terms, but it has no idea how to talk to a specific piece of hardware - that hardware-specific protocol knowledge lives entirely in the driver, which the kernel loads and calls into.

A worked configuration

terraform {
  required_providers {
    fakecloud = { source = "shellgenius/fakecloud" }
  }
}
provider "fakecloud" {}

When something goes wrong at apply time, resist the urge to guess - separate the failure into its actual layer: is it a configuration/type error (HCL is malformed or references something nonexistent)? A plugin installation or checksum error (init couldn't fetch or verify the provider)? An authentication/authorization failure (credentials are missing, expired, or lack permission)? A transport failure (the network can't reach the API)? An API-level rejection (the request was well-formed but the service said no - a quota, a name collision)? Or provider-internal inconsistency (a bug in the plugin itself)? TF_LOG=DEBUG shows the raw RPC traffic between Core and the provider, which is often the fastest way to tell these apart - but that traffic can include credentials and other sensitive values, so only enable it briefly and never paste it unsanitized into a public ticket.

Common mistakes in production

Goal: Treat the plan as a change contract: understand every create, update, replacement, and destroy before approving it. Reinforce this lesson in the tf-first-apply Terraform lab. Open /labs/terraform and choose slug tf-first-apply; the lab runs real Terraform against the offline FakeCloud provider.