Galle, LK--:--5/6 sites live

Infrastructure as Code (IaC) with Terraform on AWS

Clicking in the AWS Console is prone to human error and cannot be reviewed or version controlled. Terraform allows you to define your entire AWS infrastructure declaratively in code, store it in Git, and…

16 Jun 20263 min readSelf Guide · Stage 5#AWS#IAM#W3DevOps

Lesson 16 of 26 · Module: AWS

22 min read · Source notes — Terraform Architecture & Commands

Clicking in the AWS Console is prone to human error and cannot be reviewed or version controlled. Terraform allows you to define your entire AWS infrastructure declaratively in code, store it in Git, and deploy it reproducibly.

Screenshot: Terraform Architecture & Commands

Instructor explaining terraform core, state file, and provider plugins communicating with AWS APIs.

1. The Core Terraform Workflow

CommandWhat It Does Under the Hood
terraform initInitializes working directory, downloads required provider plugins (e.g., hashicorp/aws), configures backend.
terraform planCompares desired code state with actual cloud state (read from terraform.tfstate) and outputs exact diff.
terraform applyExecutes the planned changes against the AWS API to create, modify, or delete resources.
terraform destroyTears down and deletes all resources tracked in the state file to avoid runaway cloud costs.

Screenshot: Provisioning EC2 with User Data in Terraform

Writing HCL resource definitions for aws_instance with dynamic user_data scripts.

2. Complete Production Terraform Script (main.tf)

Declarative AWS Architecture: EC2 + Security Group + S3 Bucket

ExampleTerraform
terraform {
  required_version = ">= 1.5.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

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

# 1. Security Group for Web Server
resource "aws_security_group" "web_sg" {
  name        = "web-server-sg"
  description = "Allow inbound HTTP and SSH traffic"

  ingress {
    description = "SSH from anywhere (restrict in prod!)"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "HTTP web traffic"
    from_port   = 80
    to_port     = 80
    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"]
  }
}

# 2. EC2 Instance Provisioning
resource "aws_instance" "app_server" {
  ami           = "ami-0c7217cdde317cfec" # Ubuntu 22.04 LTS us-east-1
  instance_type = "t3.micro"
  vpc_security_group_ids = [aws_security_group.web_sg.id]

  user_data = <<-EOF
              #!/bin/bash
              apt update -y
              apt install -y nginx
              echo "Provisioned by HashiCorp Terraform!" > /var/www/html/index.html
              systemctl start nginx
              EOF

  tags = {
    Name        = "Production-WebServer"
    Environment = "Prod"
    ManagedBy   = "Terraform"
  }
}

# 3. S3 Bucket for Static Assets
resource "aws_s3_bucket" "assets_bucket" {
  bucket = "company-app-assets-prod-2026"
  
  tags = {
    Environment = "Prod"
  }
}

output "instance_public_ip" {
  description = "Public IP address of the deployed EC2 instance"
  value       = aws_instance.app_server.public_ip
}

Keep going: ← Lesson 15 · Lesson 17 → · Module overview · Track it in the Self Guide