├── .gitignore ├── variables.tf ├── main.tf ├── output.tf ├── vpc.tf ├── container-definitions └── container-def.json ├── iam.tf ├── experimental └── ecr.tf ├── alb.tf ├── ecs.tf ├── asg.tf ├── .github └── workflows │ └── ci_terraform.yaml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .terraform/ -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "key_name" { 2 | type = string 3 | description = "The name for ssh key, used for aws_launch_configuration" 4 | } 5 | 6 | variable "cluster_name" { 7 | type = string 8 | description = "The name of AWS ECS cluster" 9 | } -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-east-1" 3 | version = "~> 2.63" 4 | } 5 | 6 | 7 | terraform { 8 | backend "s3" { 9 | bucket = "ecsworkshopbucket" 10 | key = "state/terraform.tfstate" 11 | region = "us-east-1" 12 | } 13 | } -------------------------------------------------------------------------------- /output.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns" { 2 | value = aws_lb.test-lb.dns_name 3 | } 4 | 5 | output "vpc_id" { 6 | value = module.vpc.vpc_id 7 | } 8 | 9 | output "public_subnets" { 10 | value = module.vpc.public_subnets 11 | } 12 | 13 | output "igw_id" { 14 | value = module.vpc.igw_id 15 | } -------------------------------------------------------------------------------- /vpc.tf: -------------------------------------------------------------------------------- 1 | module "vpc" { 2 | source = "terraform-aws-modules/vpc/aws" 3 | version = "2.38.0" 4 | name = "test_ecs_provisioning" 5 | cidr = "10.0.0.0/16" 6 | azs = ["us-east-1a", "us-east-1b", "us-east-1c"] 7 | public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] 8 | tags = { 9 | "env" = "dev" 10 | "createdBy" = "mkerimova" 11 | } 12 | 13 | } 14 | 15 | data "aws_vpc" "main" { 16 | id = module.vpc.vpc_id 17 | } -------------------------------------------------------------------------------- /container-definitions/container-def.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "pink-slon", 4 | "image": "334600146392.dkr.ecr.us-east-1.amazonaws.com/workshop:latest", 5 | "cpu": 10, 6 | "memory": 256, 7 | "essential": true, 8 | "portMappings": [ 9 | { 10 | "containerPort": 80 11 | } 12 | ], 13 | "logConfiguration": { 14 | "logDriver": "awslogs", 15 | "options": { 16 | "awslogs-group" : "/ecs/frontend-container", 17 | "awslogs-region": "us-east-1" 18 | } 19 | } 20 | } 21 | ] -------------------------------------------------------------------------------- /iam.tf: -------------------------------------------------------------------------------- 1 | resource "aws_iam_role" "ecs-instance-role" { 2 | name = "ecs-instance-role-test-web" 3 | path = "/" 4 | 5 | assume_role_policy = <> /etc/ecs/ecs.config 58 | EOF 59 | } 60 | 61 | resource "aws_autoscaling_group" "asg" { 62 | name = "test-asg" 63 | launch_configuration = aws_launch_configuration.lc.name 64 | min_size = 3 65 | max_size = 4 66 | desired_capacity = 3 67 | health_check_type = "ELB" 68 | health_check_grace_period = 300 69 | vpc_zone_identifier = module.vpc.public_subnets 70 | 71 | target_group_arns = [aws_lb_target_group.lb_target_group.arn] 72 | protect_from_scale_in = true 73 | lifecycle { 74 | create_before_destroy = true 75 | } 76 | } -------------------------------------------------------------------------------- /.github/workflows/ci_terraform.yaml: -------------------------------------------------------------------------------- 1 | name: 'Terraform' 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | terraform: 11 | name: 'Terraform' 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | 17 | - name: Setup Terraform 18 | uses: hashicorp/setup-terraform@v1 19 | 20 | - name: Terraform Init 21 | id: init 22 | env: 23 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 24 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 25 | run: terraform init -input=false 26 | 27 | - name: Terraform fmt 28 | id: fmt 29 | run: terraform fmt -check 30 | continue-on-error: true 31 | 32 | - name: Terraform Validate 33 | id: validate 34 | run: terraform validate -no-color 35 | 36 | - name: Terraform Plan 37 | id: plan 38 | env: 39 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 40 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 41 | run: terraform plan -no-color -var="key_name=ecs_workshop" -var="cluster_name=ecs_workshop_cluster" 42 | continue-on-error: true 43 | 44 | - uses: actions/github-script@v2 45 | name: Runs only on pull request 46 | if: github.event_name == 'pull_request' 47 | env: 48 | PLAN: "terraform\n${{ steps.plan.outputs.stdout }}" 49 | with: 50 | github-token: ${{ secrets.MY_GITHUB_TOKEN }} 51 | script: | 52 | const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` 53 | #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` 54 | #### Terraform Validation 🤖${{ steps.validate.outputs.stdout }} 55 | #### Terraform Plan 📖\`${{ steps.plan.outcome }}\` 56 | 57 |
Show Plan 58 | 59 | \`\`\`${process.env.PLAN}\`\`\` 60 | 61 |
62 | 63 | *Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`, Workflow: \`${{ github.workflow }}\`*`; 64 | 65 | github.issues.createComment({ 66 | issue_number: context.issue.number, 67 | owner: context.repo.owner, 68 | repo: context.repo.repo, 69 | body: output 70 | }) 71 | 72 | # - name: Terraform Apply 73 | # if: github.ref == 'refs/heads/master' && github.event_name == 'push' 74 | # run: terraform apply -auto-approve -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Provisioning VPC, ECS and ALB using Terraform 2 | 3 | ![](https://github.com/mashun4ek/ecs_terraform_workshop/workflows/Terraform/badge.svg) 4 | 5 | This is the example of creating a simple infrastructure using Terraform and AWS cloud provider. It consists of: 6 | - Virtual Private Cloud (VPC) with 3 public subnets in 3 availability zones 7 | - Elastic Container Service (ECS) 8 | - Application Load Balancer (ALB) 9 | 10 | ## How to create the infrastructure? 11 | This example implies that you have already AWS account and Terraform CLI installed. 12 | 1. `git clone https://github.com/mashun4ek/ecs_terraform_workshop.git` 13 | 2. cd ecs_terraform_workshop 14 | 3. terraform init 15 | 4. terraform plan 16 | 5. terraform apply 17 | 18 | Note: it can take about 5 minutes to provision all resources. 19 | ## How to delete the infrastructure? 20 | 1. Terminate instances 21 | 2. Run `terraform destroy` 22 | 23 | ## Brief Description 24 | 25 | Cluster is created using container instances (EC2 launch type, not Fargate!). 26 | 27 | In this example, verified module `vpc` is imported from Terraform Registry, other resources are created in relevant files. 28 | 29 | In file `ecs.tf` we create: 30 | - cluster of container instances _web-cluster_ 31 | - capacity provider, which is basically AWS Autoscaling group for EC2 instances. In this example managed scaling is enabled, Amazon ECS manages the scale-in and scale-out actions of the Auto Scaling group used when creating the capacity provider. I set target capacity to 85%, which will result in the Amazon EC2 instances in your Auto Scaling group being utilized for 85% and any instances not running any tasks will be scaled in. 32 | - task definition with family _web-family_, volume and container definition is defined in the file container-def.json 33 | - service _web-service_, desired count is set to 10, which means there are 10 tasks will be running simultaneously on your cluster. There are two service scheduler strategies available: REPLICA and DAEMON, in this example REPLICA is used. Application load balancer is attached to this service, so the traffic can be distributed between those tasks. 34 | Note: The _binpack_ task placement strategy is used, which places tasks on available instances that have the least available amount of the cpu (specified with the field parameter). 35 | 36 | In file `asg.tf` we create: 37 | - launch configuration 38 | - key pair 39 | - security groups for EC2 instances 40 | - auto-scaling group. 41 | 42 | Note: in order to enable ECS managed scaling you need to enable `protect from scale in` from auto-scaling group. 43 | 44 | In file `iam.tf` we create roles, which will help us to associate EC2 instances to clusters, and other tasks. 45 | 46 | In file `alb.tf` we create Application Load Balancer with target groups, security group and listener. 47 | 48 | --------------------------------------------------------------------------------