Hands-on Lab

#Lab: Terraform Modules

Create reusable, maintainable infrastructure with Terraform modules.

#๐ŸŽฏ Objectives

  • Create custom Terraform modules
  • Use module inputs and outputs
  • Publish and consume modules
  • Implement module best practices

#๐Ÿ“‹ Prerequisites

  • Terraform installed
  • AWS credentials configured

#โฑ๏ธ Duration: 45 minutes


#Task 1: Project Structure (5 min)

bash
1mkdir -p ~/terraform-modules-lab
2cd ~/terraform-modules-lab
3
4# Create structure
5mkdir -p modules/vpc modules/ec2 environments/{dev,prod}

#Task 2: Create VPC Module (15 min)

#modules/vpc/main.tf

hcl
1variable "vpc_cidr" {
2  description = "CIDR block for VPC"
3  type        = string
4  default     = "10.0.0.0/16"
5}
6
7variable "environment" {
8  description = "Environment name"
9  type        = string
10}
11
12variable "public_subnets" {
13  description = "List of public subnet CIDRs"
14  type        = list(string)
15  default     = ["10.0.1.0/24", "10.0.2.0/24"]
16}
17
18data "aws_availability_zones" "available" {
19  state = "available"
20}
21
22resource "aws_vpc" "main" {
23  cidr_block           = var.vpc_cidr
24  enable_dns_hostnames = true
25  enable_dns_support   = true
26
27  tags = {
28    Name        = "${var.environment}-vpc"
29    Environment = var.environment
30  }
31}
32
33resource "aws_internet_gateway" "main" {
34  vpc_id = aws_vpc.main.id
35
36  tags = {
37    Name = "${var.environment}-igw"
38  }
39}
40
41resource "aws_subnet" "public" {
42  count                   = length(var.public_subnets)
43  vpc_id                  = aws_vpc.main.id
44  cidr_block              = var.public_subnets[count.index]
45  availability_zone       = data.aws_availability_zones.available.names[count.index]
46  map_public_ip_on_launch = true
47
48  tags = {
49    Name = "${var.environment}-public-${count.index + 1}"
50  }
51}
52
53resource "aws_route_table" "public" {
54  vpc_id = aws_vpc.main.id
55
56  route {
57    cidr_block = "0.0.0.0/0"
58    gateway_id = aws_internet_gateway.main.id
59  }
60
61  tags = {
62    Name = "${var.environment}-public-rt"
63  }
64}
65
66resource "aws_route_table_association" "public" {
67  count          = length(aws_subnet.public)
68  subnet_id      = aws_subnet.public[count.index].id
69  route_table_id = aws_route_table.public.id
70}

#modules/vpc/outputs.tf

hcl
1output "vpc_id" {
2  description = "VPC ID"
3  value       = aws_vpc.main.id
4}
5
6output "public_subnet_ids" {
7  description = "Public subnet IDs"
8  value       = aws_subnet.public[*].id
9}
10
11output "vpc_cidr" {
12  description = "VPC CIDR block"
13  value       = aws_vpc.main.cidr_block
14}

#Task 3: Create EC2 Module (10 min)

#modules/ec2/main.tf

hcl
1variable "instance_type" {
2  type    = string
3  default = "t3.micro"
4}
5
6variable "subnet_id" {
7  type = string
8}
9
10variable "vpc_id" {
11  type = string
12}
13
14variable "environment" {
15  type = string
16}
17
18data "aws_ami" "amazon_linux" {
19  most_recent = true
20  owners      = ["amazon"]
21
22  filter {
23    name   = "name"
24    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
25  }
26}
27
28resource "aws_security_group" "web" {
29  name   = "${var.environment}-web-sg"
30  vpc_id = var.vpc_id
31
32  ingress {
33    from_port   = 80
34    to_port     = 80
35    protocol    = "tcp"
36    cidr_blocks = ["0.0.0.0/0"]
37  }
38
39  ingress {
40    from_port   = 22
41    to_port     = 22
42    protocol    = "tcp"
43    cidr_blocks = ["0.0.0.0/0"]
44  }
45
46  egress {
47    from_port   = 0
48    to_port     = 0
49    protocol    = "-1"
50    cidr_blocks = ["0.0.0.0/0"]
51  }
52}
53
54resource "aws_instance" "web" {
55  ami           = data.aws_ami.amazon_linux.id
56  instance_type = var.instance_type
57  subnet_id     = var.subnet_id
58
59  vpc_security_group_ids = [aws_security_group.web.id]
60
61  tags = {
62    Name        = "${var.environment}-web"
63    Environment = var.environment
64  }
65}
66
67output "instance_id" {
68  value = aws_instance.web.id
69}
70
71output "public_ip" {
72  value = aws_instance.web.public_ip
73}

#Task 4: Use Modules (10 min)

#environments/dev/main.tf

hcl
1terraform {
2  required_providers {
3    aws = {
4      source  = "hashicorp/aws"
5      version = "~> 5.0"
6    }
7  }
8}
9
10provider "aws" {
11  region = "us-east-1"
12}
13
14module "vpc" {
15  source = "../../modules/vpc"
16
17  environment    = "dev"
18  vpc_cidr       = "10.0.0.0/16"
19  public_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
20}
21
22module "web_server" {
23  source = "../../modules/ec2"
24
25  environment   = "dev"
26  vpc_id        = module.vpc.vpc_id
27  subnet_id     = module.vpc.public_subnet_ids[0]
28  instance_type = "t3.micro"
29}
30
31output "vpc_id" {
32  value = module.vpc.vpc_id
33}
34
35output "web_server_ip" {
36  value = module.web_server.public_ip
37}

#Task 5: Apply Configuration (5 min)

bash
1cd environments/dev
2
3# Initialize
4terraform init
5
6# Plan
7terraform plan
8
9# Apply (if you have AWS credentials)
10# terraform apply
11
12# Destroy when done
13# terraform destroy

#โœ… Success Criteria

  • VPC module created with inputs/outputs
  • EC2 module created
  • Modules consumed in environment
  • terraform plan shows correct resources

#๐Ÿงน Cleanup

bash
cd environments/dev
terraform destroy -auto-approve 2>/dev/null
cd ~ && rm -rf terraform-modules-lab