My AWS IAC (Infrastructure as Code)
My AWS IAC.
Module ECS is part of post
LICENSE
MIT
Posted on April 10, 2022
Hello everyone!
How are you today? I hope you're doing great. I'm excited to learn Terraform, so I'm writing this to document my experience and notes.
My name is Bervianto Leo Pratama. I'm a Software Engineer who wants to know more DevOps world. I hope you will enjoy my article, also give advice and comment on my content. I'm not an expert on the DevOps world, especially IAC (Infrastructure as Code). I believe I still need to learn more about DevOps.
You will need the tool (Terraform) if you want to try the code. You may download Terraform CLI here. Anyway, my Terraform CLI version is 1.11.7.
Since I just explore and want to know more about Terraform and AWS. I'm using the highest privilege for now. I use AdministratorAccess. Please always use the least privilege for production use.
You may need to use the Terraform Cloud. When you use the Terraform Cloud, please use this guide. However, it's not required for now.
TLDR about my goal. I use Nginx
as my service. In addition, I use Amazon ECS Fargate and ALB (Application Load Balancer). In summary, I want to access my services using Load Balancer and use Nginx for sampling.
main.tf
and add AWS as provider.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.0"
}
}
}
Note: You may setup cloud state in there. When I'm exploring, I'm using local state. But, after that I migrate to use cloud state in my Github Repository.
provider "aws" {
alias = "ap-southeast-1"
region = "ap-southeast-1"
}
Note: Please configure with your desire region. I use ap-southeast-1 (Singapore).
resource "aws_default_vpc" "my-personal-web" {
provider = aws.ap-southeast-1
tags = {
env = "dev"
}
}
resource "aws_default_subnet" "my-personal-web" {
provider = aws.ap-southeast-1
availability_zone = "ap-southeast-1a"
tags = {
env = "dev"
}
}
resource "aws_default_subnet" "my-personal-web-1" {
provider = aws.ap-southeast-1
availability_zone = "ap-southeast-1b"
tags = {
env = "dev"
}
}
resource "aws_security_group" "my-personal-web" {
provider = aws.ap-southeast-1
name = "allow_http"
description = "Allow HTTP inbound traffic"
vpc_id = aws_default_vpc.my-personal-web.id
ingress {
description = "Allow HTTP for all"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
Note: I use default vpc & subnet and config new security group to open port 80 only. Since, I will use nginx
as my services. You will need config vpc & subnet correctly for production use.
resource "aws_lb" "my-personal-web" {
provider = aws.ap-southeast-1
name = "my-personal-web-lb-tf"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.my-personal-web.id]
subnets = [aws_default_subnet.my-personal-web.id, aws_default_subnet.my-personal-web-1.id]
tags = {
env = "dev"
}
}
resource "aws_lb_target_group" "my-personal-web" {
provider = aws.ap-southeast-1
name = "tf-my-personal-web-lb-tg"
port = 80
protocol = "HTTP"
target_type = "ip"
vpc_id = aws_default_vpc.my-personal-web.id
}
resource "aws_lb_listener" "my-personal-web" {
provider = aws.ap-southeast-1
load_balancer_arn = aws_lb.my-personal-web.arn
port = "80"
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.my-personal-web.arn
}
}
Note: I just use port 80. Maybe, you will need 443 for production use.
resource "aws_ecs_cluster" "my-personal-web" {
provider = aws.ap-southeast-1
name = "my-personal-web-api-cluster"
}
resource "aws_ecs_cluster_capacity_providers" "my-personal-web" {
provider = aws.ap-southeast-1
cluster_name = aws_ecs_cluster.my-personal-web.name
capacity_providers = ["FARGATE"]
}
resource "aws_ecs_task_definition" "my-personal-web" {
provider = aws.ap-southeast-1
family = "service"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = 1024
memory = 2048
container_definitions = jsonencode([
{
name = "my-personal-web-api"
image = "nginx"
cpu = 1024
memory = 2048
essential = true
portMappings = [
{
containerPort = 80
hostPort = 80
}
]
}
])
}
resource "aws_ecs_service" "my-personal-web" {
provider = aws.ap-southeast-1
name = "my-personal-web"
cluster = aws_ecs_cluster.my-personal-web.id
task_definition = aws_ecs_task_definition.my-personal-web.arn
desired_count = 2
launch_type = "FARGATE"
network_configuration {
subnets = [aws_default_subnet.my-personal-web.id, aws_default_subnet.my-personal-web-1.id]
security_groups = [aws_security_group.my-personal-web.id]
assign_public_ip = true
}
load_balancer {
target_group_arn = aws_lb_target_group.my-personal-web.arn
container_name = "my-personal-web-api"
container_port = 80
}
tags = {
env = "dev"
}
}
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.0"
}
}
}
# Configure the AWS Provider
provider "aws" {
alias = "ap-southeast-1"
region = "ap-southeast-1"
}
resource "aws_default_vpc" "my-personal-web" {
provider = aws.ap-southeast-1
tags = {
env = "dev"
}
}
resource "aws_default_subnet" "my-personal-web" {
provider = aws.ap-southeast-1
availability_zone = "ap-southeast-1a"
tags = {
env = "dev"
}
}
resource "aws_default_subnet" "my-personal-web-1" {
provider = aws.ap-southeast-1
availability_zone = "ap-southeast-1b"
tags = {
env = "dev"
}
}
resource "aws_security_group" "my-personal-web" {
provider = aws.ap-southeast-1
name = "allow_http"
description = "Allow HTTP inbound traffic"
vpc_id = aws_default_vpc.my-personal-web.id
ingress {
description = "Allow HTTP for all"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_lb" "my-personal-web" {
provider = aws.ap-southeast-1
name = "my-personal-web-lb-tf"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.my-personal-web.id]
subnets = [aws_default_subnet.my-personal-web.id, aws_default_subnet.my-personal-web-1.id]
tags = {
env = "dev"
}
}
resource "aws_lb_target_group" "my-personal-web" {
provider = aws.ap-southeast-1
name = "tf-my-personal-web-lb-tg"
port = 80
protocol = "HTTP"
target_type = "ip"
vpc_id = aws_default_vpc.my-personal-web.id
}
resource "aws_lb_listener" "my-personal-web" {
provider = aws.ap-southeast-1
load_balancer_arn = aws_lb.my-personal-web.arn
port = "80"
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.my-personal-web.arn
}
}
resource "aws_ecs_cluster" "my-personal-web" {
provider = aws.ap-southeast-1
name = "my-personal-web-api-cluster"
}
resource "aws_ecs_cluster_capacity_providers" "my-personal-web" {
provider = aws.ap-southeast-1
cluster_name = aws_ecs_cluster.my-personal-web.name
capacity_providers = ["FARGATE"]
}
resource "aws_ecs_task_definition" "my-personal-web" {
provider = aws.ap-southeast-1
family = "service"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = 1024
memory = 2048
container_definitions = jsonencode([
{
name = "my-personal-web-api"
image = "nginx"
cpu = 1024
memory = 2048
essential = true
portMappings = [
{
containerPort = 80
hostPort = 80
}
]
}
])
}
resource "aws_ecs_service" "my-personal-web" {
provider = aws.ap-southeast-1
name = "my-personal-web"
cluster = aws_ecs_cluster.my-personal-web.id
task_definition = aws_ecs_task_definition.my-personal-web.arn
desired_count = 2
launch_type = "FARGATE"
network_configuration {
subnets = [aws_default_subnet.my-personal-web.id, aws_default_subnet.my-personal-web-1.id]
security_groups = [aws_security_group.my-personal-web.id]
assign_public_ip = true
}
load_balancer {
target_group_arn = aws_lb_target_group.my-personal-web.arn
container_name = "my-personal-web-api"
container_port = 80
}
tags = {
env = "dev"
}
}
terraform init
.terraform plan
. You may check what's the changes in this step.terraform apply
. Type yes
and Enter.Thank you for reading. I really appreciate when you read this long (because the code) article.
Posted on April 10, 2022
Sign up to receive the latest update from our blog.