Skip to content
All essays
ArchitectureJanuary 16, 202415 min

Terraform Basics: Infrastructure as Code Fundamentals

Learn Terraform fundamentals from scratch. Understand IaC concepts, HCL syntax, resource management, and core features for modern infrastructure provisioning.

Ü
Ümit Uz
Mobile & Full Stack Developer

Introduction to Terraform

Terraform is an open-source Infrastructure as Code (IaC) tool created by HashiCorp. It allows you to define and provision infrastructure resources across multiple cloud providers using a declarative configuration language.

What is Infrastructure as Code?

Infrastructure as Code is the practice of managing infrastructure in a defined, repeatable manner. Instead of manually configuring resources through console interfaces, you write code to define your infrastructure.

Benefits of IaC:

  • Version Control: Track infrastructure changes over time
  • Reusability: Share and reuse infrastructure configurations
  • Consistency: Eliminate manual configuration errors
  • Scalability: Deploy infrastructure reliably at scale

Getting Started with Terraform

Installation

bash
# macOS
brew install terraform

# Linux
wget https://releases.hashicorp.com/terraform/1.6.0/terraform_1.6.0_linux_amd64.zip
unzip terraform_1.6.0_linux_amd64.zip
sudo mv terraform /usr/local/bin/

# Windows (using Chocolatey)
choco install terraform

Verifying Installation

bash
terraform version

Terraform Workflow

The Core Workflow

Terraform follows a simple three-step workflow:

  1. 1Write: Define infrastructure in HCL configuration files
  2. 2Plan: Preview changes before applying them
  3. 3Apply: Provision the infrastructure

Basic Commands

bash
# Initialize a new Terraform project
terraform init

# Format and validate configuration
terraform fmt
terraform validate

# Preview changes
terraform plan

# Apply configuration
terraform apply

# Destroy infrastructure
terraform destroy

HCL Syntax Basics

Terraform Configuration Structure

Terraform uses HashiCorp Configuration Language (HCL), a domain-specific language designed for configuration.

hcl
# Main configuration file
terraform {
  required_version = ">= 1.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# Provider configuration
provider "aws" {
  region = "us-east-1"
}

# Resource definition
resource "aws_instance" "example" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"

  tags = {
    Name = "terraform-example"
  }
}

Configuration Blocks

Terraform Block: Defines Terraform settings

hcl
terraform {
  required_version = ">= 1.0"
  backend "s3" {
    bucket = "my-terraform-state"
    key    = "prod/terraform.tfstate"
    region = "us-east-1"
  }
}

Provider Block: Configures providers

hcl
provider "aws" {
  region  = "us-east-1"
  profile = "default"
}

Resource Block: Defines infrastructure resources

hcl
resource "aws_s3_bucket" "example" {
  bucket = "my-unique-bucket-name"

  tags = {
    Name        = "My Bucket"
    Environment = "Dev"
  }
}

Core Concepts

Providers

Providers are plugins that Terraform uses to manage resources. They abstract away the API differences between cloud providers.

hcl
provider "aws" {
  region = "us-east-1"
}

provider "azurerm" {
  features {}
}

provider "google" {
  project = "my-project-id"
  region  = "us-central1"
}

Resources

Resources are the most important element in the Terraform language. Each resource block describes one or more infrastructure objects.

hcl
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name = "main-vpc"
  }
}

resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.1.0/24"
  availability_zone       = "us-east-1a"
  map_public_ip_on_launch = true

  tags = {
    Name = "public-subnet"
  }
}

Variables

Variables make your configuration more dynamic and reusable.

hcl
# variables.tf
variable "region" {
  description = "AWS region for resources"
  type        = string
  default     = "us-east-1"
}

variable "instance_type" {
  description = "EC2 instance type"
  type        = string
  default     = "t2.micro"
}

variable "enable_monitoring" {
  description = "Enable detailed monitoring"
  type        = bool
  default     = false
}

Outputs

Outputs return values to the CLI and can be used by other Terraform configurations.

hcl
output "instance_public_ip" {
  description = "Public IP of the EC2 instance"
  value       = aws_instance.example.public_ip
}

output "vpc_id" {
  description = "ID of the VPC"
  value       = aws_vpc.main.id
}

Local Values

Locals are named values that you can reference throughout your configuration.

hcl
locals {
  common_tags = {
    Project     = "my-app"
    Environment = "production"
    ManagedBy   = "terraform"
  }

  instance_names = ["web-1", "web-2", "web-3"]
}

resource "aws_instance" "web" {
  count         = 3
  ami           = var.ami_id
  instance_type = var.instance_type
  tags          = local.common_tags
}

Data Sources

Data sources allow you to fetch information about existing resources.

hcl
data "aws_ami" "ubuntu" {
  most_recent = true

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  owners = ["099720109477"] # Canonical
}

resource "aws_instance" "web" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t2.micro"
}

State Management

Understanding State

Terraform state is the mechanism by which it tracks the resources it manages. The state file contains a mapping between your configuration and real-world resources.

State File

json
{
  "version": 4,
  "terraform_version": "1.6.0",
  "serial": 1,
  "modules": [
    {
      "path": [
        "root"
      ],
      "outputs": {},
      "resources": [
        {
          "name": "example",
          "provider": "provider["registry.terraform.io/hashicorp/aws"]",
          "type": "aws_instance",
          "instances": [
            {
              "attributes": {
                "ami": "ami-0c55b159cbfafe1f0",
                "instance_type": "t2.micro"
              }
            }
          ]
        }
      ]
    }
  ]
}

Remote State

For team collaboration, store state remotely:

hcl
terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "prod/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-state-lock"
  }
}

Dependency Management

Implicit Dependencies

Terraform automatically infers dependencies when one resource references another.

hcl
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

resource "aws_subnet" "public" {
  vpc_id     = aws_vpc.main.id  # Implicit dependency
  cidr_block = "10.0.1.0/24"
}

Explicit Dependencies

Use depends_on for dependencies not visible to Terraform.

hcl
resource "aws_db_instance" "default" {
  allocated_storage    = 20
  engine               = "mysql"
  engine_version       = "5.7"
  instance_class       = "db.t2.micro"
  db_name              = "mydb"
  username             = "admin"
  password             = var.db_password

  depends_on = [aws_security_group.db]
}

Provisioners

Provisioners execute scripts on the local machine or remote resource.

hcl
resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = "t2.micro"

  # File provisioner
  provisioner "file" {
    source      = "app.conf"
    destination = "/etc/app.conf"
  }

  # Remote-exec provisioner
  provisioner "remote-exec" {
    inline = [
      "sudo apt-get update",
      "sudo apt-get install -y nginx",
      "sudo systemctl start nginx"
    ]
  }

  connection {
    type        = "ssh"
    user        = "ubuntu"
    private_key = file("~/.ssh/id_rsa")
    host        = self.public_ip
  }
}

Best Practices

1. Directory Structure

project/
├── main.tf
├── variables.tf
├── outputs.tf
├── terraform.tfvars
└── modules/
    ├── vpc/
    ├── ec2/
    └── rds/

2. Use Variables for Customization

hcl
# Good
resource "aws_instance" "web" {
  instance_type = var.instance_type
  ami           = var.ami_id
}

# Avoid hardcoding
resource "aws_instance" "web" {
  instance_type = "t2.micro"
  ami           = "ami-12345678"
}

3. Tag Your Resources

hcl
locals {
  tags = {
    Environment = var.environment
    Project     = var.project_name
    ManagedBy   = "Terraform"
  }
}

resource "aws_instance" "web" {
  tags = local.tags
}

4. Use Terraform Format

bash
terraform fmt -recursive

5. Validate Before Applying

bash
terraform validate
terraform plan

Common Patterns

Multiple Environments

hcl
# terraform.tfvars
environment = "production"

# main.tf
locals {
  env_config = {
    production = {
      instance_count = 3
      instance_type  = "t2.large"
    }
    staging = {
      instance_count = 1
      instance_type  = "t2.micro"
    }
  }
  config = local.env_config[var.environment]
}

resource "aws_instance" "web" {
  count         = local.config.instance_count
  instance_type = local.config.instance_type
}

Count and For Each

hcl
# Count
resource "aws_instance" "web" {
  count         = 3
  ami           = var.ami_id
  instance_type = var.instance_type

  tags = {
    Name = "web-${count.index}"
  }
}

# For each
resource "aws_instance" "web" {
  for_each = toset(["web-1", "web-2", "web-3"])
  ami      = var.ami_id

  tags = {
    Name = each.value
  }
}

Troubleshooting

Common Issues

1. State Lock Issues

bash
terraform force-unlock <LOCK_ID>

2. Resource Already Exists

bash
terraform import aws_instance.web i-1234567890abcdef0

3. Drift Detection

bash
terraform plan -refresh-only

Next Steps

Now that you understand Terraform basics, explore:

  • Modules for reusability
  • State management strategies
  • CI/CD integration
  • Multi-environment deployments
  • Advanced resource configurations

Terraform basics provide the foundation for managing modern infrastructure as code. Master these concepts before moving to advanced topics like modules, state management, and CI/CD integration.

Enjoyed this essay? Share it.

Related essays

Next essay
Database Read Replicas: Scaling Read Workloads