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
}
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"
}
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
}
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"
}
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)