Skip to content

Instantly share code, notes, and snippets.

@jaycdave88
Last active February 6, 2025 16:32
Show Gist options
  • Save jaycdave88/4809f7eb30da38970444ae651dbf8cda to your computer and use it in GitHub Desktop.
Save jaycdave88/4809f7eb30da38970444ae651dbf8cda to your computer and use it in GitHub Desktop.
(work in progress) Sets up AWS Terraform distributed app
#!/bin/bash
# WARNING: Hardcoding credentials is not recommended!
export AWS_ACCESS_KEY_ID="YOUR_AWS_ACCESS_KEY_ID"
export AWS_SECRET_ACCESS_KEY="YOUR_AWS_SECRET_ACCESS_KEY"
export AWS_DEFAULT_REGION="us-east-1"
# This script creates the folder structure and Terraform files for the AWS demo environment.
# Run this script from your terminal: ./setup-demo.sh
set -e
# Base directory for the Terraform code
BASE_DIR="aws-demo-env"
echo "Creating directory structure in '${BASE_DIR}'..."
mkdir -p "${BASE_DIR}"
echo "Creating provider.tf..."
cat > "${BASE_DIR}/provider.tf" << 'EOF'
provider "aws" {
region = var.aws_region
}
EOF
echo "Creating variables.tf..."
cat > "${BASE_DIR}/variables.tf" << 'EOF'
variable "aws_region" {
description = "AWS region"
default = "us-east-1"
}
variable "key_name" {
description = "Name of the EC2 Key Pair to use for instances"
}
EOF
echo "Creating vpc.tf..."
cat > "${BASE_DIR}/vpc.tf" << 'EOF'
# Create the VPC
resource "aws_vpc" "demo_vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "demo-vpc"
}
}
# Create an Internet Gateway for the VPC
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.demo_vpc.id
tags = {
Name = "demo-igw"
}
}
# Public subnet (for DMZ/network appliances)
resource "aws_subnet" "public_subnet" {
vpc_id = aws_vpc.demo_vpc.id
cidr_block = "10.0.1.0/24"
map_public_ip_on_launch = true
availability_zone = "${var.aws_region}a"
tags = {
Name = "demo-public-subnet"
}
}
# Private subnet (for VMs and observability)
resource "aws_subnet" "private_subnet" {
vpc_id = aws_vpc.demo_vpc.id
cidr_block = "10.0.2.0/24"
map_public_ip_on_launch = false
availability_zone = "${var.aws_region}a"
tags = {
Name = "demo-private-subnet"
}
}
# Separate private subnet for RDS (can be part of a DB subnet group)
resource "aws_subnet" "rds_subnet" {
vpc_id = aws_vpc.demo_vpc.id
cidr_block = "10.0.3.0/24"
map_public_ip_on_launch = false
availability_zone = "${var.aws_region}a"
tags = {
Name = "demo-rds-subnet"
}
}
# Create a NAT Gateway in the public subnet so private instances can access the Internet
resource "aws_eip" "nat_eip" {
domain = "vpc"
}
resource "aws_nat_gateway" "nat" {
allocation_id = aws_eip.nat_eip.id
subnet_id = aws_subnet.public_subnet.id
tags = {
Name = "demo-nat"
}
}
# Public route table (for public subnet)
resource "aws_route_table" "public_rt" {
vpc_id = aws_vpc.demo_vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
tags = {
Name = "demo-public-rt"
}
}
resource "aws_route_table_association" "public_assoc" {
subnet_id = aws_subnet.public_subnet.id
route_table_id = aws_route_table.public_rt.id
}
# Private route table (for VMs and observability)
resource "aws_route_table" "private_rt" {
vpc_id = aws_vpc.demo_vpc.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.nat.id
}
tags = {
Name = "demo-private-rt"
}
}
resource "aws_route_table_association" "private_assoc" {
subnet_id = aws_subnet.private_subnet.id
route_table_id = aws_route_table.private_rt.id
}
# Route table for the RDS subnet
resource "aws_route_table" "rds_rt" {
vpc_id = aws_vpc.demo_vpc.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.nat.id
}
tags = {
Name = "demo-rds-rt"
}
}
resource "aws_route_table_association" "rds_assoc" {
subnet_id = aws_subnet.rds_subnet.id
route_table_id = aws_route_table.rds_rt.id
}
EOF
echo "Creating security_groups.tf..."
cat > "${BASE_DIR}/security_groups.tf" << 'EOF'
# Security Group for public instances (DMZ network appliances)
resource "aws_security_group" "public_sg" {
name = "demo-public-sg"
description = "Allow SSH and all traffic for DMZ"
vpc_id = aws_vpc.demo_vpc.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "demo-public-sg"
}
}
# Security Group for private instances (VMs and observability)
resource "aws_security_group" "private_sg" {
name = "demo-private-sg"
description = "Allow SSH and application ports from within VPC"
vpc_id = aws_vpc.demo_vpc.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [aws_vpc.demo_vpc.cidr_block]
}
# Allow app ports (8080, 3000, 6379) and observability (9090, 3000)
ingress {
from_port = 8080
to_port = 8080
protocol = "tcp"
cidr_blocks = [aws_vpc.demo_vpc.cidr_block]
}
ingress {
from_port = 3000
to_port = 3000
protocol = "tcp"
cidr_blocks = [aws_vpc.demo_vpc.cidr_block]
}
ingress {
from_port = 6379
to_port = 6379
protocol = "tcp"
cidr_blocks = [aws_vpc.demo_vpc.cidr_block]
}
ingress {
from_port = 9090
to_port = 9090
protocol = "tcp"
cidr_blocks = [aws_vpc.demo_vpc.cidr_block]
}
ingress {
from_port = 3000
to_port = 3000
protocol = "tcp"
cidr_blocks = [aws_vpc.demo_vpc.cidr_block]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "demo-private-sg"
}
}
# Security Group for RDS
resource "aws_security_group" "rds_sg" {
name = "demo-rds-sg"
description = "Allow PostgreSQL access from within the VPC"
vpc_id = aws_vpc.demo_vpc.id
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = [aws_vpc.demo_vpc.cidr_block]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "demo-rds-sg"
}
}
EOF
echo "Creating data_sources.tf..."
cat > "${BASE_DIR}/data_sources.tf" << 'EOF'
# Data source for Ubuntu 22.04 (VM1)
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}
# Data source for Debian 12 (VM2)
data "aws_ami" "debian" {
most_recent = true
owners = ["136693071363"]
filter {
name = "name"
values = ["debian-12-*"]
}
}
# Data source for Rocky Linux 9 (VM3)
data "aws_ami" "rocky" {
most_recent = true
owners = ["689593380443"] # Adjust this owner ID if necessary.
filter {
name = "name"
values = ["Rocky-9*"]
}
}
EOF
echo "Creating instances.tf..."
cat > "${BASE_DIR}/instances.tf" << 'EOF'
# EC2 Instance for VM1 - Ubuntu 22.04 running APP1 (Web App on port 8080)
resource "aws_instance" "vm1" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
subnet_id = aws_subnet.private_subnet.id
security_groups = [aws_security_group.private_sg.id]
associate_public_ip_address = false
key_name = var.key_name
user_data = <<EOF_USERDATA
#!/bin/bash
apt-get update -y
apt-get install -y docker.io
systemctl start docker
docker run -d -p 8080:80 nginx
EOF_USERDATA
tags = {
Name = "VM1-Ubuntu-APP1"
}
}
# EC2 Instance for VM2 - Debian 12 running APP2 (API Service on port 3000)
resource "aws_instance" "vm2" {
ami = data.aws_ami.debian.id
instance_type = "t3.micro"
subnet_id = aws_subnet.private_subnet.id
security_groups = [aws_security_group.private_sg.id]
associate_public_ip_address = false
key_name = var.key_name
user_data = <<EOF_USERDATA
#!/bin/bash
apt-get update -y
apt-get install -y docker.io
systemctl start docker
# Run a simple Node.js API using the node:14-slim image
docker run -d -p 3000:3000 node:14-slim node -e "require('http').createServer((req,res)=>{res.end('Hello from API Service (APP2)!');}).listen(3000)"
EOF_USERDATA
tags = {
Name = "VM2-Debian-APP2"
}
}
# EC2 Instance for VM3 - Rocky Linux 9 running APP3 (Cache Service on port 6379)
resource "aws_instance" "vm3" {
ami = data.aws_ami.rocky.id
instance_type = "t3.micro"
subnet_id = aws_subnet.private_subnet.id
security_groups = [aws_security_group.private_sg.id]
associate_public_ip_address = false
key_name = var.key_name
user_data = <<EOF_USERDATA
#!/bin/bash
yum update -y
yum install -y docker
systemctl start docker
docker run -d -p 6379:6379 redis
EOF_USERDATA
tags = {
Name = "VM3-Rocky-APP3"
}
}
# EC2 Instance for Network Appliance: pfSense (simulated) in the public subnet
resource "aws_instance" "pfsense" {
ami = data.aws_ami.ubuntu.id # Using Ubuntu as a dummy base image
instance_type = "t3.micro"
subnet_id = aws_subnet.public_subnet.id
security_groups = [aws_security_group.public_sg.id]
associate_public_ip_address = true
key_name = var.key_name
user_data = <<EOF_USERDATA
#!/bin/bash
echo "Simulating pfSense Firewall" > /var/log/pfsense.log
while true; do sleep 3600; done
EOF_USERDATA
tags = {
Name = "pfSense-Simulated"
}
}
# EC2 Instance for Network Appliance: Open vSwitch (simulated) in the public subnet
resource "aws_instance" "ovs" {
ami = data.aws_ami.ubuntu.id # Using Ubuntu as a dummy base image
instance_type = "t3.micro"
subnet_id = aws_subnet.public_subnet.id
security_groups = [aws_security_group.public_sg.id]
associate_public_ip_address = true
key_name = var.key_name
user_data = <<EOF_USERDATA
#!/bin/bash
echo "Simulating Open vSwitch" > /var/log/ovs.log
while true; do sleep 3600; done
EOF_USERDATA
tags = {
Name = "OVS-Simulated"
}
}
# EC2 Instance for Observability Tools (Prometheus and Grafana) in the private subnet
resource "aws_instance" "observability" {
ami = data.aws_ami.ubuntu.id # Using Ubuntu as a base
instance_type = "t3.micro"
subnet_id = aws_subnet.private_subnet.id
security_groups = [aws_security_group.private_sg.id]
associate_public_ip_address = false
key_name = var.key_name
user_data = <<EOF_USERDATA
#!/bin/bash
apt-get update -y
apt-get install -y docker.io docker-compose git
systemctl start docker
mkdir -p /home/ubuntu/observability
cat << 'EOC' > /home/ubuntu/observability/docker-compose.yml
version: '3'
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
EOC
cd /home/ubuntu/observability
docker-compose up -d
EOF_USERDATA
tags = {
Name = "Observability-Stack"
}
}
EOF
echo "Creating rds.tf..."
cat > "${BASE_DIR}/rds.tf" << 'EOF'
# Create an RDS subnet group (using the private and RDS subnets)
resource "aws_db_subnet_group" "rds_subnet_group" {
name = "demo-rds-subnet-group"
subnet_ids = [aws_subnet.private_subnet.id, aws_subnet.rds_subnet.id]
tags = {
Name = "demo-rds-subnet-group"
}
}
# Create an RDS PostgreSQL Instance
resource "aws_db_instance" "postgres" {
allocated_storage = 20
storage_type = "gp2"
engine = "postgres"
engine_version = "13.4"
instance_class = "db.t3.micro"
db_name = "demo_db"
username = "demo"
password = "demo1234" # Use a more secure password in production
db_subnet_group_name = aws_db_subnet_group.rds_subnet_group.name
vpc_security_group_ids = [aws_security_group.rds_sg.id]
skip_final_snapshot = true
publicly_accessible = false
multi_az = false
tags = {
Name = "Demo-Postgres"
}
}
EOF
echo "Creating outputs.tf..."
cat > "${BASE_DIR}/outputs.tf" << 'EOF'
output "vpc_id" {
value = aws_vpc.demo_vpc.id
}
output "public_subnet_id" {
value = aws_subnet.public_subnet.id
}
output "private_subnet_id" {
value = aws_subnet.private_subnet.id
}
output "rds_endpoint" {
value = aws_db_instance.postgres.endpoint
}
output "pfSense_public_ip" {
value = aws_instance.pfsense.public_ip
}
output "OVS_public_ip" {
value = aws_instance.ovs.public_ip
}
EOF
echo "Folder structure and Terraform files created in '${BASE_DIR}'."
echo "To continue, run the following commands:"
echo " cd ${BASE_DIR}"
echo " terraform init"
echo " terraform plan"
echo " terraform apply"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment