├── 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 |
--------------------------------------------------------------------------------