Terraform Basics
Terraform Basics Core Concepts Infrastructure as Code — declare infrastructure in .tf files, version control it, review it like code Provider — plugin to intera…
Terraform Basics
Core Concepts
Infrastructure as Code — declare infrastructure in .tf files, version control it, review it like code
Provider — plugin to interact with a platform (AWS, GCP, Azure, Kubernetes, GitHub)
Resource — infrastructure object to create/manage (EC2 instance, S3 bucket, DNS record)
State — terraform.tfstate tracks what Terraform manages; store remotely (S3 + DynamoDB lock)
Module — reusable, parameterized group of resources
Data source — read-only query of existing resources not managed by this config
HCL Syntax
# main.tf
# Provider configuration
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
provider "aws" {
region = var.aws_region
}
# Variables
variable "aws_region" {
type = string
description = "AWS region to deploy to"
default = "us-east-1"
}
variable "instance_type" {
type = string
default = "t3.micro"
validation {
condition = contains(["t3.micro", "t3.small", "t3.medium"], var.instance_type)
error_message = "Must be a t3 instance type."
}
}
variable "tags" {
type = map(string)
default = { Environment = "prod", Team = "platform" }
}
# Locals — computed values
locals {
name_prefix = "${var.project}"
common_tags = merge(var.tags, { ManagedBy = "terraform" })
}
# Data source — query existing resources
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-*-x86_64"]
}
}
# Resource
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = var.instance_type
subnet_id = aws_subnet.public.id
tags = merge(local.common_tags, { Name = "${local.name_prefix}-web" })
}
# Output — expose values after apply
output "instance_ip" {
value = aws_instance.web.public_ip
description = "Public IP of web instance"
}
output "instance_id" {
value = aws_instance.web.id
sensitive = false
}CLI Commands
# Workflow
terraform init # download providers, init backend
terraform validate # syntax + config validation
terraform fmt # format .tf files
terraform plan # preview changes (dry-run)
terraform plan -out=tfplan # save plan to file
terraform apply # apply changes (prompts for confirmation)
terraform apply tfplan # apply saved plan (no prompt)
terraform apply -auto-approve # skip confirmation (CI/CD only)
terraform destroy # destroy all managed resources
# Targeting
terraform plan -target=aws_instance.web
terraform apply -target=module.vpc
# State management
terraform state list # list managed resources
terraform state show aws_instance.web # inspect resource state
terraform state rm aws_instance.web # remove from state (not destroy)
terraform import aws_instance.web i-1234567 # import existing resource
# Workspace (lightweight environments)
terraform workspace list
terraform workspace new staging
terraform workspace select staging
# Variables from CLI
terraform apply -var="instance_type=t3.small"
terraform apply -var-file="prod.tfvars"Modules & Real Example
# Using a public module
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.4.0"
name = "${local.name_prefix}-vpc"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b", "us-east-1c"]
public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
private_subnets = ["10.0.11.0/24", "10.0.12.0/24", "10.0.13.0/24"]
enable_nat_gateway = true
tags = local.common_tags
}
# Custom module — modules/rds/main.tf
resource "aws_db_instance" "main" {
identifier = var.identifier
engine = "postgres"
engine_version = "16.1"
instance_class = var.instance_class
allocated_storage = var.storage_gb
db_name = var.db_name
username = var.username
password = var.password # use aws_secretsmanager_secret instead!
db_subnet_group_name = aws_db_subnet_group.main.name
vpc_security_group_ids = [aws_security_group.rds.id]
skip_final_snapshot = false
backup_retention_period = 7
tags = var.tags
}