Skip to content

Commit

Permalink
feat: add s3 and rds infrastructure
Browse files Browse the repository at this point in the history
Default encryption is enabled on S3 bucket. The RDS instance is a
postgres14 instance on `db.t3.micro` instance with a random generated
password. Public access to both RDS and S3 are blocked, with the RDS
instance being deployed on the private subnet.
  • Loading branch information
sydrawat01 committed Jan 11, 2024
1 parent 439ca22 commit f4732c5
Show file tree
Hide file tree
Showing 18 changed files with 267 additions and 18 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Infrastructure as Code

[![Continuous Integration](https://github.com/VoskhodXIV/infrastructure/actions/workflows/continuous-integration.yml/badge.svg)](https://github.com/VoskhodXIV/infrastructure/actions/workflows/continuous-integration.yml)

Modular infrastructure setup with Hashicorp Terraform on AWS Cloud.

> \[!NOTE]\
Expand Down
75 changes: 59 additions & 16 deletions modules/ec2/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,66 @@ data "aws_ami" "ubuntu" {
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance
resource "aws_instance" "ec2" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
associate_public_ip_address = true
vpc_security_group_ids = [var.api_sg_id]
subnet_id = var.public_subnets_id[0]
disable_api_termination = false
key_name = var.ssh_key_name

ebs_block_device {
delete_on_termination = true
device_name = var.device_name
volume_size = var.volume_size
volume_type = var.volume_type
# resource "aws_instance" "ec2" {
# ami = data.aws_ami.ubuntu.id
# instance_type = var.instance_type
# associate_public_ip_address = true
# vpc_security_group_ids = [var.api_sg_id]
# subnet_id = var.public_subnets_id[0]
# disable_api_termination = false
# key_name = var.ssh_key_name

# ebs_block_device {
# delete_on_termination = true
# device_name = var.device_name
# volume_size = var.volume_size
# volume_type = var.volume_type
# }

# tags = {
# Name = "${var.environment}-api-server"
# }
# }

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_template
resource "aws_launch_template" "ec2_launch_template" {
name = "${var.environment}-launch-template"
image_id = data.aws_ami.ubuntu.id
instance_initiated_shutdown_behavior = "terminate"
instance_type = var.instance_type
disable_api_termination = false
key_name = var.ssh_key_name
# vpc_security_group_ids = [var.api_sg_id]

# TODO: IAM Instance profile for s3 access policy

block_device_mappings {
device_name = var.device_name
ebs {
delete_on_termination = true
volume_size = var.volume_size
volume_type = var.volume_type
}
}

tags = {
Name = "${var.environment}-api-server"
network_interfaces {
associate_public_ip_address = true
delete_on_termination = true
security_groups = [var.api_sg_id]
}

user_data = base64encode("${templatefile("../modules/ec2/userdata.sh", {
ENVIRONMENT = "${var.environment}"
DATABASE = "${var.database}",
DBUSER = "${var.dbuser}",
DBPASSWORD = "${var.dbpassword}",
})}")

tag_specifications {
resource_type = "instance"

tags = {
"Name" = "${var.environment}-launch-template"
}
}
}
16 changes: 16 additions & 0 deletions modules/ec2/userdata.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash

cd /home/ubuntu/webapp || exit
touch .env

{
echo "ENVIRONMENT=$ENVIRONMENT"
echo "PORT=1337"
echo "DATABASE=$DATABASE"
echo "HOSTNAME=localhost"
echo "DBUSER=$DBUSER"
echo "DBPASSWORD=$DBPASSWORD"
} >>.env

sudo systemctl enable nodeserver.service
sudo systemctl start nodeserver.service
3 changes: 3 additions & 0 deletions modules/ec2/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ variable "instance_type" {}
variable "device_name" {}
variable "volume_size" {}
variable "volume_type" {}
variable "database" {}
variable "dbuser" {}
variable "dbpassword" {}
42 changes: 42 additions & 0 deletions modules/rds/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password
resource "random_password" "dbpassword" {
length = 16
special = false
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_subnet_group
resource "aws_db_subnet_group" "db_subnet_group" {
name = "db-subnet-group"
subnet_ids = [var.private_subnets_id[0], var.private_subnets_id[1]]

tags = {
"Name" = "${var.environment}-db-subnet-group"
}
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_parameter_group
# https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.html#Appendix.PostgreSQL.CommonDBATasks.Parameters
resource "aws_db_parameter_group" "db_parameter_group" {
name = "rds-psql-pg"
family = "postgres14"
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance
resource "aws_db_instance" "database" {
allocated_storage = 10
db_name = var.database
# https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_CreateDBInstance.html#API_CreateDBInstance_RequestParameters
engine = "postgres"
engine_version = "14"
instance_class = "db.t3.micro"
username = var.dbuser
password = random_password.dbpassword.result
parameter_group_name = aws_db_parameter_group.db_parameter_group.name
skip_final_snapshot = true
multi_az = false
vpc_security_group_ids = [var.db_sg_id]
db_subnet_group_name = aws_db_subnet_group.db_subnet_group.name
# TODO: Encrypt storage with KMS
storage_encrypted = false

}
3 changes: 3 additions & 0 deletions modules/rds/output.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
output "dbpassword" {
value = random_password.dbpassword.result
}
5 changes: 5 additions & 0 deletions modules/rds/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
variable "environment" {}
variable "database" {}
variable "dbuser" {}
variable "private_subnets_id" {}
variable "db_sg_id" {}
48 changes: 48 additions & 0 deletions modules/s3/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/uuid
resource "random_uuid" "uuid" {}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket
resource "aws_s3_bucket" "bucket" {
bucket = random_uuid.uuid.result
force_destroy = true

tags = {
"Name" = "${var.environment}-s3-bucket"
}
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration
resource "aws_s3_bucket_lifecycle_configuration" "s3_bucket_lifecycle_config" {
bucket = aws_s3_bucket.bucket.id

rule {
id = "rule-1"
status = "Enabled"
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration#transition
transition {
days = 30
storage_class = "STANDARD_IA"
}
}
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration
resource "aws_s3_bucket_server_side_encryption_configuration" "s3_bucket_encryption" {
bucket = aws_s3_bucket.bucket.id

rule {
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration#apply_server_side_encryption_by_default
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block
resource "aws_s3_bucket_public_access_block" "s3_bucket_public_access" {
bucket = aws_s3_bucket.bucket.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
Empty file added modules/s3/output.tf
Empty file.
1 change: 1 addition & 0 deletions modules/s3/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
variable "environment" {}
27 changes: 25 additions & 2 deletions modules/sg/main.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group
resource "aws_security_group" "api_sg" {
name = "api security group"
name = "api-sg"
description = "Enable SSH,API,HTTP & HTTPS access on ports 22,1337,80 & 443"
vpc_id = var.vpc_id

Expand Down Expand Up @@ -45,4 +45,27 @@ resource "aws_security_group" "api_sg" {
}
}

# TODO: Database port sg
resource "aws_security_group" "db_sg" {
name = "db-sg"
description = "Allow inbound traffic to 5432"
vpc_id = var.vpc_id

ingress {
description = "open port 5432 to vpc"
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.api_sg.id]
}

# egress {
# from_port = 0
# to_port = 0
# protocol = -1
# cidr_blocks = ["0.0.0.0/0"]
# }

tags = {
Name = "database"
}
}
4 changes: 4 additions & 0 deletions modules/sg/output.tf
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
output "api_sg_id" {
value = aws_security_group.api_sg.id
}

output "db_sg_id" {
value = aws_security_group.db_sg.id
}
4 changes: 4 additions & 0 deletions modules/vpc/output.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ output "vpc_id" {
output "public_subnets_id" {
value = aws_subnet.public_subnets[*].id
}

output "private_subnets_id" {
value = aws_subnet.private_subnets[*].id
}
20 changes: 20 additions & 0 deletions root/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions root/example.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ vpc_cidr_block = "xxx.xxx.xxx.xxx/xx"
environment = "dev" #OR 'staging' OR 'prod'
public_subnets = ["xxx.xxx.xxx.xxx/xx", "xxx.xxx.xxx.xxx/xx", "xxx.xxx.xxx.xxx/xx"]
private_subnets = ["xxx.xxx.xxx.xxx/xx", "xxx.xxx.xxx.xxx/xx", "xxx.xxx.xxx.xxx/xx"]
database = "dev"
dbuser = "dev_user"
ssh_key_file = "xxx.pub"
instance_type = "xx.xxxxx"
device_name = "/xxx/xxxx"
Expand Down
17 changes: 17 additions & 0 deletions root/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ module "security_group" {
environment = var.environment
}

module "s3_bucket" {
source = "../modules/s3"
environment = var.environment
}

module "rds" {
source = "../modules/rds"
environment = var.environment
private_subnets_id = module.vpc.private_subnets_id
db_sg_id = module.security_group.db_sg_id
database = var.database
dbuser = var.dbuser
}

module "ssh" {
source = "../modules/ssh"
ssh_key_file = var.ssh_key_file
Expand All @@ -30,4 +44,7 @@ module "ec2" {
device_name = var.device_name
volume_size = var.volume_size
volume_type = var.volume_type
database = var.database
dbuser = var.dbuser
dbpassword = module.rds.dbpassword
}
4 changes: 4 additions & 0 deletions root/providers.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ terraform {
source = "hashicorp/aws"
version = "~> 5.0"
}
random = {
source = "hashicorp/random"
version = "3.6.0"
}
}
}

Expand Down
12 changes: 12 additions & 0 deletions root/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ variable "private_subnets" {
default = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
}

variable "database" {
description = "Database name"
type = string
default = "dev"
}

variable "dbuser" {
description = "Database username"
type = string
default = "dev_user"
}

variable "ssh_key_file" {
description = "ssh-keygen generated public RSA key to SSH into an EC2 instance"
type = string
Expand Down

0 comments on commit f4732c5

Please sign in to comment.