infra: add Terraform main infrastructure - VPC, subnets, EC2, security groups, IAM
This commit is contained in:
@@ -0,0 +1,95 @@
|
|||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# Data Source — Latest Amazon Linux 2023 AMI
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
data "aws_ami" "amazon_linux_2023" {
|
||||||
|
most_recent = true
|
||||||
|
owners = ["amazon"]
|
||||||
|
|
||||||
|
filter {
|
||||||
|
name = "name"
|
||||||
|
values = ["al2023-ami-2023.*-x86_64"]
|
||||||
|
}
|
||||||
|
|
||||||
|
filter {
|
||||||
|
name = "virtualization-type"
|
||||||
|
values = ["hvm"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# EC2 Instance
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
resource "aws_instance" "app" {
|
||||||
|
ami = data.aws_ami.amazon_linux_2023.id
|
||||||
|
instance_type = "t3.micro"
|
||||||
|
subnet_id = aws_subnet.public.id
|
||||||
|
vpc_security_group_ids = [aws_security_group.nginx.id]
|
||||||
|
iam_instance_profile = aws_iam_instance_profile.ec2_profile.name
|
||||||
|
|
||||||
|
user_data = <<-EOF
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Update system
|
||||||
|
dnf update -y
|
||||||
|
|
||||||
|
# Install Docker
|
||||||
|
dnf install -y docker
|
||||||
|
systemctl enable docker
|
||||||
|
systemctl start docker
|
||||||
|
|
||||||
|
# Wait for Docker to be fully ready
|
||||||
|
sleep 10
|
||||||
|
|
||||||
|
# Add ec2-user to docker group
|
||||||
|
usermod -aG docker ec2-user
|
||||||
|
|
||||||
|
# Ensure SSM Agent is running (pre-installed on AL2023)
|
||||||
|
systemctl enable amazon-ssm-agent
|
||||||
|
systemctl start amazon-ssm-agent
|
||||||
|
|
||||||
|
# Authenticate to ECR
|
||||||
|
aws ecr get-login-password --region ${var.aws_region} | \
|
||||||
|
docker login --username AWS --password-stdin \
|
||||||
|
${var.aws_account_id}.dkr.ecr.${var.aws_region}.amazonaws.com
|
||||||
|
|
||||||
|
# Pull images from ECR
|
||||||
|
docker pull ${var.aws_account_id}.dkr.ecr.${var.aws_region}.amazonaws.com/task-backend:latest
|
||||||
|
docker pull ${var.aws_account_id}.dkr.ecr.${var.aws_region}.amazonaws.com/task-frontend:latest
|
||||||
|
|
||||||
|
# Run backend
|
||||||
|
docker run -d \
|
||||||
|
--name task-backend \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p 3000:3000 \
|
||||||
|
${var.aws_account_id}.dkr.ecr.${var.aws_region}.amazonaws.com/task-backend:latest
|
||||||
|
|
||||||
|
# Wait for backend to be ready
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
# Run frontend
|
||||||
|
docker run -d \
|
||||||
|
--name task-frontend \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p 80:80 \
|
||||||
|
--link task-backend:backend \
|
||||||
|
${var.aws_account_id}.dkr.ecr.${var.aws_region}.amazonaws.com/task-frontend:latest
|
||||||
|
EOF
|
||||||
|
|
||||||
|
root_block_device {
|
||||||
|
volume_size = 20
|
||||||
|
volume_type = "gp3"
|
||||||
|
encrypted = true
|
||||||
|
delete_on_termination = true
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata_options {
|
||||||
|
http_tokens = "required"
|
||||||
|
}
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-app-server"
|
||||||
|
Project = var.project_name
|
||||||
|
Environment = var.environment
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# IAM Role — EC2 instance identity
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
resource "aws_iam_role" "ec2_role" {
|
||||||
|
name = "${var.project_name}-ec2-role"
|
||||||
|
|
||||||
|
assume_role_policy = jsonencode({
|
||||||
|
Version = "2012-10-17"
|
||||||
|
Statement = [
|
||||||
|
{
|
||||||
|
Effect = "Allow"
|
||||||
|
Principal = {
|
||||||
|
Service = "ec2.amazonaws.com"
|
||||||
|
}
|
||||||
|
Action = "sts:AssumeRole"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-ec2-role"
|
||||||
|
Project = var.project_name
|
||||||
|
Environment = var.environment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# Policy Attachments
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
resource "aws_iam_role_policy_attachment" "ssm" {
|
||||||
|
role = aws_iam_role.ec2_role.name
|
||||||
|
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_role_policy_attachment" "ecr" {
|
||||||
|
role = aws_iam_role.ec2_role.name
|
||||||
|
policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# Instance Profile
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
resource "aws_iam_instance_profile" "ec2_profile" {
|
||||||
|
name = "${var.project_name}-ec2-profile"
|
||||||
|
role = aws_iam_role.ec2_role.name
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-ec2-profile"
|
||||||
|
Project = var.project_name
|
||||||
|
Environment = var.environment
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
output "vpc_id" {
|
||||||
|
description = "ID of the VPC"
|
||||||
|
value = aws_vpc.main.id
|
||||||
|
}
|
||||||
|
|
||||||
|
output "public_subnet_id" {
|
||||||
|
description = "ID of the public subnet"
|
||||||
|
value = aws_subnet.public.id
|
||||||
|
}
|
||||||
|
|
||||||
|
output "private_subnet_id" {
|
||||||
|
description = "ID of the private subnet"
|
||||||
|
value = aws_subnet.private.id
|
||||||
|
}
|
||||||
|
|
||||||
|
output "ec2_instance_id" {
|
||||||
|
description = "EC2 instance ID — use this to connect via SSM"
|
||||||
|
value = aws_instance.app.id
|
||||||
|
}
|
||||||
|
|
||||||
|
output "ec2_public_ip" {
|
||||||
|
description = "Public IP of the EC2 instance"
|
||||||
|
value = aws_instance.app.public_ip
|
||||||
|
}
|
||||||
|
|
||||||
|
output "ec2_public_dns" {
|
||||||
|
description = "Public DNS of the EC2 instance"
|
||||||
|
value = aws_instance.app.public_dns
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# Security Group — Nginx (Public)
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
resource "aws_security_group" "nginx" {
|
||||||
|
name = "${var.project_name}-nginx-sg"
|
||||||
|
description = "Security group for Nginx reverse proxy"
|
||||||
|
vpc_id = aws_vpc.main.id
|
||||||
|
|
||||||
|
ingress {
|
||||||
|
description = "HTTP from internet"
|
||||||
|
from_port = 80
|
||||||
|
to_port = 80
|
||||||
|
protocol = "tcp"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
|
||||||
|
ingress {
|
||||||
|
description = "HTTPS from internet"
|
||||||
|
from_port = 443
|
||||||
|
to_port = 443
|
||||||
|
protocol = "tcp"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
|
||||||
|
egress {
|
||||||
|
description = "Allow all outbound"
|
||||||
|
from_port = 0
|
||||||
|
to_port = 0
|
||||||
|
protocol = "-1"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-nginx-sg"
|
||||||
|
Project = var.project_name
|
||||||
|
Environment = var.environment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# Security Group — Backend (Private)
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
resource "aws_security_group" "backend" {
|
||||||
|
name = "${var.project_name}-backend-sg"
|
||||||
|
description = "Security group for backend API - only accepts traffic from Nginx"
|
||||||
|
vpc_id = aws_vpc.main.id
|
||||||
|
|
||||||
|
ingress {
|
||||||
|
description = "Allow traffic from Nginx only"
|
||||||
|
from_port = 3000
|
||||||
|
to_port = 3000
|
||||||
|
protocol = "tcp"
|
||||||
|
security_groups = [aws_security_group.nginx.id]
|
||||||
|
}
|
||||||
|
|
||||||
|
egress {
|
||||||
|
description = "Allow all outbound"
|
||||||
|
from_port = 0
|
||||||
|
to_port = 0
|
||||||
|
protocol = "-1"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-backend-sg"
|
||||||
|
Project = var.project_name
|
||||||
|
Environment = var.environment
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# VPC
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
resource "aws_vpc" "main" {
|
||||||
|
cidr_block = var.vpc_cidr
|
||||||
|
enable_dns_hostnames = true
|
||||||
|
enable_dns_support = true
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-vpc"
|
||||||
|
Project = var.project_name
|
||||||
|
Environment = var.environment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# Subnets
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
resource "aws_subnet" "public" {
|
||||||
|
vpc_id = aws_vpc.main.id
|
||||||
|
cidr_block = var.public_subnet_cidr
|
||||||
|
availability_zone = "${var.aws_region}a"
|
||||||
|
map_public_ip_on_launch = true
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-public-subnet"
|
||||||
|
Project = var.project_name
|
||||||
|
Environment = var.environment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "private" {
|
||||||
|
vpc_id = aws_vpc.main.id
|
||||||
|
cidr_block = var.private_subnet_cidr
|
||||||
|
availability_zone = "${var.aws_region}a"
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-private-subnet"
|
||||||
|
Project = var.project_name
|
||||||
|
Environment = var.environment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# Internet Gateway
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
resource "aws_internet_gateway" "main" {
|
||||||
|
vpc_id = aws_vpc.main.id
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-igw"
|
||||||
|
Project = var.project_name
|
||||||
|
Environment = var.environment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# Route Tables
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
resource "aws_route_table" "public" {
|
||||||
|
vpc_id = aws_vpc.main.id
|
||||||
|
|
||||||
|
route {
|
||||||
|
cidr_block = "0.0.0.0/0"
|
||||||
|
gateway_id = aws_internet_gateway.main.id
|
||||||
|
}
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-public-rt"
|
||||||
|
Project = var.project_name
|
||||||
|
Environment = var.environment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_route_table_association" "public" {
|
||||||
|
subnet_id = aws_subnet.public.id
|
||||||
|
route_table_id = aws_route_table.public.id
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user