Skip to content

Instantly share code, notes, and snippets.

@silbermm
Last active August 26, 2020 03:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save silbermm/8f5f08389c23a84325259118a47dd22d to your computer and use it in GitHub Desktop.
Save silbermm/8f5f08389c23a84325259118a47dd22d to your computer and use it in GitHub Desktop.
ECS Terraform
provider aws {
profile = "default"
region = "us-east-1"
}
variable app_name {
default = "your_app_name"
}
resource aws_vpc main {
cidr_block = "your_cidr_block"
tags = {
Name = "Default VPC"
}
}
resource "aws_ecr_repository" "repo" {
name = "ecs_example_repo" # give this a better name
image_tag_mutability = "MUTABLE"
image_scanning_configuration {
scan_on_push = true
}
}
data aws_subnet_ids vpc_subnets {
vpc_id = aws_vpc.main.id
}
data aws_subnet default_subnet {
count = "${length(data.aws_subnet_ids.vpc_subnets.ids)}"
id = "${tolist(data.aws_subnet_ids.vpc_subnets.ids)[count.index]}"
}
data "aws_caller_identity" "current" {}
resource aws_lb_target_group lb_target_group {
name = "ecs-app-tg" # choose a name that makes sense
port = 4000 # We expose port 4000 from our container
protocol = "HTTP"
vpc_id = aws_vpc.main.id # our default vpc id
target_type = "ip"
health_check {
path = "/health" # we configured a rest endpoint that just returns 200 for this
port = "4000"
}
stickiness {
type = "lb_cookie"
enabled = "true"
cookie_duration = "3600"
}
}
# Only listen on 443
resource aws_lb_listener ecs_listener {
load_balancer_arn = aws_lb.load_balancer.arn
port = "443"
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-2016-08"
certificate_arn = "your_certificate_arn" # Get this from AWS Certificate Manager
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.lb_target_group.arn
}
}
resource aws_lb load_balancer {
name = "ecs-app-lb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.lb_security_group.id]
subnets = data.aws_subnet.default_subnet.*.id
enable_deletion_protection = true
}
# needed to allow web traffic to hit the ALB
resource aws_security_group lb_security_group {
name = "lb_security_group"
description = "Allow all outbound traffic and https inbound"
vpc_id = aws_vpc.main.id
ingress {
description = "HTTPS"
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"]
}
}
resource aws_ecs_cluster ecs_cluster {
name = "${var.app_name}_cluster"
}
resource aws_ecs_task_definition task_definition {
family = "${var.app_name}_task"
task_role_arn = aws_iam_role.ecs_role.arn
execution_role_arn = aws_iam_role.ecs_execution_role.arn
requires_compatibilities = ["FARGATE"]
memory = 8192
cpu = 4096
network_mode = "awsvpc"
container_definitions = <<-EOF
[
{
"cpu": 0,
"image": "${aws_ecr_repository.repo.repository_url}:latest",
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/${var.app_name}",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ecs"
}
},
"portMappings": [
{
"hostPort": 4000,
"protocol": "tcp",
"containerPort": 4000
}
],
"environment": [],
"mountPoints": [],
"volumesFrom": [],
"essential": true,
"links": [],
"name": "${var.app_name}"
}
]
EOF
}
resource aws_ecs_service service {
name = "${var.app_name}_service"
cluster = aws_ecs_cluster.ecs_cluster.id
# note, you will need to subsitute your_account_id with your actual aws account id
# I have not found an easier way to get the full task_definition ARN
task_definition = "arn:aws:ecs:us-east-1:${data.aws_caller_identity.current.account_id}:task-definition/${aws_ecs_task_definition.task_definition.family}:3"
desired_count = 1
launch_type = "FARGATE"
network_configuration {
security_groups = [aws_security_group.security_group.id]
subnets = data.aws_subnet.default_subnet.*.id
assign_public_ip = true # this seems to be required to access the container repo
}
load_balancer {
target_group_arn = aws_lb_target_group.lb_target_group.arn
container_name = var.app_name
container_port = "4000"
}
# this will come into play when we talk about distributed clustering
service_registries {
registry_arn = aws_service_discovery_service.service_discovery.arn
container_name = var.app_name
}
}
# needed that that our container can access the outside world
# and traffic in your VPC can access the containers
resource aws_security_group security_group {
name = var.app_name
description = "Allow all outbound traffic"
vpc_id = aws_vpc.main.id
ingress {
description = "HTTP/S Traffic"
from_port = 0
to_port = 65535
protocol = "tcp"
cidr_blocks = [aws_vpc.main.cidr_block]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
# this is the role that your container runs as
# you can give it permissions to other parts of AWS that it may need to access
# like S3 or DynamoDB for instance.
resource aws_iam_role ecs_role {
name = "ecs_role"
assume_role_policy = <<-EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
# this role and the following permissions are required
# for the ECS service to pull the container from ECR
# and write log events
resource aws_iam_role ecs_execution_role {
name = "ecs_execution_role"
assume_role_policy = <<-EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource aws_iam_policy ecs_policy {
name = "ecs_policy"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
]
}
EOF
}
resource aws_iam_policy_attachment attach_ecs_policy {
name = "attach-ecs-policy"
roles = [aws_iam_role.ecs_execution_role.name]
policy_arn = aws_iam_policy.ecs_policy.arn
}
resource aws_cloudwatch_log_group log_group {
name = "/ecs/${var.app_name}"
}
# these enable service discovery to help us cluster our servers
resource "aws_service_discovery_private_dns_namespace" dns_namespace {
name = "${var.app_name}.local"
description = "some desc"
vpc = aws_vpc.main.id
}
resource "aws_service_discovery_service" service_discovery {
name = var.app_name
dns_config {
namespace_id = aws_service_discovery_private_dns_namespace.dns_namespace.id
dns_records {
ttl = 10
type = "A"
}
routing_policy = "MULTIVALUE"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment