Overview
Terraform lets you define cloud infrastructure in declarative configuration files, version them in Git, and apply changes consistently across environments. This tutorial walks through installing Terraform, writing your first configuration, and managing the state file.
Why Infrastructure as Code?
| Manual provisioning | Terraform |
|---|---|
| Click-through console changes | Versioned configuration files |
| No record of who changed what | Full audit trail in Git |
| Environment drift | Identical deployments across environments |
| Slow to reproduce | Reproducible in one command |
Install Terraform
# macOS
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
# Ubuntu
sudo apt update && sudo apt install -y gnupg software-properties-common
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform
Precompiled binaries for all platforms are available on the HashiCorp Terraform downloads page.
Verify:
terraform -version
Core Workflow
| Command | Purpose |
|---|---|
terraform init | Download provider plugins |
terraform plan | Preview changes without applying |
terraform apply | Create or modify infrastructure |
terraform destroy | Remove all managed resources |
terraform fmt | Format configuration files |
terraform validate | Check syntax and internal consistency |
Step 1: Configure the Provider
Create main.tf:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.region
}
Step 2: Define Variables
Create variables.tf:
variable "region" {
description = "AWS region"
type = string
default = "us-east-1"
}
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t3.micro"
}
Step 3: Define a Resource
Create ec2.tf:
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
tags = {
Name = "terraform-web"
}
}
output "public_ip" {
value = aws_instance.web.public_ip
}
Step 4: Apply
terraform init
terraform plan
terraform apply
Type yes to confirm. Terraform creates the instance and prints the public IP.
Understanding State
Terraform stores the mapping between your configuration and real resources in terraform.tfstate. This file is critical. For team use, store it remotely:
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
Never commit terraform.tfstate to Git. It often contains secrets in plaintext.
Best Practices
- Use modules to group reusable infrastructure patterns.
- Commit a
.terraform.lock.hclfile to pin provider versions. - Keep state in a remote backend with locking enabled.
- Separate environments into different workspaces or directories.
- Run
terraform fmtandterraform validatein CI.
