Terraform Guide — Infrastructure as Code with HCL
Terraform is an infrastructure-as-code tool that lets you define, provision, and manage cloud resources across AWS, Azure, GCP, and hundreds of other providers using a declarative configuration language (HCL).
What You’ll Learn
- Writing Terraform configurations with providers and resources
- Using variables and outputs for reusable configurations
- Understanding Terraform state and remote state management
- Creating and using modules for reusable infrastructure
- Executing the plan/apply workflow safely
- Managing infrastructure across multiple environments
Why Terraform Matters
Clicking through cloud console UIs to create servers, databases, and networks is error-prone and unrepeatable. Terraform lets you define your entire infrastructure in code — version-controlled, reviewed, and applied consistently. When you need to recreate infrastructure, you run terraform apply, not manually recreate 50 resources from memory. DodaTech uses Terraform to manage Durga Antivirus Pro’s cloud infrastructure — the entire production environment (VPCs, EC2 instances, RDS databases, load balancers) is defined in Terraform configs, making disaster recovery a single command away.
flowchart LR
A[Bash & Cloud Basics] --> B[Terraform]
B --> C[Providers & Resources]
B --> D[State Management]
B --> E[Modules]
B --> F[Plan / Apply]
C --> G[AWS / Azure / GCP]
D --> H[Remote State]
E --> I[Reusable Components]
style B fill:#844fba,color:#fff
Core Concepts
Providers and Resources
# main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
# Configure the AWS provider
provider "aws" {
region = "us-east-1"
}
# Define resources
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0" # Amazon Linux 2
instance_type = "t2.micro"
tags = {
Name = "WebServer"
Env = "production"
}
}
resource "aws_security_group" "web_sg" {
name = "web-server-sg"
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}Plan / Apply Workflow
# Initialize the project (download providers)
terraform init
# Output:
# Initializing the backend...
# Initializing provider plugins...
# Terraform has been successfully initialized!
# Preview changes
terraform plan
# Output:
# Terraform will perform the following actions:
# + aws_instance.web create
# + aws_security_group.web_sg create
# Plan: 2 to add, 0 to change, 0 to destroy.
# Apply changes
terraform apply
# Enter a value: yes
# Output:
# aws_security_group.web_sg: Creating...
# aws_security_group.web_sg: Creation complete
# aws_instance.web: Creating...
# aws_instance.web: Creation complete
# Apply complete! Resources: 2 added, 0 changed, 0 destroyed.Output: terraform plan shows what will be created/changed/destroyed without making changes. terraform apply executes the plan. Always review the plan before applying — it catches accidental deletions.
Variables and Outputs
# variables.tf
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t2.micro"
}
variable "environment" {
description = "Environment name (dev/staging/prod)"
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
variable "tags" {
description = "Common tags for all resources"
type = map(string)
default = {
Project = "MyApp"
ManagedBy = "Terraform"
}
}# outputs.tf
output "instance_public_ip" {
description = "Public IP of the web server"
value = aws_instance.web.public_ip
}
output "instance_id" {
value = aws_instance.web.id
}
output "connection_string" {
value = "ssh://ec2-user@${aws_instance.web.public_ip}"
sensitive = false
}# Set variables via command line or file
terraform apply -var="instance_type=t3.small" -var="environment=staging"
# Use a tfvars file
# terraform.tfvars
instance_type = "t3.medium"
environment = "prod"
# Apply with tfvars
terraform apply -var-file="prod.tfvars"State and Remote State
# backend.tf — store state in S3 (never use local state for teams)
terraform {
backend "s3" {
bucket = "myapp-terraform-state"
key = "production/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-state-lock"
encrypt = true
}
}
# The state is now in S3, not locally
# Multiple team members can run Terraform safely
# DynamoDB prevents concurrent operationsWhy remote state matters: With local state, if your laptop dies, your infrastructure definition is gone. With remote state in S3, your entire infrastructure definition is backed up, accessible to your team, and locked against concurrent modifications.
Modules
# modules/webserver/main.tf
variable "name" { type = string }
variable "instance_type" { type = string }
variable "subnet_id" { type = string }
variable "vpc_id" { type = string }
resource "aws_instance" "this" {
ami = data.aws_ami.amazon_linux.id
instance_type = var.instance_type
subnet_id = var.subnet_id
tags = { Name = var.name }
}
resource "aws_security_group" "this" {
name = "${var.name}-sg"
vpc_id = var.vpc_id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
output "instance_ip" {
value = aws_instance.this.public_ip
}# production/main.tf — using the module
module "web_server" {
source = "../modules/webserver"
name = "prod-web"
instance_type = "t3.large"
subnet_id = aws_subnet.public.id
vpc_id = aws_vpc.main.id
}
output "web_ip" {
value = module.web_server.instance_ip
}Common Mistakes
Storing secrets in variables or state: Terraform state often contains plaintext values of resource attributes (database passwords, access keys). Use
sensitive = trueand a secrets manager (AWS Secrets Manager, Vault).Running
terraform applywithoutterraform plan: Always review the plan first. A typo in a variable can cause Terraform to destroy and recreate critical infrastructure.Not using remote state in teams: Local state causes conflicts when multiple people run Terraform. Use S3/Azure Storage/GCS backends with state locking.
Hardcoding values instead of using variables: Hardcoding AMI IDs, instance types, and region values makes configurations inflexible. Always use variables with sensible defaults.
Ignoring
terraform destroysafety:terraform destroydeletes ALL resources defined in your configuration. Use-targetto destroy specific resources, and never run destroy on production without extreme caution.
Practice Questions
What is the purpose of
terraform init? Answer:terraform initdownloads provider plugins, initializes the backend (local or remote), and downloads modules. It’s the first command to run after cloning a Terraform repository.How does Terraform track the state of deployed resources? Answer: Terraform stores a state file (
terraform.tfstate) that maps configuration resources to real-world infrastructure IDs. Remote backends (S3, Terraform Cloud) enable team collaboration.What is the difference between
terraform planandterraform apply? Answer:planshows what changes will be made without executing them.applyexecutes the planned changes. Always review the plan before applying.How do variables make Terraform reusable across environments? Answer: Variables allow the same configuration to produce different infrastructure for dev, staging, and prod by changing variable values via
tfvarsfiles or command-line flags.
Challenge
Provision a three-tier application: create Terraform modules for VPC (networking), database (RDS PostgreSQL), and application (EC2 autoscaling group with ALB), use remote state in S3 with DynamoDB locking, create dev.tfvars and prod.tfvars with different instance sizes, and practice the full apply/destroy cycle.
FAQ
Try It Yourself
# Install Terraform
# macOS: brew install terraform
# Linux: https://developer.hashicorp.com/terraform/install
mkdir terraform-demo && cd terraform-demo
# Create a minimal config
cat > main.tf << 'EOF'
terraform {
required_providers {
local = { source = "hashicorp/local" }
}
}
resource "local_file" "demo" {
content = "Hello from Terraform!"
filename = "${path.module}/hello.txt"
}
EOF
terraform init
terraform plan
terraform apply
cat hello.txt
terraform destroyWhat’s Next
| Topic | Description |
|---|---|
| Configuration management alongside Terraform | |
| Provision cloud infrastructure with Terraform |
Related topics: Bash, AWS, Linux, Docker
What’s Next
Congratulations on completing this Terraform tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro