Terraform / OpenTofu¶
What is it?¶
Terraform (and its community-driven fork, OpenTofu) is an Infrastructure as Code (IaC) tool.
It allows you to define, provision, and manage cloud infrastructure using simple, declarative configuration files written in HCL (HashiCorp Configuration Language).
Instead of clicking through cloud dashboards, you describe your infrastructure in text files — which makes it versionable, reproducible, and automatable.
OpenTofu is a fully open-source fork of Terraform, created after HashiCorp changed Terraform’s license from the Mozilla Public License (MPL) to the Business Source License (BUSL) in 2023.
👉 Think of Terraform/OpenTofu as the programming toolchain for your cloud infrastructure.
Why do we need it? Where do we use it?¶
Managing modern infrastructure manually is hard.
You have VMs, networks, databases, load balancers… all living in different cloud providers.
Terraform/OpenTofu helps by providing:
- Automation: You write your desired state once, and Terraform makes it happen.
- Version control: Infrastructure configurations can live in Git, just like code.
- Reproducibility: Deploy the same setup across environments (dev, staging, prod).
- Multi-cloud support: Works with AWS, Azure, GCP, Kubernetes, and many others.
You’ll find Terraform/OpenTofu used in: - DevOps pipelines (e.g., automated infrastructure provisioning in CI/CD) - Cloud migrations - Kubernetes cluster setups - Self-service infrastructure portals
History Lesson¶
| Year | Event | Description |
|---|---|---|
| 2014 | Terraform v0.1 released | HashiCorp introduces Terraform as an open-source IaC tool under the MPL 2.0 license. |
| 2017–2020 | Growth & ecosystem boom | Terraform gains popularity, adding providers for AWS, Azure, GCP, and others. IaC becomes a DevOps standard. |
| 2021 | Terraform 1.0 released 🎉 | Marks Terraform as stable for production use; backward compatibility guaranteed. |
| August 2023 | License change | HashiCorp switches from MPL to BUSL, sparking community concern about open-source freedom. |
| September 2023 | OpenTofu announced | Linux Foundation and community launch OpenTofu as a fork of Terraform, fully open source and Apache 2.0 licensed. |
| 2024+ | Parallel development | Terraform and OpenTofu continue as separate projects, mostly compatible but independently maintained. |
Interaction with other topics?¶
Terraform/OpenTofu sits at the intersection of several key areas:
- CI/CD pipelines: Integrates with tools like GitHub Actions, GitLab CI, Jenkins, etc.
- Configuration management: Complements tools like Ansible, Puppet, or Chef (Terraform provisions infra, Ansible configures it).
- Cloud providers: Interacts with AWS, Azure, GCP, etc., via providers (plugins).
- Secrets management: Works with Vault, AWS Secrets Manager, etc.
- Containers & Kubernetes: You can define clusters and services declaratively.
Here’s how Terraform fits in a DevOps workflow:
flowchart LR
Dev[Developer] -->|writes HCL files| Git[Git Repository]
Git --> CI[CI/CD Pipeline]
CI --> TF[Terraform / OpenTofu]
TF --> Cloud[Cloud Provider]
Cloud --> Infra[Deployed Infrastructure]
How does it work?¶
Terraform/OpenTofu follows a plan → apply → destroy workflow, powered by a few core building blocks.
The main components¶
- HCL (HashiCorp Configuration Language): The declarative language you write in
.tffiles. You describe the desired state (resources, their arguments, and relationships). HCL also supports expressions, variables,for_each,count, and conditionals. - CLI binary (
terraformortofu): The command‑line engine that parses HCL, builds a dependency graph, proposes a plan, and executes it. It also talks to backends for remote state and to providers for CRUD operations. - Providers: Plug‑in shims that translate resource operations into API calls against real systems (AWS, Azure, GCP, Kubernetes, databases, SaaS, etc.). Providers expose resources and data sources.
- Backends / State storage: Where the state file lives (local, S3+DynamoDB, GCS, AzureRM, Terraform Cloud, etc.). Backends also provide locking to avoid concurrent writes.
- Remote APIs: The actual cloud/service endpoints that create VMs, buckets, networks, etc. Providers handle authentication, rate limiting, retries, and API nuances on your behalf.
The dependency graph (DAG)¶
Internally, Terraform represents your configuration as a Directed Acyclic Graph (DAG) of nodes:
- Nodes are resources, modules, data sources, and provisioners.
- Edges are dependencies inferred from references (e.g.,
aws_subnet.appneedsaws_vpc.main.id). - During plan/apply, Terraform performs a topological sort of this DAG so that independent nodes can run in parallel, while dependent nodes wait. This is why you often see multiple resources being created concurrently.
flowchart TD
VARS[variables.tf / .tfvars] --> PARSE[Parse HCL]
PARSE --> GRAPH[Build Dependency DAG]
STATE[(State Backend)] --> PLAN[Compute Plan]
GRAPH --> PLAN
PLAN -->|CRUD via Providers| APIS[Cloud / Service APIs]
APIS --> APPLY[Real World]
APPLY --> REFRESH[Refresh & Update State]
REFRESH --> STATE
HCL, state, and the real world¶
- Read & Refresh: When you run
plan, Terraform reads your state and refreshes it by querying providers (unless you disable refresh). This detects drift (differences between state and reality). - Diff: Terraform compares refreshed state to the desired state from HCL, producing an execution plan (
+ create,~ update in‑place,- destroy,-/+ replace). - Apply: Terraform executes provider operations to reconcile reality with your HCL. On success, it persists the new state atomically in the backend (with a lock if supported).
💡 Tip: If state is remote (e.g., S3 + DynamoDB lock or Terraform Cloud), teams avoid conflicts and get reliable locking and history.
Modules - composition and reuse¶
- A module is just a directory of
.tffiles. The root of your repo is the root module; anymoduleblocks you call are child modules. - Modules expose input variables and outputs and are versioned (e.g.,
version = "~> 2.3"). - You can source modules locally, from the Terraform/Tofu Registry, a Git repo, or private registries.
- Modules enable abstraction (hide provider details), consistency (same patterns across environments), and composition (stack modules together).
module "network" {
source = "git::https://example.com/infra/network.git?ref=v2.3.1"
cidr = var.vpc_cidr
az_count = 3
}
module "app" {
source = "./modules/app"
image_tag = var.image_tag
subnet_ids = module.network.private_subnet_ids
}
Services like Terraform Cloud¶
Managed services (e.g., Terraform Cloud/Enterprise) improve the IaC workflow by providing: - Remote state + locking out of the box (no DIY S3/GCS + DynamoDB). - VCS‑driven runs: automatically run plan/apply on PRs with run logs and approvals. - RBAC & workspaces: isolate environments, manage permissions, audit trails. - Secure variable storage: store secrets/workspace vars (masked in logs). - Policies (e.g., Sentinel/Open Policy Agent): enforce guardrails before apply. - Run consistency: plans happen in consistent, ephemeral runners; no “works on my laptop.” - Cost estimates & drift detection (where supported): preview spend and detect config drift.
You can get many of these benefits self‑hosted with alternative backends (e.g., S3+lock, Atlantis for VCS‑driven plans, OPA for policy), but managed platforms reduce glue work and operational burden.
Examples: Usage or Theory¶
Common Terraform / OpenTofu commands¶
| Command | What it does | Example |
|---|---|---|
init |
Initialize a working directory, download providers & modules, configure backend | terraform init -upgrade |
validate |
Check configuration is syntactically valid and internally consistent | terraform validate |
fmt |
Reformat HCL files to canonical style | terraform fmt -recursive |
providers |
Show required and installed providers | terraform providers |
graph |
Output the execution DAG (great for learning/debugging) | terraform graph > graph.dot |
plan |
Create an execution plan (no changes) | terraform plan -var-file=prod.tfvars |
apply |
Apply changes (reconciles to desired state) | terraform apply -auto-approve |
destroy |
Destroy managed infrastructure | terraform destroy -target=aws_s3_bucket.example |
output |
Show root module outputs | terraform output -json |
show |
Inspect state or plan files | terraform show or terraform show plan.out |
state |
Low‑level state operations (list, rm, mv) | terraform state list |
import |
Adopt existing resources into state | terraform import aws_s3_bucket.example my-bucket |
| Workspaces | Manage multiple logical environments in one config | terraform workspace new staging |
| Login | Authenticate to Terraform Cloud/Enterprise | terraform login |
🧪 Modern replacement: Instead of the old
taintcommand, useplan -replace/apply -replace. Example:terraform apply -replace=aws_instance.web.
Example: rendering the DAG¶
Example: OpenTofu parity¶
Everything above works similarly with OpenTofu. Replace terraform with tofu: