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