Architecture¶
See also
index | architecture | operations | security | index#Questions
Terraform is HashiCorp's infrastructure-as-code tool that uses HCL (HashiCorp Configuration Language) to declaratively define cloud resources. The core engine parses HCL, builds a dependency graph, and orchestrates provider plugins via gRPC to reconcile desired state with real infrastructure.
Component Overview¶
graph TB
subgraph CLI["CLI Layer"]
TF["terraform CLI"]
end
subgraph CORE["Terraform Core"]
HCL["HCL Parser<br/>(hcl lang library)"]
CFG["Config Loader<br/>(module resolver)"]
GRAPH["Graph Builder<br/>(DAG construction)"]
EVAL["Evaluator<br/>(expression evaluation)"]
PLAN["Plan Engine"]
APPLY["Apply Engine"]
end
subgraph PLUGINS["Plugin System"]
PV["Provider Plugins<br/>(gRPC / protocol v5-v6)"]
PROV["Provisioner Plugins"]
end
subgraph STATE["State Layer"]
SF["State File<br/>(JSON)"]
BE["Backend<br/>(S3, GCS, Azure, consul, local)"]
WS["Workspaces"]
end
subgraph REG["Registry"]
TREG["Terraform Registry<br/>(registry.terraform.io)"]
PRIV["Private Registry<br/>(HCP Terraform)"]
end
subgraph HCPTF["HCP Terraform (optional)"]
API["Terraform Cloud API"]
SENT["Sentinel Policy"]
VCS["VCS Integration"]
end
TF --> HCL
HCL --> CFG
CFG --> GRAPH
GRAPH --> EVAL
EVAL --> PLAN
PLAN --> APPLY
APPLY --> PV
APPLY --> PROV
PLAN --> SF
APPLY --> SF
SF --> BE
WS --> SF
CFG -->|resolve modules| TREG
CFG -->|resolve modules| PRIV
TF -->|remote operations| HCPTF
Core Components¶
HCL Parser¶
The HCL parser (implemented in the hcl Go library, github.com/hashicorp/hcl/v2) handles:
- Parsing
.tffiles into an abstract syntax tree (AST). - Evaluating HCL expressions including variables, locals, conditionals,
forexpressions, and template strings. - Validating block types and attribute schemas against provider-defined schemas.
- Producing diagnostic messages with source location information.
The parser is language-agnostic -- the same library is used by other HashiCorp tools (Vault, Consul, Nomad).
Config Loader and Module System¶
The config loader resolves the full configuration from multiple sources:
- Root module: The
.tffiles in the working directory. - Child modules: Referenced via
moduleblocks, sourced from: - Local filesystem paths.
- Terraform Registry (
registry.terraform.io). - Git repositories (GitHub, GitLab, generic).
- S3 buckets, GCS buckets, and other storage backends.
- HCP Terraform private module registry.
Module versioning is handled through a lock file (.terraform.lock.hcl) that pins provider versions and checksums.
Graph Builder¶
The graph builder constructs a directed acyclic graph (DAG) that determines the order of operations:
- Nodes: Root variables, module variables, providers, resources, data sources, outputs, locals.
- Edges: Implicit dependencies detected by scanning HCL expression references, plus explicit
depends_ondeclarations. - Transformers: A series of graph transformers process the DAG:
ReferenceTransformer-- connects references to their targets.ProviderTransformer-- associates resources with their provider instances.CountBoundaryTransformer-- handlescountandfor_eachexpansion.OrphanResourceTransformer-- detects resources in state but not in config.TransitiveReductionTransformer-- removes redundant edges.
Separate graph builders for each operation phase:
- PlanGraphBuilder -- produces the plan.
- ApplyGraphBuilder -- executes planned changes.
- DestroyGraphBuilder -- reverse-order destruction.
- RefreshGraphBuilder -- updates state from real infrastructure.
- ValidateGraphBuilder -- validates configuration without planning.
Evaluator¶
The evaluator walks the graph and resolves all expressions:
- Evaluates variable values (from
.tfvarsfiles, environment variables, CLI flags). - Resolves
localsblocks in dependency order. - Expands
countandfor_eachinto individual resource instances. - Handles
lifecyclemeta-arguments (create_before_destroy,prevent_destroy,ignore_changes). - Produces the complete "desired state" that represents what the configuration declares.
Plugin Protocol¶
Terraform communicates with providers via a gRPC-based plugin protocol (protocol version 5 and 6).
Protocol Evolution¶
| Version | Transport | Introduced | Notes |
|---|---|---|---|
| v1-v4 | net/rpc (Go-specific) |
Pre-0.12 | Legacy, deprecated |
| v5 | gRPC (tfplugin5) |
Terraform 0.12 | Current standard |
| v6 | gRPC (tfplugin6) |
Terraform 1.0+ | Adds log-level control, moved types |
Plugin Handshake¶
sequenceDiagram
participant Core as Terraform Core
participant Plugin as Provider Plugin
Core->>Plugin: Launch process
Plugin-->>Core: Handshake (stdout)<br/>protocol version + gRPC address + TLS cert
Core->>Plugin: gRPC connection (mTLS)
Core->>Plugin: GetSchema
Plugin-->>Core: Resource & data source schemas
Core->>Plugin: ConfigureProvider
Plugin-->>Core: Config validated
Note over Core,Plugin: Plan phase
Core->>Plugin: PlanResourceChange (per resource)
Plugin-->>Core: Planned state + requires replacement?
Note over Core,Plugin: Apply phase
Core->>Plugin: ApplyResourceChange (per resource)
Plugin-->>Core: New state
Core->>Plugin: Close (graceful shutdown)
Plugin SDKs¶
| SDK | Status | Usage |
|---|---|---|
terraform-plugin-sdk/v2 (SDKv2) |
Mature | Most existing providers |
terraform-plugin-framework |
Recommended | New providers, protocol v6 features |
terraform-plugin-mux |
Active | Combine SDKv2 and framework in one provider |
Plan / Apply Flow¶
flowchart TD
A["terraform init"] --> B["Install providers<br/>Download modules<br/>Initialize backend"]
B --> C["terraform plan"]
C --> D["Parse HCL config"]
D --> E["Load prior state from backend"]
E --> F["Refresh: read real-world state via providers"]
F --> G["Evaluate config against refreshed state"]
G --> H["Build plan graph"]
H --> I["Compute diff per resource instance"]
I --> J["Save plan file (optional)"]
J --> K["terraform apply"]
K --> L["Load saved plan OR re-plan"]
L --> M["Build apply graph"]
M --> N["Walk graph: create / update / delete resources"]
N --> O["Update state incrementally"]
O --> P["Persist final state to backend"]
P --> Q["terraform output"]
Deterministic plans
The plan file is a binary artifact that captures the exact set of changes. When applied, Terraform guarantees that only those planned changes are executed. The plan file includes a cryptographic hash of the configuration to detect configuration drift between plan and apply.
State Management¶
State File Structure¶
The state file (terraform.tfstate) is a JSON document containing:
- Version: State format version (currently 4).
- Terraform version: Version that produced the state.
- Serial: Monotonically increasing counter for optimistic locking.
- Lineage: Unique UUID identifying the state lineage (prevents corruption from swapping state files).
- Outputs: Root module output values.
- Resources: Array of resource instances with:
type,name,provideridentifier.instances: Array of individual instances (fromcount/for_each).- Each instance has
attributes(current state),dependencies,schema_version.
Backend Options¶
| Backend | State Locking | Encryption | Notes |
|---|---|---|---|
local |
No | None | Default, terraform.tfstate on disk |
s3 |
Yes (DynamoDB) | SSE-S3, SSE-KMS | Most common AWS choice |
gcs |
Yes (native) | Google-managed or CMEK | Google Cloud |
azurerm |
Yes (blob lease) | SSE | Azure |
consul |
Yes (session) | Optional (base64) | HashiCorp Consul |
pg |
Yes (advisory lock) | Optional | PostgreSQL |
remote (HCP TF) |
Yes | Encryption at rest | Terraform Cloud managed |
oss |
Yes | SSE | Alibaba Cloud |
Workspaces¶
Workspaces provide isolated state instances within the same configuration:
- Each workspace maintains its own state file.
terraform.workspacevariable enables workspace-conditional logic.- Default workspace is always present (
default). - In HCP Terraform, workspaces map to separate workspaces with independent VCS triggers, variables, and run settings.
HCP Terraform (Terraform Cloud)¶
HCP Terraform extends the open-source CLI with managed infrastructure:
| Feature | Description |
|---|---|
| Remote operations | Plans and applies run on HashiCorp-managed infrastructure |
| VCS integration | Auto-trigger runs on GitHub, GitLab, Bitbucket pushes |
| Sentinel policies | Policy-as-code framework for governance |
| Private registry | Organizational modules and providers |
| Cost estimation | Estimated cloud spend per plan (Business tier) |
| RBAC | Team-based access control per workspace |
| SSO | SAML/SSO with major identity providers |
| API-driven workflow | Full CRUD API for workspace, run, variable management |
Comparison with Alternatives¶
| Aspect | Terraform | OpenTofu | Pulumi |
|---|---|---|---|
| Language | HCL | HCL | TS, Python, Go, .NET, Java |
| License | BSL 1.1 | MPL 2.0 | Apache 2.0 |
| Provider protocol | gRPC v5/v6 | gRPC v5/v6 (compatible) | gRPC (own protocol) |
| State encryption | Not native | Built-in (AES-GCM + KMS) | Per-secret encryption |
| Policy-as-code | Sentinel (paid) | OPA (community) | CrossGuard |
| Managed platform | HCP Terraform | Community/self-hosted | Pulumi Cloud |
References¶
- Terraform documentation
- Terraform GitHub repository
- Plugin protocol
- HCL specification
- HCP Terraform docs
How It Works¶
Provider plugin architecture, state management, plan/apply lifecycle, and dependency graph.
Core Lifecycle¶
sequenceDiagram
participant User as User
participant CLI as Terraform CLI
participant State as State File
participant Provider as Provider Plugin (gRPC)
participant Cloud as Cloud API
User->>CLI: terraform plan
CLI->>State: Read current state
CLI->>Provider: Refresh resource statuses
Provider->>Cloud: API calls to check real state
Provider-->>CLI: Current state
CLI->>CLI: Diff: desired (HCL) vs current
CLI-->>User: Execution plan (+ / ~ / -)
User->>CLI: terraform apply
CLI->>CLI: Build dependency graph (DAG)
CLI->>Provider: Create/Update/Delete resources
Provider->>Cloud: API calls
Provider-->>CLI: Resource attributes
CLI->>State: Write updated state
Dependency Graph (DAG)¶
Terraform builds a Directed Acyclic Graph of all resources, ensuring correct ordering:
flowchart TB
VPC["aws_vpc"] --> Subnet["aws_subnet"]
Subnet --> SG["aws_security_group"]
Subnet --> Instance["aws_instance"]
SG --> Instance
Instance --> EIP["aws_eip"]
style VPC fill:#ff6f00,color:#fff
Resources without dependencies are created in parallel.
Provider Plugin Architecture¶
flowchart LR
CLI_T["Terraform CLI\n(core engine)"] <-->|"gRPC"| AWS["AWS Provider\n(plugin binary)"]
CLI_T <-->|"gRPC"| GCP["GCP Provider"]
CLI_T <-->|"gRPC"| K8s_P["K8s Provider"]
CLI_T <-->|"gRPC"| Custom["Custom Provider"]
style CLI_T fill:#7b42bc,color:#fff
Each provider is a separate binary communicating via gRPC. Providers are downloaded during terraform init from the Terraform Registry.
State File¶
| Aspect | Detail |
|---|---|
| Purpose | Maps HCL resources to real-world objects |
| Format | JSON (human-readable but not for editing) |
| Locking | DynamoDB (AWS), GCS, Consul for remote |
| Sensitive data | ⚠️ Stored in plaintext (use remote backend + encryption) |
| Remote backends | S3, GCS, Azure Blob, Terraform Cloud, Consul |
Sources¶
Benchmarks¶
Scope
Performance characteristics, scaling limits, and resource consumption for Terraform.
Plan/Apply Performance¶
| State Size | Plan Time | Apply Time | Memory |
|---|---|---|---|
| 50 resources | < 5s | 1-3m | 100MB |
| 500 resources | 15-60s | 5-15m | 500MB |
| 2,000 resources | 2-10m | 15-45m | 2GB |
| 10,000 resources | 10-30m | 1-3h | 8GB+ |
Provider API Limits¶
| Provider | Rate Limit | Impact on Large Plans |
|---|---|---|
| AWS | 20-100 req/s (varies by API) | Parallelism > 10 may hit limits |
| Azure | 12,000 req/h per subscription | Large plans need throttling |
| GCP | 10-100 req/s | Moderate limitation |
Module Performance¶
| Module Count | Init Time | Plan Overhead |
|---|---|---|
| 5 | 5-10s | Negligible |
| 20 | 10-30s | 10-20% slower |
| 50+ | 30-120s | Consider splitting |
State File Benchmarks¶
| Metric | Small (< 1MB) | Medium (1-10MB) | Large (10-100MB) |
|---|---|---|---|
| Read | < 1s | 1-5s | 5-30s |
| Write | < 1s | 1-5s | 5-30s |
| Plan (full) | < 30s | 30s-5m | 5-30m |
Sourcing Status¶
Unsourced Performance Data
The performance numbers in this document are estimated from vendor documentation, community benchmarks, and engineering judgment. They do not represent controlled benchmarks with documented test conditions. Specific hardware configurations, software versions, and test methodologies were not recorded.
Use these figures as rough guidance only. For production capacity planning, run your own benchmarks against your specific workload and infrastructure.