Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions .github/workflows/terraform.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: Terraform

on:
push:
branches:
- main
paths:
- 'infra/terraform/**'
pull_request:
paths:
- 'infra/terraform/**'

env:
TF_VERSION: '1.7.0'
AWS_REGION: us-east-1

jobs:
terraform:
name: Terraform Plan/Apply
runs-on: ubuntu-latest
defaults:
run:
working-directory: infra/terraform

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}

- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}

- name: Terraform Format Check
run: terraform fmt -check -recursive

- name: Terraform Init
run: terraform init

- name: Terraform Validate
run: terraform validate

- name: Terraform Plan
id: plan
run: terraform plan -no-color
continue-on-error: true

- name: Comment PR with Plan
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const output = `#### Terraform Plan 📖

<details><summary>Show Plan</summary>

\`\`\`terraform
${{ steps.plan.outputs.stdout }}
\`\`\`

</details>`;

github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
});

- name: Terraform Apply
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: terraform apply -auto-approve
85 changes: 85 additions & 0 deletions infra/terraform/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Brain Storm - Terraform Infrastructure

This directory contains Terraform configurations for deploying Brain Storm to AWS.

## Architecture

- VPC with public and private subnets across 2 availability zones
- RDS PostgreSQL 16 with automated backups
- ElastiCache Redis cluster with automatic failover
- ECS Fargate for container orchestration
- Application Load Balancer for traffic distribution

## Prerequisites

- Terraform >= 1.0
- AWS CLI configured with appropriate credentials
- S3 bucket for remote state: `brain-storm-terraform-state`
- DynamoDB table for state locking: `brain-storm-terraform-locks`

## Setup Remote State

```bash
# Create S3 bucket for state
aws s3api create-bucket \
--bucket brain-storm-terraform-state \
--region us-east-1

aws s3api put-bucket-versioning \
--bucket brain-storm-terraform-state \
--versioning-configuration Status=Enabled

# Create DynamoDB table for locking
aws dynamodb create-table \
--table-name brain-storm-terraform-locks \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--region us-east-1
```

## Usage

1. Copy the example variables file:
```bash
cp terraform.tfvars.example terraform.tfvars
```

2. Edit `terraform.tfvars` with your values

3. Initialize Terraform:
```bash
terraform init
```

4. Plan the deployment:
```bash
terraform plan
```

5. Apply the configuration:
```bash
terraform apply
```

## Outputs

After successful apply, Terraform will output:
- ALB DNS name for accessing the application
- VPC ID
- Database and Redis endpoints (sensitive)

## Resource Limits

Production configuration uses:
- Backend: 512 CPU, 1024 MB memory (2 tasks)
- Frontend: 256 CPU, 512 MB memory (2 tasks)
- RDS: db.t3.micro with auto-scaling storage
- Redis: cache.t3.micro with 2 nodes

## Cost Optimization

For development/staging environments, adjust in `terraform.tfvars`:
- Use smaller instance classes
- Reduce ECS task counts
- Disable multi-AZ for RDS and Redis
76 changes: 76 additions & 0 deletions infra/terraform/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
terraform {
required_version = ">= 1.0"

required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}

backend "s3" {
bucket = "brain-storm-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "brain-storm-terraform-locks"
}
}

provider "aws" {
region = var.aws_region
}

module "vpc" {
source = "./modules/vpc"

environment = var.environment
vpc_cidr = var.vpc_cidr
}

module "rds" {
source = "./modules/rds"

environment = var.environment
vpc_id = module.vpc.vpc_id
private_subnet_ids = module.vpc.private_subnet_ids
db_name = var.db_name
db_username = var.db_username
db_password = var.db_password
db_instance_class = var.db_instance_class
}

module "elasticache" {
source = "./modules/elasticache"

environment = var.environment
vpc_id = module.vpc.vpc_id
private_subnet_ids = module.vpc.private_subnet_ids
node_type = var.redis_node_type
}

module "ecs" {
source = "./modules/ecs"

environment = var.environment
vpc_id = module.vpc.vpc_id
private_subnet_ids = module.vpc.private_subnet_ids
public_subnet_ids = module.vpc.public_subnet_ids

backend_image = var.backend_image
frontend_image = var.frontend_image

db_host = module.rds.db_endpoint
redis_host = module.elasticache.redis_endpoint
}

module "alb" {
source = "./modules/alb"

environment = var.environment
vpc_id = module.vpc.vpc_id
public_subnet_ids = module.vpc.public_subnet_ids

backend_target_group_arn = module.ecs.backend_target_group_arn
frontend_target_group_arn = module.ecs.frontend_target_group_arn
}
57 changes: 57 additions & 0 deletions infra/terraform/modules/alb/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
resource "aws_security_group" "alb" {
name = "${var.environment}-brain-storm-alb-sg"
description = "Security group for Application Load Balancer"
vpc_id = var.vpc_id

ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}

ingress {
from_port = 443
to_port = 443
protocol = "tcp"
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 = "${var.environment}-brain-storm-alb-sg"
Environment = var.environment
}
}

resource "aws_lb" "main" {
name = "${var.environment}-brain-storm-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.alb.id]
subnets = var.public_subnet_ids

enable_deletion_protection = var.environment == "prod"

tags = {
Name = "${var.environment}-brain-storm-alb"
Environment = var.environment
}
}

resource "aws_lb_listener" "http" {
load_balancer_arn = aws_lb.main.arn
port = "80"
protocol = "HTTP"

default_action {
type = "forward"
target_group_arn = var.frontend_target_group_arn
}
}
9 changes: 9 additions & 0 deletions infra/terraform/modules/alb/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
output "alb_dns_name" {
description = "ALB DNS name"
value = aws_lb.main.dns_name
}

output "alb_arn" {
description = "ALB ARN"
value = aws_lb.main.arn
}
24 changes: 24 additions & 0 deletions infra/terraform/modules/alb/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
variable "environment" {
description = "Environment name"
type = string
}

variable "vpc_id" {
description = "VPC ID"
type = string
}

variable "public_subnet_ids" {
description = "Public subnet IDs"
type = list(string)
}

variable "backend_target_group_arn" {
description = "Backend target group ARN"
type = string
}

variable "frontend_target_group_arn" {
description = "Frontend target group ARN"
type = string
}
Loading