Table of contents
- The Moment You Get Tired of Clicking the Console
- What Is Infrastructure as Code
- Where Terraform Fits
- Declarative vs Imperative
- How Terraform Works
- Terraform vs Other Tools
- What Terraform Doesn’t Solve
- Before Taking the First Step
The Moment You Get Tired of Clicking the Console
You’ve probably logged into the AWS Console and created an EC2 instance before. Pick a region, choose an AMI, select an instance type, create a security group, attach a key pair, and finally hit “Launch.” The first instance is exciting and fun.
The trouble starts when you need to create the same environment again. One for development, another for staging, yet another for production. The three environments are similar but subtly different. Which security group had port 22 open again? What was the instance type for staging? There’s no trace left of what you clicked in the console. The configuration notes you jotted down in Google Docs are out of sync with reality after just three weeks.
When someone says “replicate that environment to another region,” an entire day evaporates. Infrastructure built by clicking has no reproducibility. This is exactly the problem Terraform solves.
What Is Infrastructure as Code
Infrastructure as Code (IaC) is exactly what it sounds like. You define infrastructure as code. Cloud resources like servers, networks, storage, and databases are described in text files, and running those files creates the actual infrastructure in the cloud.
# Declare a single EC2 instance
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
tags = {
Name = "web-server"
}
}
This file is the blueprint for your infrastructure. Running terraform apply causes Terraform to call the AWS API and create the actual instance. Run the same file in a different account and you get an identical instance.
Managing with code means you can use the tools developers are already familiar with for infrastructure too.
- Version control with Git. Who changed what and when is all recorded in the commit log
- Review via PRs. “Add a new subnet to the VPC” becomes “a PR merged to the main branch”
- Validate with CI. Run
terraform planto automatically verify changes - Rollback is straightforward. Go back to the desired commit and apply
None of these happen naturally with console clicking. IaC is a declaration: “Let’s treat infrastructure operations like software development.”
Where Terraform Fits
Terraform is one of the most widely used IaC tools. HashiCorp released it as open source in 2014, and it has effectively become the standard for multi-cloud IaC.
Terraform’s character can be summed up in one line: A tool that manages multiple cloud providers declaratively using a single language (HCL).
Three words are key.
- Multiple clouds: AWS, Azure, GCP, Kubernetes, Cloudflare, Datadog, GitHub, and more. If “it has an API,” you can probably manage it with Terraform
- Single language (HCL): HashiCorp Configuration Language. More expressive than YAML and more readable than JSON
- Declarative: You write “what state should exist,” not “how to create it”
The last point — declarative — best represents Terraform’s philosophy. Let’s dig into this concept in the next section.
Declarative vs Imperative
When comparing IaC tools, the distinction between declarative and imperative always comes up. Terraform falls in the declarative camp.
Imperative means “do this, then do that” — listing steps one by one. Bash scripts are a classic example.
# Imperative — give instructions step by step
aws ec2 create-security-group --group-name web-sg --description "web"
aws ec2 authorize-security-group-ingress --group-name web-sg --protocol tcp --port 80 --cidr 0.0.0.0/0
aws ec2 run-instances --image-id ami-xxx --count 1 --instance-type t3.micro \
--security-groups web-sg
The problem is that running this script twice throws an error saying the security group already exists. You have to write logic in the script yourself to “skip what’s already created and only create what’s missing.” As environments grow more complex, you end up in if-else hell.
Declarative is different. You only write “this state should exist.”
# Declarative — just describe the desired state
resource "aws_security_group" "web" {
name = "web-sg"
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_instance" "web" {
ami = "ami-xxx"
instance_type = "t3.micro"
vpc_security_group_ids = [aws_security_group.web.id]
}
No matter how many times you apply this file, the result is the same. If it already exists, leave it alone; if it doesn’t, create it; if attributes have changed, fix them. This property is called idempotency. Terraform calculates the difference between the actual state and the declared state and makes only the necessary API calls.
The benefits of the declarative approach can be visualized as follows.
flowchart LR
DEV[Developer writes .tf files] --> PLAN[terraform plan]
PLAN -->|Calculate diff| DIFF{Current state vs<br/>Desired state}
DIFF -->|Needs addition| CREATE[Create resource]
DIFF -->|Needs modification| UPDATE[Update resource]
DIFF -->|Needs deletion| DELETE[Delete resource]
DIFF -->|No difference| NOOP[Do nothing]
The developer writes “this state should exist,” and Terraform figures out “the actions needed to reach that state.” This aligns closely with Kubernetes’ declarative philosophy as well.
How Terraform Works
You might be curious how the declarative approach is actually implemented. Terraform works by combining three things.
- Configuration (.tf files): The desired state written by the user
- State (terraform.tfstate): A JSON file where Terraform records “the things I created”
- Provider: Plugins that call actual APIs like AWS or GCP
terraform plan compares these three. It shows you what’s new, what changed, and what disappeared by comparing what’s recorded in the State against the Configuration. terraform apply actually applies those differences.
flowchart TB
USER[User]
TF[Terraform Core]
CFG[.tf files<br/>Desired state]
STATE[(terraform.tfstate<br/>Recorded state)]
PROV[Provider<br/>AWS/GCP/Azure...]
CLOUD[(Actual cloud)]
USER -->|Write| CFG
USER -->|terraform apply| TF
CFG --> TF
STATE <--> TF
TF <-->|API calls| PROV
PROV <--> CLOUD
The especially important concept here is State. Terraform needs to remember “this resource is one I created” to know which resources to modify and which to delete. State will be covered in depth in a later part. For now, just remember that “Terraform has a file where it remembers its past.”
Terraform vs Other Tools
Terraform isn’t the only IaC tool. There are several similar-looking tools, each with a different focus. Understanding these differences matters when choosing Terraform in practice.
vs Ansible
Ansible is the most frequently compared tool. Both describe environments in YAML/HCL, but they cover different domains.
- Ansible: Configures what’s inside the server. Tasks like package installation, file copying, and service restarts
- Terraform: Creates the server itself and its surrounding infrastructure. Things like VPCs, load balancers, and RDS
The two aren’t competitors — they’re more like a division of labor. A typical combination is creating EC2 with Terraform and installing nginx inside it with Ansible. Ansible is fundamentally imperative (Playbook tasks run in order), while Terraform is fully declarative.
vs AWS CloudFormation
CloudFormation is an IaC service built directly by AWS. It describes AWS resources in JSON/YAML.
- CloudFormation: AWS-only. When AWS releases a new feature, CloudFormation gets updated alongside it
- Terraform: Multi-cloud. Supports numerous providers beyond AWS, including GCP, Azure, Cloudflare, and more
If your organization only uses AWS, CloudFormation is a reasonable choice. But if you’re multi-cloud, or need to manage SaaS services outside AWS (Datadog, Cloudflare, GitHub), Terraform is much cleaner.
State management also differs. CloudFormation has AWS manage the state for you, so there’s less to worry about, but Terraform requires you to manage the State file yourself. In return, Terraform works the same way across any cloud.
vs Pulumi
Pulumi is a relatively new tool. It describes infrastructure using programming languages (TypeScript, Python, Go, etc.).
- Pulumi: Written in general-purpose languages. Abstractions like loops, functions, and classes are freely available
- Terraform: Written in a DSL (HCL). More constraints, but correspondingly more consistent
For complex scenarios where the expressive power of a programming language is needed (e.g., complex manifest generation logic), Pulumi has the advantage. On the other hand, if multiple team members need to understand and maintain the code at a similar level, Terraform’s “declarative DSL” constraint means a lower learning curve. Terraform does support expressions like loops and for, but full program flow control is not possible.
Comparison Summary
flowchart TB
subgraph IMP[Imperative / Procedural]
ANS[Ansible<br/>Server configuration management]
end
subgraph DEC[Declarative]
TF[Terraform<br/>Multi-cloud]
CF[CloudFormation<br/>AWS only]
PU[Pulumi<br/>General-purpose language based]
end
subgraph SCOPE[Domain]
INFRA[Infrastructure provisioning]
CFG[Server internal configuration]
end
TF -.-> INFRA
CF -.-> INFRA
PU -.-> INFRA
ANS -.-> CFG
Here’s a summary of why teams choose Terraform in practice.
- Want to manage multiple clouds/SaaS with a single tool
- Need the stability and idempotency of a declarative approach
- Has the largest community and provider ecosystem
- Rich official modules (Terraform Registry)
What Terraform Doesn’t Solve
To truly understand a tool’s value, you need to know its boundaries. Terraform is not a silver bullet.
- Weak at managing server internals. That’s why it’s combined with Ansible/Chef/Puppet
- Not suited for runtime deployment. Rolling updates or canary deployments are better handled by Kubernetes/ArgoCD
- Doesn’t do real-time monitoring/auto-healing. After a single apply, if state drifts, it stays that way until the next apply
- Leaves application code alone. This was never its purpose
Terraform’s role is clear: A tool that declaratively manages the lifecycle (creation, modification, deletion) of cloud resources. By focusing on this role, it’s also easy to combine with other tools.
Before Taking the First Step
If you’ve read this far, you should have a sense of “why” Terraform is needed. Next comes the “how.” In the next part, we’ll actually install Terraform, set up an AWS profile, and spin up our first EC2 instance with code. We’ll learn the meaning of the four commands — terraform init, plan, apply, destroy — through hands-on experience.
The declarative philosophy clicks fastest when you learn it through practice. Let’s feel the difference from the console-clicking experience firsthand.




Loading comments...