Skip to content

Commit

Permalink
Merge pull request #2 from silinternational/develop
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
briskt authored Jun 10, 2023
2 parents d30b91e + 6d18e63 commit 2eb06c2
Show file tree
Hide file tree
Showing 10 changed files with 791 additions and 0 deletions.
43 changes: 43 additions & 0 deletions .github/workflows/terraform.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# This workflow installs the latest version of Terraform CLI. On pull request events, this workflow will run
# `terraform init`, `terraform fmt`, and `terraform plan`.
#
# Documentation for `hashicorp/setup-terraform` is located here: https://github.com/hashicorp/setup-terraform

name: 'Terraform'

on:
push:

permissions:
contents: read

jobs:
terraform:
name: 'Terraform'
runs-on: ubuntu-latest

# Use the Bash shell regardless whether the GitHub Actions runner is ubuntu-latest, macos-latest, or windows-latest
defaults:
run:
shell: bash

steps:
# Checkout the repository to the GitHub Actions runner
- name: Checkout
uses: actions/checkout@v3

# Install the latest version of Terraform CLI
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2

# Checks that all Terraform configuration files adhere to a canonical format
- name: Terraform Format
run: terraform fmt -check -diff -recursive

# Initialize a new or existing Terraform working directory by creating initial files, loading any remote state, downloading modules, etc.
- name: Terraform Init
run: terraform -chdir=test init

# Validate the files, referring only to the configuration and not accessing any remote services
- name: Terraform Validate
run: terraform -chdir=test validate
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@ override.tf.json
# Ignore CLI configuration files
.terraformrc
terraform.rc

# IDE configuration
.idea/
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,14 @@
# terraform-aws-ecs-app
Terraform module to host an app on AWS ECS

Includes:

* VPC - Virtual Private Cloud
* ALB - Application Load Balancer
* ASG - Autoscaling Group
* ECS (Elastic Container Service) Cluster and Service
* ECR - Elastic Container Registry
* RDS (Relational Database Service) Instance
* CloudWatch Dashboard (optional)
* Cloudflare DNS Record (optional)
* Adminer database manager (optional)
246 changes: 246 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
locals {
app_name_and_env = "${var.app_name}-${local.app_env}"
app_env = var.app_env

db_host = module.rds.address
db_password = random_password.db_root.result

account = data.aws_caller_identity.this.account_id
region = data.aws_region.current.name
}

/*
* Create user for CI/CD to perform ECS actions
*/
resource "aws_iam_user" "cd" {
count = var.create_cd_user ? 1 : 0

name = "cd-${local.app_name_and_env}"
}

resource "aws_iam_access_key" "cd" {
count = var.create_cd_user ? 1 : 0

user = aws_iam_user.cd[0].name
}

resource "aws_iam_user_policy" "cd" {
count = var.create_cd_user ? 1 : 0

name = "ecs_deployment"
user = aws_iam_user.cd[0].name

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"ecr:GetAuthorizationToken",
"ecs:DeregisterTaskDefinition",
"ecs:DescribeTaskDefinition",
"ecs:ListTaskDefinitions",
"ecs:RegisterTaskDefinition",
],
Resource = "*"
},
{
Effect = "Allow"
Action = [
"ecs:DescribeServices",
"ecs:UpdateService",
]
Resource = "arn:aws:ecs:${local.region}:${local.account}:service/${module.ecsasg.ecs_cluster_name}/${module.ecs.service_name}"
},
{
Effect = "Allow"
Action = [
"ecs:DescribeTasks",
"ecs:StopTask",
]
Resource = "arn:aws:ecs:${local.region}:${local.account}:task/${module.ecsasg.ecs_cluster_name}/*"
},
{
Effect = "Allow"
Action = [
"ecs:ListTasks",
]
"Effect" : "Allow",
"Condition" : {
"ArnEquals" : {
"ecs:cluster" : "arn:aws:ecs:${local.region}:${local.account}:cluster/${module.ecsasg.ecs_cluster_name}"
}
}
Resource = "*"
},
{
Effect = "Allow"
Action = [
"ecs:StartTask",
]
Resource = "arn:aws:ecs:${local.region}:${local.account}:task-definition/${module.ecs.task_def_family}:*"
},
{
Effect = "Allow"
Action = [
"iam:PassRole",
]
Resource = module.ecsasg.ecsServiceRole_arn
},
]
})
}

/*
* Create Cloudwatch log group
*/
resource "aws_cloudwatch_log_group" "logs" {
name = local.app_name_and_env
retention_in_days = 30
}

/*
* Create target group for ALB
*/
resource "aws_alb_target_group" "tg" {
name = substr("tg-${local.app_name_and_env}", 0, 32)
port = "80"
protocol = "HTTP"
vpc_id = module.vpc.id
deregistration_delay = "30"

stickiness {
type = "lb_cookie"
}

health_check {
path = "/"
matcher = "302"
}
}

/*
* Create listener rule for hostname routing to new target group
*/
resource "aws_alb_listener_rule" "tg" {
listener_arn = module.alb.https_listener_arn
priority = "218"

action {
type = "forward"
target_group_arn = aws_alb_target_group.tg.arn
}

condition {
host_header {
values = ["${var.subdomain}.${var.domain_name}"]
}
}
}

/*
* Create cloudwatch dashboard for service
*/
module "ecs-service-cloudwatch-dashboard" {
count = var.create_dashboard ? 1 : 0

source = "silinternational/ecs-service-cloudwatch-dashboard/aws"
version = "~> 3.0.1"

cluster_name = module.ecsasg.ecs_cluster_name
dashboard_name = local.app_name_and_env
service_names = [var.app_name]
}

/*
* Create RDS root password
*/
resource "random_password" "db_root" {
length = 16
}

/*
* Create an RDS database
*/
module "rds" {
source = "github.com/silinternational/terraform-modules//aws/rds/mariadb?ref=8.2.1"

app_name = var.app_name
app_env = local.app_env
db_name = var.database_name
db_root_user = var.database_user
db_root_pass = random_password.db_root.result
subnet_group_name = module.vpc.db_subnet_group_name
security_groups = [module.vpc.vpc_default_sg_id]

allocated_storage = 20 // 20 gibibyte
instance_class = "db.t3.micro"
multi_az = true
}

/*
* Optional Adminer database manager
*/
module "adminer" {
count = var.create_adminer ? 1 : 0
source = "silinternational/adminer/aws"
version = "1.0.2"

adminer_default_server = module.rds.address
app_name = var.app_name
app_env = var.app_env
vpc_id = module.vpc.id
alb_https_listener_arn = module.alb.https_listener_arn
subdomain = "adminer"
cloudflare_domain = var.domain_name
ecs_cluster_id = module.ecsasg.ecs_cluster_id
ecsServiceRole_arn = module.ecsasg.ecsServiceRole_arn
alb_dns_name = module.alb.dns_name
enable = var.enable_adminer
}

/*
* Create new ecs service
*/
module "ecs" {
source = "github.com/silinternational/terraform-modules//aws/ecs/service-only?ref=8.2.1"
cluster_id = module.ecsasg.ecs_cluster_id
service_name = var.app_name
service_env = local.app_env
container_def_json = var.container_def_json
desired_count = var.desired_count
tg_arn = aws_alb_target_group.tg.arn
lb_container_name = "hub"
lb_container_port = "80"
ecsServiceRole_arn = module.ecsasg.ecsServiceRole_arn
}

/*
* Create Cloudflare DNS record
*/
resource "cloudflare_record" "dns" {
count = var.create_dns_record ? 1 : 0

zone_id = data.cloudflare_zones.domain.zones[0].id
name = var.subdomain
value = module.alb.dns_name
type = "CNAME"
proxied = true
}

data "cloudflare_zones" "domain" {
filter {
name = var.domain_name
lookup_type = "exact"
status = "active"
}
}


/*
* AWS data
*/

data "aws_caller_identity" "this" {}

data "aws_region" "current" {}
42 changes: 42 additions & 0 deletions outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

output "app_url" {
value = "https://${var.subdomain}.${var.domain_name}"
}

output "cloudwatch_log_group_name" {
value = aws_cloudwatch_log_group.logs.name
}

output "database_host" {
value = module.rds.address
}

output "database_password" {
value = random_password.db_root.result
sensitive = true
}

output "adminer_url" {
value = one(module.adminer[*].adminer_url)
}

output "ecsInstanceRole_arn" {
value = module.ecsasg.ecsInstanceRole_arn
}

output "ecsServiceRole_arn" {
value = module.ecsasg.ecsServiceRole_arn
}

output "cd_user_arn" {
value = one(aws_iam_user.cd[*].arn)
}

output "cd_user_access_key_id" {
value = one(aws_iam_access_key.cd[*].id)
}

output "cd_user_secret_access_key_id" {
value = one(aws_iam_access_key.cd[*].secret)
sensitive = true
}
Loading

0 comments on commit 2eb06c2

Please sign in to comment.