├── versions.tf ├── modules ├── ecs │ ├── versions.tf │ ├── ecr.tf │ ├── cluster.tf │ ├── policies │ │ ├── ecs-task-execution-role.json │ │ ├── ecs-execution-role-policy.json │ │ └── ecs-service-role.json │ ├── output.tf │ ├── task-definitions │ │ └── api-task.json │ ├── services.tf │ ├── tasks.tf │ ├── iam.tf │ ├── variables.tf │ ├── security_group.tf │ ├── auto_scaling.tf │ └── alb.tf └── pipeline │ ├── versions.tf │ ├── outputs.tf │ ├── buckets.tf │ ├── templates │ ├── policies │ │ ├── codebuild_role.json │ │ ├── codepipeline_role.json │ │ ├── codebuild_policy.json │ │ └── codepipeline.json │ └── buildspec.yml │ ├── variables.tf │ ├── iam.tf │ ├── codebuild.tf │ └── codepipeline.tf ├── examples ├── api-server-ssl │ ├── versions.tf │ └── main.tf └── api-server-with-vpc │ ├── main.tf │ ├── versions.tf │ └── api-server.tf ├── .gitignore ├── .github └── images │ └── architecture.png ├── main.tf ├── outputs.tf ├── variables.tf ├── README.md └── LICENSE /versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /modules/ecs/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /modules/pipeline/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /examples/api-server-ssl/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /examples/api-server-with-vpc/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "ap-northeast-1" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .terraform 2 | terraform.tfstate 3 | *.tfstate* 4 | terraform.tfvars 5 | todo.txt 6 | /.idea -------------------------------------------------------------------------------- /examples/api-server-with-vpc/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /modules/ecs/ecr.tf: -------------------------------------------------------------------------------- 1 | resource "aws_ecr_repository" "web-app" { 2 | name = var.app_repository_name 3 | } 4 | 5 | -------------------------------------------------------------------------------- /.github/images/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ispec-inc/terraform-aws-ecs-deploy-pipeline/HEAD/.github/images/architecture.png -------------------------------------------------------------------------------- /modules/pipeline/outputs.tf: -------------------------------------------------------------------------------- 1 | output pipeline_s3_id { 2 | value = aws_s3_bucket.source.id 3 | } 4 | 5 | output pipeline_id { 6 | value = aws_codepipeline.pipeline.id 7 | } -------------------------------------------------------------------------------- /modules/pipeline/buckets.tf: -------------------------------------------------------------------------------- 1 | resource "aws_s3_bucket" "source" { 2 | bucket = "${var.cluster_name}-pipeline-ex" 3 | acl = "private" 4 | force_destroy = true 5 | region = var.region 6 | } 7 | 8 | -------------------------------------------------------------------------------- /modules/ecs/cluster.tf: -------------------------------------------------------------------------------- 1 | resource "aws_ecs_cluster" "cluster" { 2 | name = var.cluster_name 3 | } 4 | 5 | resource "aws_cloudwatch_log_group" "web-app" { 6 | name = "${var.cluster_name}-logs" 7 | 8 | tags = { 9 | Application = var.cluster_name 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /modules/pipeline/templates/policies/codebuild_role.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "codebuild.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /modules/pipeline/templates/policies/codepipeline_role.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "codepipeline.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /modules/ecs/policies/ecs-task-execution-role.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "", 6 | "Effect": "Allow", 7 | "Principal": { 8 | "Service": "ecs-tasks.amazonaws.com" 9 | }, 10 | "Action": "sts:AssumeRole" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /modules/ecs/policies/ecs-execution-role-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": [ 7 | "ecr:GetAuthorizationToken", 8 | "ecr:BatchCheckLayerAvailability", 9 | "ecr:GetDownloadUrlForLayer", 10 | "ecr:BatchGetImage", 11 | "logs:CreateLogStream", 12 | "logs:PutLogEvents" 13 | ], 14 | "Resource": "*" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /modules/ecs/policies/ecs-service-role.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": [ 7 | "elasticloadbalancing:Describe*", 8 | "elasticloadbalancing:DeregisterInstancesFromLoadBalancer", 9 | "elasticloadbalancing:RegisterInstancesWithLoadBalancer", 10 | "ec2:Describe*", 11 | "ec2:AuthorizeSecurityGroupIngress" 12 | ], 13 | "Resource": [ 14 | "*" 15 | ] 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /modules/ecs/output.tf: -------------------------------------------------------------------------------- 1 | output "repository_url" { 2 | value = aws_ecr_repository.web-app.repository_url 3 | } 4 | 5 | output "service_name" { 6 | value = aws_ecs_service.web-api.name 7 | } 8 | 9 | output "app_sg_id" { 10 | value = aws_security_group.app_sg.id 11 | } 12 | 13 | output "alb_sg_id" { 14 | value = aws_security_group.alb_sg.id 15 | } 16 | 17 | output "ecs_sg_id" { 18 | value = aws_security_group.ecs_sg.id 19 | } 20 | 21 | output "cloudwatch_log_group_arn" { 22 | value = aws_cloudwatch_log_group.web-app.arn 23 | } 24 | 25 | output "enable_custom_domain" { 26 | value = local.can_domain 27 | } 28 | 29 | output "enable_ssl" { 30 | value = local.can_ssl 31 | } 32 | 33 | output "alb_dns_name" { 34 | value = aws_alb.app_alb.dns_name 35 | } 36 | 37 | -------------------------------------------------------------------------------- /modules/ecs/task-definitions/api-task.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "${container_name}", 4 | "image": "${image}", 5 | "portMappings": [ 6 | { 7 | "containerPort": ${container_port}, 8 | "hostPort": ${container_port} 9 | } 10 | ], 11 | "cpu": ${desired_task_cpu}, 12 | "memory": ${desired_task_memory}, 13 | "networkMode": "awsvpc", 14 | "essential": true, 15 | "logConfiguration": { 16 | "logDriver": "awslogs", 17 | "options": { 18 | "awslogs-group": "${log_group}", 19 | "awslogs-region": "ap-northeast-1", 20 | "awslogs-stream-prefix": "${container_name}" 21 | } 22 | }, 23 | "environment": [${environment_variables_str}] 24 | } 25 | ] 26 | -------------------------------------------------------------------------------- /modules/pipeline/templates/policies/codebuild_policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Resource": [ 7 | "*" 8 | ], 9 | "Action": [ 10 | "logs:CreateLogGroup", 11 | "logs:CreateLogStream", 12 | "logs:PutLogEvents", 13 | "ecr:GetAuthorizationToken", 14 | "ecr:InitiateLayerUpload", 15 | "ecr:UploadLayerPart", 16 | "ecr:CompleteLayerUpload", 17 | "ecr:BatchCheckLayerAvailability", 18 | "ecr:PutImage", 19 | "ecs:RunTask", 20 | "iam:PassRole" 21 | ] 22 | }, 23 | { 24 | "Effect":"Allow", 25 | "Action": [ 26 | "s3:GetObject", 27 | "s3:GetObjectVersion", 28 | "s3:GetBucketVersioning", 29 | "s3:List*", 30 | "s3:PutObject" 31 | ], 32 | "Resource": [ 33 | "${aws_s3_bucket_arn}", 34 | "${aws_s3_bucket_arn}/*" 35 | ] 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /modules/pipeline/templates/buildspec.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | phases: 4 | pre_build: 5 | commands: 6 | - echo Logging in to Amazon ECR... 7 | - aws --version 8 | - $(aws ecr get-login --region ${region} --no-include-email) 9 | - REPOSITORY_URI=${repository_url} 10 | - IMAGE_TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7) 11 | build: 12 | commands: 13 | - echo Build started on `date` 14 | - echo Building the Docker image... 15 | - docker build -t $REPOSITORY_URI:latest . ${build_options} 16 | - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG 17 | post_build: 18 | commands: 19 | - echo Build completed on `date` 20 | - echo Pushing the Docker images... 21 | - docker push $REPOSITORY_URI:latest 22 | - docker push $REPOSITORY_URI:$IMAGE_TAG 23 | - echo Writing image definitions file... 24 | - printf '[{"name":"${container_name}","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json 25 | artifacts: 26 | files: 27 | - imagedefinitions.json 28 | discard-paths: yes 29 | -------------------------------------------------------------------------------- /modules/pipeline/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" { 2 | description = "The cluster_name" 3 | } 4 | 5 | variable "app_repository_name" { 6 | description = "ECR Repository name" 7 | } 8 | 9 | variable "app_service_name" { 10 | description = "Service name" 11 | } 12 | 13 | variable "git_repository" { 14 | type = map(string) 15 | description = "ecs task environment variables" 16 | } 17 | 18 | variable "vpc_id" { 19 | description = "The VPC id" 20 | } 21 | 22 | variable "repository_url" { 23 | description = "The url of the ECR repository" 24 | } 25 | 26 | variable "subnet_ids" { 27 | type = list(string) 28 | description = "Subnet ids" 29 | } 30 | 31 | variable "region" { 32 | description = "The region to use" 33 | default = "ap-northeast-1" 34 | } 35 | 36 | variable "container_name" { 37 | description = "Container name" 38 | } 39 | 40 | variable "build_args" { 41 | type = map(string) 42 | default = {} 43 | } 44 | 45 | variable "build_options" { 46 | type = string 47 | default = "" 48 | description = "Docker build options. ex: '-f ./build/Dockerfile' " 49 | } 50 | -------------------------------------------------------------------------------- /modules/ecs/services.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | security_group_ids = [ 3 | aws_security_group.app_sg.id, 4 | aws_security_group.alb_sg.id, 5 | aws_security_group.ecs_sg.id, 6 | ] 7 | } 8 | 9 | resource "aws_ecs_service" "web-api" { 10 | name = var.cluster_name 11 | task_definition = aws_ecs_task_definition.web-api.arn 12 | cluster = aws_ecs_cluster.cluster.id 13 | launch_type = "FARGATE" 14 | desired_count = var.desired_tasks 15 | 16 | // deployment_controller { 17 | // type = "CODE_DEPLOY" 18 | // } 19 | 20 | network_configuration { 21 | security_groups = local.security_group_ids 22 | subnets = var.availability_zones 23 | assign_public_ip = true // false 24 | } 25 | 26 | load_balancer { 27 | target_group_arn = aws_alb_target_group.api_target_group.arn 28 | container_name = var.container_name 29 | container_port = var.container_port 30 | } 31 | 32 | lifecycle { 33 | ignore_changes = [ 34 | # task_definition, 35 | load_balancer, 36 | ] 37 | } 38 | 39 | depends_on = [aws_iam_role_policy.ecs_service_role_policy, aws_alb_target_group.api_target_group] 40 | } 41 | 42 | -------------------------------------------------------------------------------- /examples/api-server-ssl/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "ap-northeast-1" 3 | } 4 | 5 | locals { 6 | domain = "drone.ispec.tech" 7 | application_name = "simple-go-ping-api" 8 | application_name_lower = replace(lower(local.application_name), "/[^a-z0-9]/", "") 9 | } 10 | 11 | data "aws_acm_certificate" "example" { 12 | domain = local.domain 13 | statuses = ["ISSUED"] 14 | most_recent = true 15 | } 16 | 17 | module "ecs-deploy-pipeline" { 18 | source = "ispec-inc/ecs-deploy-pipeline/aws" 19 | version = "0.4.3" 20 | 21 | vpc_id = "vpc-1e337679" 22 | public_subnets = ["subnet-26631d7d", "subnet-41776669"] 23 | 24 | cluster_name = local.application_name 25 | app_repository_name = local.application_name 26 | container_name = local.application_name 27 | 28 | helth_check_path = "/ping" 29 | alb_port = "8005" 30 | container_port = "8005" 31 | 32 | git_repository = { 33 | owner = "murawakimitsuhiro" 34 | name = "go-simple-RESTful-api" 35 | branch = "feature/only-ping" 36 | } 37 | 38 | domain_name = local.domain 39 | ssl_certificate_arn = data.aws_acm_certificate.example.arn 40 | } 41 | -------------------------------------------------------------------------------- /modules/pipeline/iam.tf: -------------------------------------------------------------------------------- 1 | resource "aws_iam_role" "codepipeline_role" { 2 | name = "${var.app_repository_name}-codepipeline-role" 3 | assume_role_policy = file("${path.module}/templates/policies/codepipeline_role.json") 4 | } 5 | 6 | data "template_file" "codepipeline_policy" { 7 | template = file("${path.module}/templates/policies/codepipeline.json") 8 | 9 | vars = { 10 | aws_s3_bucket_arn = aws_s3_bucket.source.arn 11 | } 12 | } 13 | 14 | resource "aws_iam_role_policy" "codepipeline_policy" { 15 | name = "codepipeline_policy" 16 | role = aws_iam_role.codepipeline_role.id 17 | policy = data.template_file.codepipeline_policy.rendered 18 | } 19 | 20 | resource "aws_iam_role" "codebuild_role" { 21 | name = "${var.app_repository_name}-codebuild-role" 22 | assume_role_policy = file("${path.module}/templates/policies/codebuild_role.json") 23 | } 24 | 25 | data "template_file" "codebuild_policy" { 26 | template = file("${path.module}/templates/policies/codebuild_policy.json") 27 | 28 | vars = { 29 | aws_s3_bucket_arn = aws_s3_bucket.source.arn 30 | } 31 | } 32 | 33 | resource "aws_iam_role_policy" "codebuild_policy" { 34 | name = "codebuild-policy" 35 | role = aws_iam_role.codebuild_role.id 36 | policy = data.template_file.codebuild_policy.rendered 37 | } 38 | 39 | -------------------------------------------------------------------------------- /examples/api-server-with-vpc/api-server.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | application_name = "simple-go-ping-api" 3 | application_name_lower = replace(lower(local.application_name), "/[^a-z0-9]/", "") 4 | 5 | environment = "dev" 6 | 7 | azs = ["ap-northeast-1a", "ap-northeast-1c"] 8 | } 9 | 10 | module "vpc" { 11 | source = "terraform-aws-modules/vpc/aws" 12 | 13 | name = local.application_name 14 | 15 | azs = local.azs 16 | cidr = "10.0.0.0/16" 17 | public_subnets = ["10.0.101.0/24", "10.0.102.0/24"] 18 | 19 | enable_ipv6 = true 20 | 21 | tags = { 22 | Terraform = "true" 23 | Application = local.application_name 24 | Environment = local.environment 25 | } 26 | } 27 | 28 | module "ecs-pipeline" { 29 | source = "../.." 30 | 31 | vpc_id = module.vpc.vpc_id 32 | public_subnets = module.vpc.public_subnets 33 | 34 | cluster_name = local.application_name 35 | app_repository_name = local.application_name 36 | container_name = local.application_name 37 | 38 | alb_port = "8005" 39 | container_port = "8005" 40 | helth_check_path = "/ping" 41 | 42 | build_options = "-f ./build/Dockerfile" 43 | #build_args = { 44 | # is_build_mode_prod = "true" 45 | # build_configuration = "dev" 46 | #} 47 | 48 | git_repository = { 49 | owner = "murawakimitsuhiro" 50 | name = "go-simple-RESTful-api" 51 | branch = "feature/only-ping" 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /modules/ecs/tasks.tf: -------------------------------------------------------------------------------- 1 | data "template_file" "api_task" { 2 | template = file("${path.module}/task-definitions/api-task.json") 3 | 4 | vars = { 5 | image = aws_ecr_repository.web-app.repository_url 6 | container_name = var.container_name 7 | container_port = var.container_port 8 | log_group = aws_cloudwatch_log_group.web-app.name 9 | desired_task_cpu = var.desired_task_cpu 10 | desired_task_memory = var.desired_task_memory 11 | # environment_variables_str = "${replace(join(",",formatlist("{\"name\":%q,\"value\":%q}",keys(var.environment_variables),values(var.environment_variables))), "rds_endpoint", var.db_host_endpoint)}" 12 | environment_variables_str = join( 13 | ",", 14 | formatlist( 15 | "{\"name\":%q,\"value\":%q}", 16 | keys(var.environment_variables), 17 | values(var.environment_variables), 18 | ), 19 | ) 20 | } 21 | } 22 | 23 | resource "aws_ecs_task_definition" "web-api" { 24 | family = "${var.cluster_name}_app" 25 | container_definitions = data.template_file.api_task.rendered 26 | requires_compatibilities = ["FARGATE"] 27 | network_mode = "awsvpc" 28 | cpu = var.desired_task_cpu 29 | memory = var.desired_task_memory 30 | 31 | execution_role_arn = aws_iam_role.ecs_execution_role.arn 32 | task_role_arn = aws_iam_role.ecs_execution_role.arn 33 | } 34 | 35 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | module "pipeline" { 2 | source = "./modules/pipeline" 3 | 4 | cluster_name = var.cluster_name 5 | container_name = var.container_name 6 | app_repository_name = var.app_repository_name 7 | git_repository = var.git_repository 8 | repository_url = module.ecs.repository_url 9 | app_service_name = module.ecs.service_name 10 | vpc_id = var.vpc_id 11 | 12 | build_options = var.build_options 13 | build_args = var.build_args 14 | 15 | subnet_ids = var.public_subnets 16 | } 17 | 18 | module "ecs" { 19 | source = "./modules/ecs" 20 | vpc_id = var.vpc_id 21 | cluster_name = var.cluster_name 22 | container_name = var.container_name 23 | app_repository_name = var.app_repository_name 24 | alb_port = var.alb_port 25 | container_port = var.container_port 26 | min_tasks = var.min_tasks 27 | max_tasks = var.max_tasks 28 | cpu_to_scale_up = var.cpu_to_scale_up 29 | cpu_to_scale_down = var.cpu_to_scale_down 30 | desired_tasks = var.desired_tasks 31 | desired_task_cpu = var.desired_task_cpu 32 | desired_task_memory = var.desired_task_memory 33 | 34 | helth_check_path = var.helth_check_path 35 | environment_variables = var.environment_variables 36 | ssl_certificate_arn = var.ssl_certificate_arn 37 | domain_name = var.domain_name 38 | 39 | availability_zones = var.public_subnets 40 | } 41 | 42 | -------------------------------------------------------------------------------- /modules/pipeline/codebuild.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | needsBuildArgs = length(var.build_args) > 0 3 | buildArgsCommandStr = "--build-arg ${join( 4 | " --build-arg ", 5 | formatlist("%s=%s", keys(var.build_args), values(var.build_args)), 6 | )}" 7 | build_options = format("%s %s", var.build_options, local.needsBuildArgs ? local.buildArgsCommandStr : "") 8 | } 9 | 10 | data "template_file" "buildspec" { 11 | template = file("${path.module}/templates/buildspec.yml") 12 | 13 | vars = { 14 | repository_url = var.repository_url 15 | region = var.region 16 | cluster_name = var.cluster_name 17 | container_name = var.container_name 18 | security_group_ids = join(",", var.subnet_ids) 19 | build_options = local.build_options 20 | } 21 | } 22 | 23 | resource "aws_codebuild_project" "app_build" { 24 | name = "${var.cluster_name}-codebuild" 25 | build_timeout = "60" 26 | 27 | service_role = aws_iam_role.codebuild_role.arn 28 | 29 | artifacts { 30 | type = "CODEPIPELINE" 31 | } 32 | 33 | environment { 34 | compute_type = "BUILD_GENERAL1_SMALL" 35 | 36 | // https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html 37 | image = "aws/codebuild/docker:17.09.0" 38 | type = "LINUX_CONTAINER" 39 | privileged_mode = true 40 | } 41 | 42 | source { 43 | type = "CODEPIPELINE" 44 | buildspec = data.template_file.buildspec.rendered 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | output "vpc_id" { 2 | value = var.vpc_id 3 | description = "vpc id." 4 | } 5 | 6 | output "vpc_public_subnet_ids" { 7 | value = var.public_subnets 8 | description = "List of IDs of VPC public subnets." 9 | } 10 | 11 | output "ecs_repository_url" { 12 | value = module.ecs.repository_url 13 | description = "URL of ECR with build artifacts." 14 | } 15 | 16 | output "app_sg_id" { 17 | value = module.ecs.app_sg_id 18 | description = "ID of application security group. (ALB and ECS adapted)" 19 | } 20 | 21 | output "alb_sg_id" { 22 | value = module.ecs.alb_sg_id 23 | description = "ID of ALB security group." 24 | } 25 | 26 | output "ecs_sg_id" { 27 | value = module.ecs.ecs_sg_id 28 | description = "ID of ECS security group." 29 | } 30 | 31 | output "cloudwatch_log_group_arn" { 32 | value = module.ecs.cloudwatch_log_group_arn 33 | description = "ARN of ecs cloudwatch log group." 34 | } 35 | 36 | output code_pipeline_artifact_s3_id { 37 | value = module.pipeline.pipeline_s3_id 38 | description = "ID of s3 bucket for code pipeline artifact store." 39 | } 40 | 41 | output code_pipeline_id { 42 | value = module.pipeline.pipeline_id 43 | description = "ID of code pipeline." 44 | } 45 | 46 | output "enable_custom_domain" { 47 | value = module.ecs.enable_custom_domain 48 | description = "Bool value of domain is valid or not." 49 | } 50 | 51 | output "enable_ssl" { 52 | value = module.ecs.enable_ssl 53 | description = "Bool value of ssl is valid or not." 54 | } 55 | 56 | output "alb_dns_name" { 57 | value = module.ecs.alb_dns_name 58 | description = "DNS address linked to ALB. (automatically)" 59 | } 60 | 61 | -------------------------------------------------------------------------------- /modules/ecs/iam.tf: -------------------------------------------------------------------------------- 1 | # Cluster Execution Role 2 | resource "aws_iam_role" "ecs_execution_role" { 3 | name = "${var.cluster_name}-ecs_task_role" 4 | assume_role_policy = file("${path.module}/policies/ecs-task-execution-role.json") 5 | } 6 | 7 | # Cluster Execution Policy 8 | resource "aws_iam_role_policy" "ecs_execution_role_policy" { 9 | name = "${var.cluster_name}_role_policy" 10 | policy = file("${path.module}/policies/ecs-execution-role-policy.json") 11 | role = aws_iam_role.ecs_execution_role.id 12 | } 13 | 14 | resource "aws_iam_role" "ecs_role" { 15 | name = "${var.cluster_name}-ecs_role" 16 | assume_role_policy = data.aws_iam_policy_document.ecs_service_role.json 17 | } 18 | 19 | resource "aws_iam_role_policy" "ecs_service_role_policy" { 20 | name = "${var.cluster_name}_role_policy" 21 | policy = data.aws_iam_policy_document.ecs_service_policy.json 22 | role = aws_iam_role.ecs_role.id 23 | } 24 | 25 | # ECS Service Policy 26 | data "aws_iam_policy_document" "ecs_service_policy" { 27 | statement { 28 | effect = "Allow" 29 | resources = ["*"] 30 | 31 | actions = [ 32 | "elasticloadbalancing:Describe*", 33 | "elasticloadbalancing:DeregisterInstancesFromLoadBalancer", 34 | "elasticloadbalancing:RegisterInstancesWithLoadBalancer", 35 | "ec2:Describe*", 36 | "ec2:AuthorizeSecurityGroupIngress", 37 | ] 38 | } 39 | } 40 | 41 | # ECS Service Policy 42 | data "aws_iam_policy_document" "ecs_service_role" { 43 | statement { 44 | effect = "Allow" 45 | actions = ["sts:AssumeRole"] 46 | 47 | principals { 48 | type = "Service" 49 | identifiers = ["ecs.amazonaws.com"] 50 | } 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /modules/pipeline/codepipeline.tf: -------------------------------------------------------------------------------- 1 | resource "aws_codepipeline" "pipeline" { 2 | name = "${var.cluster_name}-pipeline" 3 | role_arn = aws_iam_role.codepipeline_role.arn 4 | 5 | artifact_store { 6 | location = aws_s3_bucket.source.bucket 7 | type = "S3" 8 | } 9 | 10 | stage { 11 | name = "Source" 12 | 13 | action { 14 | name = "Source" 15 | category = "Source" 16 | owner = "ThirdParty" 17 | provider = "GitHub" 18 | version = "1" 19 | output_artifacts = ["source"] 20 | 21 | configuration = { 22 | Owner = var.git_repository["owner"] 23 | Repo = var.git_repository["name"] 24 | Branch = var.git_repository["branch"] 25 | } 26 | } 27 | } 28 | 29 | stage { 30 | name = "Build" 31 | 32 | action { 33 | name = "Build" 34 | category = "Build" 35 | owner = "AWS" 36 | provider = "CodeBuild" 37 | version = "1" 38 | input_artifacts = ["source"] 39 | output_artifacts = ["imagedefinitions"] 40 | 41 | configuration = { 42 | ProjectName = "${var.cluster_name}-codebuild" 43 | } 44 | } 45 | } 46 | 47 | stage { 48 | name = "Production" 49 | 50 | action { 51 | name = "Deploy" 52 | category = "Deploy" 53 | owner = "AWS" 54 | provider = "ECS" 55 | input_artifacts = ["imagedefinitions"] 56 | version = "1" 57 | 58 | configuration = { 59 | ClusterName = var.cluster_name 60 | ServiceName = var.app_service_name 61 | FileName = "imagedefinitions.json" 62 | } 63 | } 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /modules/ecs/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" { 2 | description = "The cluster_name" 3 | } 4 | 5 | variable "container_name" { 6 | description = "Container name" 7 | } 8 | 9 | variable "vpc_id" { 10 | description = "The VPC id" 11 | } 12 | 13 | variable "availability_zones" { 14 | type = list(string) 15 | description = "The azs to use" 16 | } 17 | 18 | //variable "public_subnets" { 19 | // description = "Public Subnet array" 20 | //} 21 | 22 | variable "app_repository_name" { 23 | description = "Name of ECR Repository" 24 | } 25 | 26 | variable "alb_port" { 27 | description = "ALB listener port" 28 | } 29 | 30 | variable "container_port" { 31 | description = "ALB target port" 32 | } 33 | 34 | variable "desired_tasks" { 35 | description = "Number of containers desired to run the application task" 36 | } 37 | 38 | variable "desired_task_cpu" { 39 | description = "Task CPU Limit" 40 | } 41 | 42 | variable "desired_task_memory" { 43 | description = "Task Memory Limit" 44 | } 45 | 46 | variable "min_tasks" { 47 | description = "Minimum" 48 | } 49 | 50 | variable "max_tasks" { 51 | description = "Maximum" 52 | } 53 | 54 | variable "cpu_to_scale_up" { 55 | description = "CPU % to Scale Up the number of containers" 56 | } 57 | 58 | variable "cpu_to_scale_down" { 59 | description = "CPU % to Scale Down the number of containers" 60 | } 61 | 62 | variable "helth_check_path" { 63 | description = "" 64 | default = "/" 65 | } 66 | 67 | variable "environment_variables" { 68 | type = map(string) 69 | description = "ecs task environment variables" 70 | } 71 | 72 | variable "ssl_certificate_arn" { 73 | type = string 74 | description = "ssl certification arn" 75 | default = "" 76 | } 77 | 78 | variable "domain_name" { 79 | type = string 80 | default = "" 81 | } 82 | 83 | -------------------------------------------------------------------------------- /modules/ecs/security_group.tf: -------------------------------------------------------------------------------- 1 | # App Security Group 2 | resource "aws_security_group" "app_sg" { 3 | name = "${var.cluster_name}-app-sg" 4 | description = "Default security group to allow inbound/outbound from the VPC" 5 | vpc_id = var.vpc_id 6 | 7 | ingress { 8 | from_port = "0" 9 | to_port = "0" 10 | protocol = "-1" 11 | self = true 12 | } 13 | 14 | egress { 15 | from_port = "0" 16 | to_port = "0" 17 | protocol = "-1" 18 | self = "true" 19 | } 20 | 21 | tags = { 22 | Environment = var.cluster_name 23 | } 24 | } 25 | 26 | # ALB Security Group 27 | resource "aws_security_group" "alb_sg" { 28 | name = "${var.cluster_name}-alb-sg" 29 | description = "ALB Security Group" 30 | vpc_id = var.vpc_id 31 | 32 | ingress { 33 | from_port = var.alb_port 34 | to_port = var.container_port 35 | protocol = "tcp" 36 | cidr_blocks = ["0.0.0.0/0"] 37 | } 38 | 39 | ingress { 40 | from_port = "80" 41 | to_port = var.container_port 42 | protocol = "tcp" 43 | cidr_blocks = ["0.0.0.0/0"] 44 | } 45 | 46 | ingress { 47 | from_port = "443" 48 | to_port = "443" #"${var.container_port}" 49 | protocol = "tcp" 50 | cidr_blocks = ["0.0.0.0/0"] 51 | } 52 | 53 | ingress { 54 | from_port = 8 55 | to_port = 0 56 | protocol = "icmp" 57 | cidr_blocks = ["0.0.0.0/0"] 58 | } 59 | 60 | egress { 61 | from_port = 0 62 | to_port = 0 63 | protocol = "-1" 64 | cidr_blocks = ["0.0.0.0/0"] 65 | } 66 | 67 | tags = { 68 | Name = "${var.cluster_name}-alb-sg" 69 | } 70 | } 71 | 72 | # ECS Cluster Security Group 73 | resource "aws_security_group" "ecs_sg" { 74 | vpc_id = var.vpc_id 75 | name = "${var.cluster_name}-ecs-service-sg" 76 | description = "Allow egress from container" 77 | 78 | egress { 79 | from_port = 0 80 | to_port = 0 81 | protocol = "-1" 82 | cidr_blocks = ["0.0.0.0/0"] 83 | } 84 | 85 | ingress { 86 | from_port = 8 87 | to_port = 0 88 | protocol = "icmp" 89 | cidr_blocks = ["0.0.0.0/0"] 90 | } 91 | 92 | tags = { 93 | Name = "${var.cluster_name}-ecs-service-sg" 94 | Environment = var.cluster_name 95 | } 96 | } 97 | 98 | -------------------------------------------------------------------------------- /modules/pipeline/templates/policies/codepipeline.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect":"Allow", 6 | "Action": [ 7 | "s3:GetObject", 8 | "s3:GetObjectVersion", 9 | "s3:GetBucketVersioning", 10 | "s3:List*", 11 | "s3:PutObject" 12 | ], 13 | "Resource": [ 14 | "${aws_s3_bucket_arn}", 15 | "${aws_s3_bucket_arn}/*" 16 | ] 17 | }, 18 | { 19 | "Effect": "Allow", 20 | "Action": [ 21 | "codebuild:BatchGetBuilds", 22 | "codebuild:StartBuild" 23 | ], 24 | "Resource": "*" 25 | }, 26 | { 27 | "Action": [ 28 | "ecs:*", 29 | "events:DescribeRule", 30 | "events:DeleteRule", 31 | "events:ListRuleNamesByTarget", 32 | "events:ListTargetsByRule", 33 | "events:PutRule", 34 | "events:PutTargets", 35 | "events:RemoveTargets", 36 | "iam:ListAttachedRolePolicies", 37 | "iam:ListInstanceProfiles", 38 | "iam:ListRoles", 39 | "logs:CreateLogGroup", 40 | "logs:DescribeLogGroups", 41 | "logs:FilterLogEvents" 42 | ], 43 | "Resource": "*", 44 | "Effect": "Allow" 45 | }, 46 | { 47 | "Action": "iam:PassRole", 48 | "Effect": "Allow", 49 | "Resource": [ 50 | "*" 51 | ], 52 | "Condition": { 53 | "StringLike": { 54 | "iam:PassedToService": "ecs-tasks.amazonaws.com" 55 | } 56 | } 57 | }, 58 | { 59 | "Action": "iam:PassRole", 60 | "Effect": "Allow", 61 | "Resource": [ 62 | "arn:aws:iam::*:role/ecsInstanceRole*" 63 | ], 64 | "Condition": { 65 | "StringLike": { 66 | "iam:PassedToService": [ 67 | "ec2.amazonaws.com", 68 | "ec2.amazonaws.com.cn" 69 | ] 70 | } 71 | } 72 | }, 73 | { 74 | "Action": "iam:PassRole", 75 | "Effect": "Allow", 76 | "Resource": [ 77 | "arn:aws:iam::*:role/ecsAutoscaleRole*" 78 | ], 79 | "Condition": { 80 | "StringLike": { 81 | "iam:PassedToService": [ 82 | "application-autoscaling.amazonaws.com", 83 | "application-autoscaling.amazonaws.com.cn" 84 | ] 85 | } 86 | } 87 | }, 88 | { 89 | "Effect": "Allow", 90 | "Action": "iam:CreateServiceLinkedRole", 91 | "Resource": "*", 92 | "Condition": { 93 | "StringLike": { 94 | "iam:AWSServiceName": [ 95 | "ecs.amazonaws.com", 96 | "spot.amazonaws.com", 97 | "spotfleet.amazonaws.com" 98 | ] 99 | } 100 | } 101 | } 102 | ] 103 | } -------------------------------------------------------------------------------- /modules/ecs/auto_scaling.tf: -------------------------------------------------------------------------------- 1 | resource "aws_appautoscaling_target" "app_scale_target" { 2 | service_namespace = "ecs" 3 | resource_id = "service/${aws_ecs_cluster.cluster.name}/${aws_ecs_service.web-api.name}" 4 | scalable_dimension = "ecs:service:DesiredCount" 5 | 6 | max_capacity = var.max_tasks 7 | min_capacity = var.min_tasks 8 | } 9 | 10 | // Trocar aqui 11 | resource "aws_cloudwatch_metric_alarm" "cpu_utilization_high" { 12 | alarm_name = "${var.cluster_name}-CPU-Utilization-High" 13 | comparison_operator = "GreaterThanOrEqualToThreshold" 14 | evaluation_periods = "1" 15 | metric_name = "CPUUtilization" 16 | namespace = "AWS/ECS" 17 | period = "60" 18 | statistic = "Average" 19 | threshold = var.cpu_to_scale_up 20 | 21 | dimensions = { 22 | ClusterName = aws_ecs_cluster.cluster.name 23 | ServiceName = aws_ecs_service.web-api.name 24 | } 25 | 26 | alarm_actions = [aws_appautoscaling_policy.app_up.arn] 27 | } 28 | 29 | resource "aws_appautoscaling_policy" "app_up" { 30 | name = "${var.cluster_name}-app-scale-up" 31 | service_namespace = aws_appautoscaling_target.app_scale_target.service_namespace 32 | resource_id = aws_appautoscaling_target.app_scale_target.resource_id 33 | scalable_dimension = aws_appautoscaling_target.app_scale_target.scalable_dimension 34 | 35 | step_scaling_policy_configuration { 36 | adjustment_type = "ChangeInCapacity" 37 | cooldown = 60 38 | metric_aggregation_type = "Average" 39 | 40 | step_adjustment { 41 | metric_interval_lower_bound = 0 42 | scaling_adjustment = 1 43 | } 44 | } 45 | } 46 | 47 | resource "aws_cloudwatch_metric_alarm" "cpu_utilization_low" { 48 | alarm_name = "${var.cluster_name}-CPU-Utilization-Low" 49 | comparison_operator = "LessThanThreshold" 50 | evaluation_periods = "1" 51 | metric_name = "CPUUtilization" 52 | namespace = "AWS/ECS" 53 | period = "60" 54 | statistic = "Average" 55 | threshold = var.cpu_to_scale_down 56 | 57 | dimensions = { 58 | ClusterName = aws_ecs_cluster.cluster.name 59 | ServiceName = aws_ecs_service.web-api.name 60 | } 61 | 62 | alarm_actions = [aws_appautoscaling_policy.app_down.arn] 63 | } 64 | 65 | resource "aws_appautoscaling_policy" "app_down" { 66 | name = "${var.cluster_name}-scale-down" 67 | service_namespace = aws_appautoscaling_target.app_scale_target.service_namespace 68 | resource_id = aws_appautoscaling_target.app_scale_target.resource_id 69 | scalable_dimension = aws_appautoscaling_target.app_scale_target.scalable_dimension 70 | 71 | step_scaling_policy_configuration { 72 | adjustment_type = "ChangeInCapacity" 73 | cooldown = 300 74 | metric_aggregation_type = "Average" 75 | 76 | step_adjustment { 77 | metric_interval_upper_bound = 0 78 | scaling_adjustment = -1 79 | } 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" { 2 | type = string 3 | description = "ecs cluster name" 4 | default = "" 5 | } 6 | 7 | variable "vpc_id" { 8 | type = string 9 | description = "vpc for provisioning resources" 10 | } 11 | 12 | variable "public_subnets" { 13 | type = list(string) 14 | description = "public subnet array (length>=2)" 15 | } 16 | 17 | variable "alb_port" { 18 | type = string 19 | description = "origin application load balancer port" 20 | } 21 | 22 | variable "container_port" { 23 | type = string 24 | description = "destination application load balancer port" 25 | } 26 | 27 | variable "app_repository_name" { 28 | type = string 29 | description = "ecr repository name" 30 | default = "" 31 | } 32 | 33 | variable "container_name" { 34 | type = string 35 | description = "container app name" 36 | default = "" 37 | } 38 | 39 | variable "git_repository" { 40 | type = map(string) 41 | description = "git repository. It must contain the following key: owner, name, branch" 42 | } 43 | 44 | variable "helth_check_path" { 45 | type = string 46 | description = "target group helth check path" 47 | default = "/" 48 | } 49 | 50 | variable "desired_tasks" { 51 | type = number 52 | description = "number of containers desired to run app task" 53 | default = 2 54 | } 55 | 56 | variable "min_tasks" { 57 | type = number 58 | description = "minimum" 59 | default = 2 60 | } 61 | 62 | variable "max_tasks" { 63 | type = number 64 | description = "maximum" 65 | default = 4 66 | } 67 | 68 | variable "cpu_to_scale_up" { 69 | type = number 70 | description = "cpu % to scale up the number of containers" 71 | default = 80 72 | } 73 | 74 | variable "cpu_to_scale_down" { 75 | type = number 76 | description = "cpu % to scale down the number of containers" 77 | default = 30 78 | } 79 | 80 | variable "desired_task_cpu" { 81 | type = string 82 | description = "desired cpu to run your tasks" 83 | default = "256" 84 | } 85 | 86 | variable "desired_task_memory" { 87 | type = string 88 | description = "desired memory to run your tasks" 89 | default = "512" 90 | } 91 | 92 | variable "environment_variables" { 93 | type = map(string) 94 | description = "ecs task environment variables" 95 | 96 | default = { 97 | KEY = "value" 98 | } 99 | } 100 | 101 | variable "build_options" { 102 | type = string 103 | default = "" 104 | description = "Docker build options. ex: '-f ./build/Dockerfile' " 105 | } 106 | 107 | variable "build_args" { 108 | description = "docker build args." 109 | type = map(string) 110 | default = {} 111 | } 112 | 113 | variable "ssl_certificate_arn" { 114 | type = string 115 | description = "ssl certification arn" 116 | default = "" 117 | } 118 | 119 | variable "domain_name" { 120 | description = "domain name. (must be created in route53)" 121 | type = string 122 | default = "" 123 | } 124 | 125 | -------------------------------------------------------------------------------- /modules/ecs/alb.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | can_ssl = var.ssl_certificate_arn == "" ? false : true 3 | can_domain = var.domain_name == "" ? false : true 4 | is_only_http = local.can_ssl == false && local.can_domain == true 5 | is_redirect_https = local.can_ssl && local.can_domain ? true : false 6 | } 7 | 8 | resource "aws_alb" "app_alb" { 9 | name = "${var.cluster_name}-alb" 10 | subnets = var.availability_zones 11 | security_groups = [aws_security_group.alb_sg.id, aws_security_group.app_sg.id] 12 | 13 | tags = { 14 | Name = "${var.cluster_name}-alb" 15 | Environment = var.cluster_name 16 | } 17 | } 18 | 19 | resource "aws_alb_target_group" "api_target_group" { 20 | name_prefix = substr(var.cluster_name, 0, 6) 21 | port = var.container_port 22 | protocol = "HTTP" 23 | vpc_id = var.vpc_id 24 | target_type = "ip" 25 | 26 | lifecycle { 27 | create_before_destroy = true 28 | } 29 | 30 | health_check { 31 | path = var.helth_check_path 32 | port = var.container_port 33 | } 34 | 35 | depends_on = [aws_alb.app_alb] 36 | } 37 | 38 | # 直でALBヘアクセス 39 | resource "aws_alb_listener" "web_app" { 40 | count = local.can_ssl ? 0 : 1 41 | load_balancer_arn = aws_alb.app_alb.arn 42 | port = var.alb_port 43 | protocol = "HTTP" 44 | depends_on = [aws_alb_target_group.api_target_group] 45 | 46 | lifecycle { 47 | create_before_destroy = true 48 | } 49 | 50 | default_action { 51 | target_group_arn = aws_alb_target_group.api_target_group.arn 52 | type = "forward" 53 | } 54 | } 55 | 56 | # SSLでドメインを介してアクセス 57 | resource "aws_alb_listener" "web_app_ssl" { 58 | count = local.can_ssl ? 1 : 0 59 | load_balancer_arn = aws_alb.app_alb.arn 60 | port = "443" 61 | protocol = "HTTPS" 62 | ssl_policy = "ELBSecurityPolicy-2015-05" 63 | 64 | certificate_arn = var.ssl_certificate_arn 65 | 66 | lifecycle { 67 | create_before_destroy = true 68 | } 69 | 70 | default_action { 71 | target_group_arn = aws_alb_target_group.api_target_group.arn 72 | type = "forward" 73 | } 74 | } 75 | 76 | data "aws_route53_zone" "selected" { 77 | count = local.can_domain ? 1 : 0 78 | 79 | name = "${var.domain_name}." 80 | } 81 | 82 | # SSLを使わず、ドメインを介してアクセスするためのエイリアス 83 | resource "aws_route53_record" "alb_alias" { 84 | count = local.can_domain ? 1 : 0 85 | 86 | name = var.domain_name 87 | zone_id = data.aws_route53_zone.selected[0].zone_id 88 | type = "A" 89 | 90 | lifecycle { 91 | create_before_destroy = true 92 | } 93 | 94 | alias { 95 | name = aws_alb.app_alb.dns_name 96 | zone_id = aws_alb.app_alb.zone_id 97 | evaluate_target_health = true 98 | } 99 | } 100 | 101 | # SSLを使わず、ドメインを介してアクセス 102 | resource "aws_alb_listener" "web_app_http" { 103 | count = local.is_only_http ? 1 : 0 104 | 105 | load_balancer_arn = aws_alb.app_alb.arn 106 | port = "80" 107 | protocol = "HTTP" 108 | depends_on = [aws_alb_target_group.api_target_group] 109 | 110 | lifecycle { 111 | create_before_destroy = true 112 | } 113 | 114 | default_action { 115 | target_group_arn = aws_alb_target_group.api_target_group.arn 116 | type = "forward" 117 | } 118 | } 119 | 120 | # SSLを使える状況で、SSlを使わないアクセスが来たらリダイレクトする 121 | resource "aws_lb_listener" "http_redirect_https" { 122 | count = local.is_redirect_https ? 1 : 0 123 | 124 | load_balancer_arn = aws_alb.app_alb.arn 125 | port = "80" 126 | protocol = "HTTP" 127 | 128 | lifecycle { 129 | create_before_destroy = true 130 | } 131 | 132 | default_action { 133 | type = "redirect" 134 | 135 | redirect { 136 | port = "443" 137 | protocol = "HTTPS" 138 | status_code = "HTTP_301" 139 | } 140 | } 141 | } 142 | 143 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terraform AWS ECS Deploy Pipeline 2 | 3 | ## Introduction 4 | If you have a github repository with Dockerfile, you can use this module to build an ECS that can be accessed from a custom domain. 5 |   6 | 7 | Mainly works as follows: 8 | - This module arranges CodePipeline using github as a hook and resources provisioned to it.
9 | - The deployment destination of CodePipeline is Fargate in ECS.
10 | - And ECS is linked to Load Balancer, enable access via its own domain. 11 | 12 | ## Architecture 13 | 14 | 15 | ## Usage 16 | ### simple usage 17 | You can publish the github source by customizing the four values. 18 | 1. Application Name. 19 | 1. Vpc and subnets to use. 20 | 1. Port to Use(Internal and public). 21 | 1. Github repository to use. 22 | ```main.tf 23 | provider "aws" { 24 | region = "ap-northeast-1" 25 | } 26 | 27 | locals { 28 | # 1. Your app name. 29 | application_name = "simple-go-ping-api" 30 | application_name_lower = replace(lower(local.application_name), "/[^a-z0-9]/", "") 31 | } 32 | 33 | module "ecs-deployline" { 34 | source = "ispec-inc/ecs-deployline/aws" 35 | version = "0.4.3" 36 | 37 | # 2. Your vpc and subnets id. 38 | vpc_id = "vpc-0000000" 39 | public_subnets = ["subnet-1112", "subnet-2222"] 40 | 41 | cluster_name = local.application_name 42 | app_repository_name = local.application_name 43 | container_name = local.application_name 44 | 45 | # 3. Port to use 46 | alb_port = "8005" 47 | container_port = "8005" 48 | helth_check_path = "/ping" 49 | 50 | # 4. Your github repository. 51 | git_repository = { 52 | owner = "murawakimitsuhiro" 53 | name = "go-simple-RESTful-api" 54 | branch = "feature/only-ping" 55 | } 56 | } 57 | ``` 58 | **warning** 59 | - Dockerfile should be placed at the root of the git repository to be used, and be ready to build. 60 | - In this simple sample, we do not link the domain or use SSL. 61 | 62 | For a complete example, including a custom domain. see → [examples/api-server-ssl](https://github.com/ispec-inc/terraform-aws-ecs-deploy-pipeline/tree/master/examples/api-server-ssl) 63 | 64 | ## Inputs 65 | | Name | Description | Type | Default | Required | 66 | |------|-------------|------|---------|:-----:| 67 | | alb\_port | origin application load balancer port | `string` | n/a | yes | 68 | | app\_repository\_name | ecr repository name | `string` | `""` | no | 69 | | build\_args | docker build args. | `map(string)` | `{}` | no | 70 | | cluster\_name | ecs cluster name | `string` | `""` | no | 71 | | container\_name | container app name | `string` | `""` | no | 72 | | container\_port | destination application load balancer port | `string` | n/a | yes | 73 | | cpu\_to\_scale\_down | cpu % to scale down the number of containers | `number` | `30` | no | 74 | | cpu\_to\_scale\_up | cpu % to scale up the number of containers | `number` | `80` | no | 75 | | desired\_task\_cpu | desired cpu to run your tasks | `string` | `"256"` | no | 76 | | desired\_task\_memory | desired memory to run your tasks | `string` | `"512"` | no | 77 | | desired\_tasks | number of containers desired to run app task | `number` | `2` | no | 78 | | domain\_name | domain name. (must be created in route53) | `string` | `""` | no | 79 | | environment\_variables | ecs task environment variables | `map(string)` |
{
"KEY": "value"
}
| no | 80 | | git\_repository | git repository. It must contain the following key: owner, name, branch | `map(string)` | n/a | yes | 81 | | helth\_check\_path | target group helth check path | `string` | `"/"` | no | 82 | | max\_tasks | maximum | `number` | `4` | no | 83 | | min\_tasks | minimum | `number` | `2` | no | 84 | | public\_subnets | public subnet array (length>=2) | `list(string)` | n/a | yes | 85 | | ssl\_certificate\_arn | ssl certification arn | `string` | `""` | no | 86 | | vpc\_id | vpc for provisioning resources | `string` | n/a | yes | 87 | 88 | ## Outputs 89 | | Name | Description | 90 | |------|-------------| 91 | | alb\_dns\_name | DNS address linked to ALB. (automatically) | 92 | | alb\_sg\_id | ID of ALB security group. | 93 | | app\_sg\_id | ID of application security group. (ALB and ECS adapted) | 94 | | cloudwatch\_log\_group\_arn | ARN of ecs cloudwatch log group. | 95 | | code\_pipeline\_artifact\_s3\_id | ID of s3 bucket for code pipeline artifact store. | 96 | | code\_pipeline\_id | ID of code pipeline. | 97 | | ecs-repository-url | URL of ECR with build artifacts. | 98 | | ecs\_sg\_id | ID of ECS security group. | 99 | | enable\_custom\_domain | Bool value of domain is valid or not. | 100 | | enable\_ssl | Bool value of ssl is valid or not. | 101 | | vpc\_id | vpc id. | 102 | | vpc\_public\_subnet\_ids | List of IDs of VPC public subnets. | 103 | 104 | 105 | ## License 106 | Apache 2 Licensed. See LICENSE for full details. 107 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------