Terraform sits at the center of modern Infrastructure as Code (IaC) practice: we describe infrastructure in text, keep it in Git, and let an engine reconcile desired state with real-world cloud APIs.
Infrastructure as Code (IaC)
Infrastructure as Code is the practice of managing and provisioning infrastructure through machine‑readable configuration files rather than interactive configuration tools or consoles. The crucial mental shift is to treat infrastructure the same way you treat application code: versioned, reviewed, and automated.
Key characteristics:
- Declarative definitions
You describe what infrastructure you want (VPCs, subnets, instances, load balancers) instead of scripting how to create it step by step. - Version controlled
Configurations live in Git (or similar), so you get history, diffs, branching, and pull requests for infra changes. - Repeatable and consistent
The same configuration, with different inputs (variables, workspaces), can stand up dev, test, and prod environments that are structurally identical. - Testable and reviewable
Changes are peer‑reviewed, validated via plans, policy checks, and possibly automated tests in CI/CD, instead of ad‑hoc console clicks.
The rationale is straightforward: we already know how to manage complexity in software systems with code and discipline; IaC applies those same practices to infrastructure.
What is Terraform?
Terraform is a declarative Infrastructure as Code tool created by HashiCorp that provisions and manages infrastructure across many platforms (AWS, Azure, GCP, Kubernetes, on‑prem, and various SaaS APIs). You express your desired infrastructure using HashiCorp Configuration Language (HCL), and Terraform figures out the necessary operations to reach that state.
Conceptually, Terraform:
- Reads your configuration, which represents the desired state.
- Compares it to its state plus the actual infrastructure.
- Constructs an execution plan describing the required changes.
- Applies the plan by calling provider APIs in the correct dependency order.
Why this model is powerful:
- Multi‑cloud, single workflow
The same CLI, syntax, and mental model work across different clouds and services. - State‑aware
Terraform tracks what it has created, so it can safely update or destroy resources without guesswork. - Ecosystem and reuse
A rich registry of modules and providers enables you to stand on others’ shoulders instead of rebuilding common patterns.
In essence, Terraform acts as a reconciliation engine: it continuously aligns reality with the infrastructure state you declare in code.
Core Components of Terraform
While Terraform’s architecture is modular, you mainly interact with a small set of core concepts.
CLI and Core Engine
The CLI (terraform) is your main interface. Terraform Core:
- Parses HCL configuration files.
- Builds a dependency graph of resources and data sources.
- Compares configuration and state to determine what must change.
- Produces an execution plan and applies it while respecting dependencies.
The dependency graph is central: it ensures, for example, that networks exist before instances are created, and databases exist before applications that depend on them.
Providers
Providers are plugins that encapsulate the logic for talking to external APIs such as AWS, Azure, GCP, Kubernetes, GitHub, Datadog, and many others. Each provider:
- Defines available resource types and data sources.
- Implements create, read, update, and delete semantics for those resources.
- Handles authentication and low‑level API interactions.
The rationale is separation of concerns: Terraform Core stays generic, while providers handle domain‑specific details of each platform.
Resources
Resources are the primitive units of infrastructure in Terraform. Each resource represents a managed object, such as:
- A virtual machine or container cluster.
- A network component (VPC, subnet, load balancer).
- A managed service instance (database, cache, queue).
Terraform manages the full lifecycle of resources: creation, in‑place update when possible, replacement when required, and destruction when no longer desired.
Data Sources
Data sources allow configurations to read information from providers without managing the lifecycle of those objects. They are typically used to:
- Look up existing infrastructure (e.g., a VPC or subnet created outside the current configuration).
- Retrieve dynamic values (e.g., the latest AMI matching a filter).
- Integrate with pre‑existing environments instead of forcing everything to be created by the same codebase.
They keep configurations flexible and reduce hard‑coded values, which improves reuse and maintainability.
State and Backends
Terraform uses a state file to map resources in your configuration to real-world objects in the target platforms. This state:
- Stores resource identifiers and metadata.
- Enables Terraform to understand what already exists and what needs to change.
- Is updated after each successful apply.
Backends determine where this state is stored:
- Local backend: state in a file on your machine; fine for experiments and learning.
- Remote backends (e.g., S3, GCS, Terraform Cloud): better for teams, as they support centralized storage, locking, and collaboration.
The rationale is that state is a single source of truth about managed infrastructure, and treating it carefully (remote, locked, backed up) is critical for safe operations.
Modules
Modules are reusable, composable units of Terraform configuration. A module can contain:
- Resources and data sources.
- Variables, outputs, locals, and even submodules.
Reasons to structure code into modules:
- Encapsulation
Hide low‑level details behind a stable interface of inputs and outputs. - Reuse
Apply the same pattern (e.g., a VPC, a Kubernetes cluster, a standard microservice stack) in multiple environments or projects. - Governance
Centralize best practices and security controls in shared modules, reducing drift and inconsistent patterns across teams.
Variables, Outputs, and Locals
These language constructs support configurability and clarity:
- Variables
Declare the inputs your configuration expects (e.g., region, instance type, environment name). They enable per‑environment customization without forking the code. - Outputs
Expose selected values afterapply(e.g., IP addresses, ARNs, URLs). Outputs are often consumed by other systems or simply used for manual checks. - Locals
Store computed values or shared expressions to avoid duplication and encode small bits of logic directly in the configuration.
The rationale is to keep your Terraform code DRY, expressive, and easy to reason about as configurations grow.
Typical Terraform Workflow
Terraform promotes a structured workflow that aligns naturally with Git‑based development and CI/CD pipelines. This workflow is crucial to reducing risk and improving predictability.
1. Write Configuration
You start by writing .tf files using HCL:
- Declare providers and their configuration.
- Define resources, data sources, modules, variables, locals, and outputs.
- Organize files logically (root module, submodules, environment dirs).
The focus is on describing the target state of the infrastructure rather than prescribing a sequence of imperative steps.
2. Initialize (terraform init)
You run:
terraform init
This command:
- Downloads required providers and modules.
- Sets up the backend for state (local by default, or remote if configured).
- Prepares the working directory so subsequent commands can function correctly.
Rationale: separating initialization from planning/applying makes dependencies explicit and reproducible, especially across machines or CI runners.
3. Plan (terraform plan)
You run:
terraform plan
Terraform then:
- Reads configuration and current state.
- Queries providers for the real infrastructure state.
- Computes and displays the execution plan, indicating which resources will be created, changed, or destroyed.
The plan serves as your infrastructure “diff,” analogous to git diff for code. In a mature setup, this plan is typically generated as part of a pull request, allowing reviewers to reason about the exact infra impact of a code change before approval.
4. Apply (terraform apply)
You run:
terraform apply
Terraform:
- Either recomputes or uses a previously saved plan.
- Executes the required operations in dependency order.
- Handles partial failures and retries where possible.
- Updates the state file upon successful completion.
The key practice is discipline: operators avoid ad‑hoc console changes, and instead always modify the .tf files, inspect the plan, and then apply. This ensures the code and reality remain aligned.
5. Destroy (terraform destroy)
When you need to decommission an environment, you run:
terraform destroy
Terraform:
- Plans the removal of managed resources.
- Executes deletions in an order that respects dependencies.
- Updates the state so it no longer references removed resources.
This is especially valuable for ephemeral or per‑branch environments and is a powerful tool for cost control and cleanup.
Recent Comments