Skip to content
Open
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
90 changes: 90 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Terraform Provision
'on':
push:
branches:
- main
- port-changes
jobs:
Bookstore-App:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4.1.1

- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 6.0.x

- name: Restore dependencies
run: dotnet restore

- name: Install ABP CLI
run: dotnet tool install -g Volo.Abp.Cli

- name: Install client-side libraries
run: abp install-libs

- name: Build
run: dotnet build --no-restore

- name: Test
run: dotnet test --no-build --verbosity normal

- name: Set up Terraform
uses: hashicorp/setup-terraform@v3.0.0
with:
terraform_version: 1.6.3

- name: Configure AWS credentials
run: >
echo "AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }}" >>
$GITHUB_ENV

echo "AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }}" >>
$GITHUB_ENV

- name: Initialize Terraform
run: |
cd infrastructure/ecr
terraform init

- name : Get ECR Repository URL
id : ecr-repo
run : |
cd infrastructure/ecr
ecr_repo_url=$(terraform output -raw ecr_repository_url)
echo "ECR_REPO_URL=$ecr_repo_url" >> $GITHUB_ENV
echo "The ECR repository URL is ${{ env.ECR_REPO_URL }}"

- name: Print Repository Name and Images
run: |
REPO_NAME=$(echo "${{ github.repository }}" | awk -F/ '{print $2}' | tr '[:upper:]' '[:lower:]')
echo "Repository Name: $REPO_NAME"

- name: Build and Push Docker Image
run: |
ecr_repo_url=${{ env.ECR_REPO_URL }}
docker compose -f compose.yml build

aws ecr get-login-password --region ${{ secrets.AWS_REGION }} | docker login --username AWS --password-stdin $ecr_repo_url

docker images

REPO_URI="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com"

REPO_NAME=$(echo "${{ github.repository }}" | awk -F/ '{print $2}' | tr '[:upper:]' '[:lower:]')

IMAGES=("app" "web" "auth" "migrator")

for IMAGE in "${IMAGES[@]}"; do
aws ecr create-repository --repository-name $REPO_NAME-$IMAGE --region ${{ secrets.AWS_REGION }}
IMAGE_NAME="$REPO_URI/$REPO_NAME-$IMAGE:${{ github.sha }}"
docker tag $REPO_NAME-$IMAGE $IMAGE_NAME
docker push $IMAGE_NAME
done

- name: Provision underlying infrastructure for ECS Cluster
run: |
cd infrastructure/ecs-cluster
terraform apply -auto-approve
37 changes: 36 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -266,4 +266,39 @@ src/Acme.BookStore.Blazor.Server.Tiered/Logs/*
.yarn
!certificate.pfx
node_modules
appsettings.*.json
appsettings.*.json

# Local .terraform directories
**/.terraform/*

# .tfstate files
*.tfstate
*.tfstate.*

# Crash log files
crash.log
crash.*.log

# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
*.tfvars
*.tfvars.json

# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json

# Include override files you do wish to add to version control using negated pattern
# !example_override.tf

# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*

# Ignore CLI configuration files
.terraformrc
terraform.rc
6 changes: 3 additions & 3 deletions compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ services:
migrator:
condition: service_completed_successfully
ports:
- "5001:443"
- "5001:80"
healthcheck:
test: ["CMD", "curl", "-k -v", "https://localhost/swagger/index.html"]
interval: 10s
Expand All @@ -58,7 +58,7 @@ services:
app:
condition: service_started
ports:
- "5002:443"
- "5002:80"

auth:
build:
Expand All @@ -76,7 +76,7 @@ services:
app:
condition: service_started
ports:
- "5003:443"
- "5003:80"

migrator:
build:
Expand Down
22 changes: 22 additions & 0 deletions infrastructure/ecr/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
provider "aws" {
region = "eu-north-1"
}

resource "aws_ecr_repository" "this" {
name = "myrepo"
image_tag_mutability = "MUTABLE"

image_scanning_configuration {
scan_on_push = true
}
}

output "ecr_repository_url" {
value = aws_ecr_repository.this.repository_url
}


# resource "aws_instance" "example" {
# ami = "ami-0fe8bec493a81c7da"
# instance_type = "t3.micro"
# }
141 changes: 141 additions & 0 deletions infrastructure/ecs-cluster/01-network.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
provider "aws" {
region = local.region
}

resource "aws_vpc" "this" {
cidr_block = local.vpc_cidr
tags = {
Name = "${local.resource_tag_prefix}-vpc"
}
}

resource "aws_internet_gateway" "this" {
vpc_id = aws_vpc.this.id
tags = {
Name = "${local.resource_tag_prefix}-igw"
}
}

resource "aws_subnet" "public" {
count = length(local.public_subnet_cidrs)
cidr_block = local.public_subnet_cidrs[count.index]
availability_zone = local.availability_zones[count.index]
vpc_id = aws_vpc.this.id
map_public_ip_on_launch = true

tags = {
Name = "${local.resource_tag_prefix}-public-subnet-${count.index + 1}"
}
}

resource "aws_subnet" "private" {
count = length(local.private_subnet_cidrs)
cidr_block = local.private_subnet_cidrs[count.index]
availability_zone = local.availability_zones[count.index]
vpc_id = aws_vpc.this.id

tags = {
Name = "${local.resource_tag_prefix}-private-subnet-${count.index + 1}"
}
}

resource "aws_eip" "this" {
count = local.elastic_ip_count
vpc = true
depends_on = [aws_internet_gateway.this]
}

resource "aws_nat_gateway" "this" {
count = local.nat_gateway_count
subnet_id = element(aws_subnet.public.*.id, count.index)
allocation_id = element(aws_eip.this.*.id, count.index)
tags = {
Name = "${local.resource_tag_prefix}-nat-gateway-${count.index + 1}"
}
}

resource "aws_route_table" "public_route_table" {
vpc_id = aws_vpc.this.id

route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.this.id
}

tags = {
Name = "${local.resource_tag_prefix}-public-rt"
}
}

resource "aws_route_table_association" "public_associations" {
count = length(aws_subnet.public.*.id)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public_route_table.id
}

resource "aws_route_table" "private" {
count = length(aws_nat_gateway.this.*.id)
vpc_id = aws_vpc.this.id

route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.this[count.index].id
}

tags = {
Name = "${local.resource_tag_prefix}-private-rt-${count.index + 1}"
}
}

resource "aws_route_table_association" "private_associations" {
count = length(aws_subnet.private.*.id)
subnet_id = aws_subnet.private[count.index].id
route_table_id = aws_route_table.private[count.index].id
}


resource "aws_security_group" "lb" {
name = "${local.resource_tag_prefix}-ecs-ec2-alb-sg"
vpc_id = aws_vpc.this.id

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

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

resource "aws_security_group" "ecs_task" {
name = "${local.resource_tag_prefix}-ecs-task-sg"
vpc_id = aws_vpc.this.id

ingress {
protocol = "tcp"
from_port = 80
to_port = 80
security_groups = [aws_security_group.lb.id]
}

ingress {
protocol = "tcp"
from_port = 5001
to_port = 5001
security_groups = [aws_security_group.lb.id]
}


egress {
protocol = "-1"
from_port = 0
to_port = 0
cidr_blocks = ["0.0.0.0/0"]
}
}
50 changes: 50 additions & 0 deletions infrastructure/ecs-cluster/02-autoscaling.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
resource "aws_iam_role" "this" {
name = "${local.resource_tag_prefix}-ecs-role"

assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Action = "sts:AssumeRole",
Effect = "Allow",
Principal = {
Service = "ec2.amazonaws.com"
}
}
]
})
}

resource "aws_iam_role_policy_attachment" "service_policy_attachment" {
role = aws_iam_role.this.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
}

resource "aws_iam_instance_profile" "this" {
name = "${local.resource_tag_prefix}-ecs-instance-profile"
role = aws_iam_role.this.name
}

resource "aws_launch_configuration" "this" {
name_prefix = "${local.resource_tag_prefix}-ecs-launch-config"
image_id = local.ami_id
instance_type = local.instance_type
iam_instance_profile = aws_iam_instance_profile.this.name
security_groups = [aws_security_group.ecs_task.id]
user_data = <<-EOF
#!/bin/bash
echo 'ECS_CLUSTER=${local.cluster_name}' >> /etc/ecs/ecs.config
yum update -y
yum install -y aws-cli
EOF
}

resource "aws_autoscaling_group" "this" {
launch_configuration = aws_launch_configuration.this.name
vpc_zone_identifier = aws_subnet.private.*.id
min_size = local.asg_min_size
desired_capacity = local.asg_desired_size
max_size = local.asg_max_size
health_check_grace_period = 300
health_check_type = "EC2"
}
Loading