DEV Community

Cover image for How I Helped a Startup Automate Cloud Infrastructure in Minutes
Fife Oluwabunmi
Fife Oluwabunmi

Posted on

How I Helped a Startup Automate Cloud Infrastructure in Minutes

A while ago, I was approached by a Co-Founder to help them push out a new feature and for this feature to work as expected, they needed cloud resources to be created on the fly- easily, quickly without any additional configuration and they needed it as part of a workflow.

Now from that brief description, the obvious technology that can make this possible is Infrastructure as Code- but figuring that part out is the easy part. Ensuring that every time the Terraform scripts are run, the resources are created seamlessly was where the real work was!

I'll be walking you through how I was able to achieve this for AWS & GCP ;)

Managing AWS Cloud with Terraform

The job was "simple". Write a Terraform script to automate the creation of AWS ec2 instance(s) with all the supporting resources ensuring it's secure and accessible.

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

# Configure the AWS Provider
provider "aws" {
  region = "us-east-1"
}

# Create the VPC
resource "aws_vpc" "company_vpc" {
  cidr_block = "10.0.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true
  tags = {
    Name = "company-vpc"
  }
}

# Create the Subnet
resource "aws_subnet" "company_subnet" {
  vpc_id            = aws_vpc.company_vpc.id
  cidr_block        = "10.0.1.0/24"
  map_public_ip_on_launch = true
  availability_zone = "us-east-1a"
  tags = {
    Name = "company-subnet"
  }
}

# Create the Internet Gateway
resource "aws_internet_gateway" "company_igw" {
  vpc_id = aws_vpc.company_vpc.id
  tags = {
    Name = "company-igw"
  }
}


# Create the Route Table
resource "aws_route_table" "company_route_table" {
  vpc_id = aws_vpc.company_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.company_igw.id
  }

  tags = {
    Name = "company-route-table"
  }
}

# Associate Route Table with Subnet
resource "aws_route_table_association" "company_subnet_assoc" {
  subnet_id      = aws_subnet.company_subnet.id
  route_table_id = aws_route_table.company_route_table.id
}
Enter fullscreen mode Exit fullscreen mode

If you're not concerned with setting up a vpc for the infrastructure, then ignore the jargon above XD

Now for the interesting part, we will create the ec2 instance, Security Groups, EBS, and Key pair.

# Create Security Group
resource "aws_security_group" "company_sg" {
  vpc_id = aws_vpc.company_vpc.id
  tags = {
    Name = "company-sg"
  }
}

# Ingress Rules
resource "aws_vpc_security_group_ingress_rule" "ssh_ingress" {
  security_group_id = aws_security_group.company_sg.id
  from_port         = 22
  to_port           = 22
  ip_protocol       = "tcp"
  cidr_ipv4         = "0.0.0.0/0"
}

# If you have specifics for the in-bound rules, then specify them.
# Avoid this!
resource "aws_vpc_security_group_ingress_rule" "all_tcp_ingress" {
  security_group_id = aws_security_group.company_sg.id
  from_port         = 0
  to_port           = 65535
  ip_protocol       = "tcp"
  cidr_ipv4         = "0.0.0.0/0"
}

# Egress Rules
resource "aws_vpc_security_group_egress_rule" "all_egress" {
  security_group_id = aws_security_group.company_sg.id
  from_port         = 0
  to_port           = 0
  ip_protocol       = "-1"
  cidr_ipv4         = "0.0.0.0/0"
}

# Create EC2 Instances
resource "aws_instance" "company_instance" {
  count = var.instance_count

  # Change this to the ami & instance type you want to use
  ami           = "ami-0e2c8caa4b6378d8c"
  instance_type = "t3.medium"

  subnet_id                   = aws_subnet.company_subnet.id
  vpc_security_group_ids      = [aws_security_group.company_sg.id]
  associate_public_ip_address = true
  key_name = aws_key_pair.company-key.key_name

  # Root Volume (Default Storage)
  root_block_device {
    volume_size = 50  # Size in GB
    volume_type = "gp3"
  }

  # Additional EBS Volume
  ebs_block_device {
    device_name           = "/dev/xvdb"
    volume_size           = 100  # Size in GB
    volume_type           = "gp3"
    delete_on_termination = true
  }

  tags = {
    Name = "company-instance-${count.index + 1}"
  }
}

resource "tls_private_key" "pk" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

resource "aws_key_pair" "company-key" {
  key_name   = "company-aws-key-pair"
  public_key = tls_private_key.pk.public_key_openssh
}

resource "local_file" "company_key" {
  content         = tls_private_key.pk.private_key_pem
  filename        = "./company-aws-key-pair.pem"
  file_permission = "0400"
}
Enter fullscreen mode Exit fullscreen mode

outputs.tf

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

output "subnet_id" {
  description = "ID of the subnet"
  value       = aws_subnet.company_subnet.id
}

output "security_group_id" {
  description = "ID of the security group"
  value       = aws_security_group.company_sg.id
}

output "instance_public_ips" {
  description = "Public IPs of the brimble EC2 instances"
  value       = aws_instance.company_instance[*].public_ip
}

output "private_key_path" {
  description = "Path to the generated private key file"
  value       = local_file.company_key.filename
}

output "key_pair_name" {
  description = "Name of the AWS key pair"
  value       = aws_key_pair.company-key.key_name
}
Enter fullscreen mode Exit fullscreen mode

variables.tf

variable "instance_count" {
  description = "Number of AWS instances to launch"
  type        = number
  default     = 1
}

variable "region" {
  description = "AWS region to deploy resources"
  type        = string
  default     = "us-east-1"
}

variable "vpc_cidr_block" {
  description = "CIDR block for the VPC"
  type        = string
  default     = "10.0.0.0/16"
}

variable "subnet_cidr_block" {
  description = "CIDR block for the public subnet"
  type        = string
  default     = "10.0.1.0/24"
}

variable "availability_zone" {
  description = "Availability zone for the subnet"
  type        = string
  default     = "us-east-1a"
}

variable "ami" {
  description = "AMI ID for the EC2 instances"
  type        = string
  default     = "ami-0e2c8caa4b6378d8c"
}

variable "instance_type" {
  description = "Instance type for EC2 instances"
  type        = string
  default     = "t2.large"
}
Enter fullscreen mode Exit fullscreen mode

A few things to take away from this, we have a secure ec2 instance setup with supporting resources like vpc, security group, and subnet created. We also have the variables file to dynamically handle various resource naming and a couple of other things.

Thanks for reading ;D

Top comments (0)